From ddccdef37cc91c19b58abd5e197d647b0051d5fc Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Mon, 10 Dec 2018 17:35:29 +0300 Subject: [PATCH 001/112] ToString() HttpExecutor improvements + default read timeout 0 Coverage improvements + README fixs --- README.md | 21 +++--- .../api/etherscan/core/impl/EtherScanApi.java | 8 +++ .../etherscan/executor/impl/HttpExecutor.java | 22 +++--- src/main/java/io/api/etherscan/model/Abi.java | 26 +++++++ .../java/io/api/etherscan/model/BaseTx.java | 17 +++++ .../java/io/api/etherscan/model/Block.java | 10 +++ src/main/java/io/api/etherscan/model/Log.java | 22 ++++++ .../java/io/api/etherscan/model/Price.java | 12 ++++ .../java/io/api/etherscan/model/Status.java | 8 +++ .../io/api/etherscan/model/TokenBalance.java | 7 ++ src/main/java/io/api/etherscan/model/Tx.java | 14 ++++ .../io/api/etherscan/model/TxInternal.java | 10 +++ .../java/io/api/etherscan/model/TxToken.java | 15 ++++ .../java/io/api/etherscan/model/Uncle.java | 9 +++ .../io/api/etherscan/model/UncleBlock.java | 9 +++ src/main/java/io/api/etherscan/model/Wei.java | 7 ++ .../api/etherscan/model/proxy/BlockProxy.java | 51 ++++++++++++++ .../etherscan/model/proxy/ReceiptProxy.java | 43 ++++++++++++ .../io/api/etherscan/model/proxy/TxProxy.java | 45 ++++++++++++ .../model/utility/BaseResponseTO.java | 4 +- .../io/api/etherscan/util/BasicUtils.java | 31 +++++---- .../account/AccountBalanceListTest.java | 7 ++ .../etherscan/account/AccountBalanceTest.java | 1 + .../account/AccountMinedBlocksTest.java | 1 + .../etherscan/contract/ContractApiTest.java | 1 + src/test/java/io/api/util/UtilTests.java | 68 ++++++++++++++++++- 26 files changed, 431 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 3d84bad..b288b45 100644 --- a/README.md +++ b/README.md @@ -43,9 +43,10 @@ dependencies { API support Ethereum: *[MAINNET](https://etherscan.io), [ROPSTEN](https://ropsten.etherscan.io), [KOVAN](https://kovan.etherscan.io), [RINKEBY](https://rinkeby.etherscan.io)* networks. ```java -EtherScanApi api = new EtherScanApi(EthNetwork.MAINTNET); -EtherScanApi api = new EtherScanApi(EthNetwork.RINKEBY); -EtherScanApi api = new EtherScanApi("YourApiKey", EthNetwork.KOVAN); +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); ``` ## Custom HttpClient @@ -56,10 +57,10 @@ just implement **IHttpExecutor** 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); ``` @@ -70,14 +71,15 @@ You can read about all API methods on [Etherscan](https://etherscan.io/apis) *Library support all available EtherScan API.* -You can use API with you key or without key as well (Check API request\sec restrictions). -Library support limit when used without key and will limit requests up to *5 req/sec by itself*. +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))*. + +Library will automatically limit requests up to **5 req/sec** when used *without* key. ```java EtherScanApi api = new EtherScanApi(); EtherScanApi api = new EtherScanApi("YourApiKey"); ``` -Below there are examples for each API category. +Below are examples for each API category. ### Account Api **Get Ether Balance for a single Address** @@ -121,6 +123,7 @@ LogQuery query = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70 .setOpTopic0_2(LogOp.OR) .setOpTopic1_2(LogOp.AND) .build(); + List logs = api.logs().logs(query); ``` @@ -152,7 +155,7 @@ Optional status = api.txs().receiptStatus("0x513c1ba0bebf66436b5fed86ab ``` ### Token Api -You can read token API [here](https://etherscan.io/apis#tokens) +You can read about token API [here](https://etherscan.io/apis#tokens) Token API methods migrated to [Account](#account-api) & [Stats](#stats-api) respectfully. 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 c94a4e3..87899f1 100644 --- a/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java +++ b/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java @@ -10,6 +10,7 @@ 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; @@ -83,6 +84,7 @@ public EtherScanApi(final String apiKey, /** * API for interactions with account and address */ + @NotNull public IAccountApi account() { return account; } @@ -90,6 +92,7 @@ public IAccountApi account() { /** * API for verifying contract ABI */ + @NotNull public IContractApi contract() { return contract; } @@ -97,6 +100,7 @@ public IContractApi contract() { /** * [BETA] API for interaction with tx statuses */ + @NotNull public ITransactionApi txs() { return txs; } @@ -104,6 +108,7 @@ public ITransactionApi txs() { /** * [BETA] API for getting block rewards and uncles */ + @NotNull public IBlockApi block() { return block; } @@ -111,6 +116,7 @@ public IBlockApi block() { /** * [BETA] API for interaction with eth_getLogs */ + @NotNull public ILogsApi logs() { return logs; } @@ -118,6 +124,7 @@ public ILogsApi logs() { /** * API for interacting with geth/proxy etherscan */ + @NotNull public IProxyApi proxy() { return proxy; } @@ -125,6 +132,7 @@ public IProxyApi proxy() { /** * API for eth price and supply statistic */ + @NotNull public IStatisticApi stats() { return stats; } 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 11f9261..6894b0c 100644 --- a/src/main/java/io/api/etherscan/executor/impl/HttpExecutor.java +++ b/src/main/java/io/api/etherscan/executor/impl/HttpExecutor.java @@ -15,6 +15,7 @@ import java.util.HashMap; import java.util.Map; import java.util.zip.GZIPInputStream; +import java.util.zip.InflaterInputStream; import static java.net.HttpURLConnection.HTTP_MOVED_PERM; import static java.net.HttpURLConnection.HTTP_MOVED_TEMP; @@ -31,13 +32,12 @@ public class HttpExecutor implements IHttpExecutor { private static final Map DEFAULT_HEADERS = new HashMap<>(); private static final int CONNECT_TIMEOUT = 8000; - private static final int READ_TIMEOUT = 30000; + private static final int READ_TIMEOUT = 0; static { - DEFAULT_HEADERS.put("Accept-Language", "en;q=0.9"); + DEFAULT_HEADERS.put("Accept-Language", "en"); DEFAULT_HEADERS.put("Accept-Encoding", "deflate, gzip"); DEFAULT_HEADERS.put("User-Agent", "Chrome/68.0.3440.106"); - DEFAULT_HEADERS.put("Content-Type", "application/x-www-form-urlencoded"); DEFAULT_HEADERS.put("Accept-Charset", "UTF-8"); } @@ -46,15 +46,14 @@ public class HttpExecutor implements IHttpExecutor { private final int readTimeout; public HttpExecutor() { - this(CONNECT_TIMEOUT, READ_TIMEOUT); + this(CONNECT_TIMEOUT); } public HttpExecutor(final int connectTimeout) { this(connectTimeout, READ_TIMEOUT); } - public HttpExecutor(final int connectTimeout, - final int readTimeout) { + public HttpExecutor(final int connectTimeout, final int readTimeout) { this(connectTimeout, readTimeout, DEFAULT_HEADERS); } @@ -142,8 +141,13 @@ private String readData(final HttpURLConnection connection) throws IOException { } private InputStreamReader getStreamReader(final HttpURLConnection connection) throws IOException { - return (connection.getContentEncoding() != null && "gzip".equals(connection.getContentEncoding())) - ? new InputStreamReader(new GZIPInputStream(connection.getInputStream()), "utf-8") - : new InputStreamReader(connection.getInputStream(), "utf-8"); + final boolean haveEncoding = connection.getContentEncoding() != null; + + if (haveEncoding && "gzip".equals(connection.getContentEncoding())) + return new InputStreamReader(new GZIPInputStream(connection.getInputStream()), "utf-8"); + else if (haveEncoding && "deflate".equals(connection.getContentEncoding())) + return new InputStreamReader(new InflaterInputStream(connection.getInputStream()), "utf-8"); + else + return new InputStreamReader(connection.getInputStream(), "utf-8"); } } diff --git a/src/main/java/io/api/etherscan/model/Abi.java b/src/main/java/io/api/etherscan/model/Abi.java index 08acdd4..b5203bc 100644 --- a/src/main/java/io/api/etherscan/model/Abi.java +++ b/src/main/java/io/api/etherscan/model/Abi.java @@ -36,4 +36,30 @@ public String getContractAbi() { public boolean isVerified() { return isVerified; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Abi abi = (Abi) o; + + if (isVerified != abi.isVerified) return false; + return contractAbi != null ? contractAbi.equals(abi.contractAbi) : abi.contractAbi == null; + } + + @Override + public int hashCode() { + int result = contractAbi != null ? contractAbi.hashCode() : 0; + result = 31 * result + (isVerified ? 1 : 0); + return result; + } + + @Override + public String toString() { + return "Abi{" + + "contractAbi='" + contractAbi + '\'' + + ", isVerified=" + isVerified + + '}'; + } } diff --git a/src/main/java/io/api/etherscan/model/BaseTx.java b/src/main/java/io/api/etherscan/model/BaseTx.java index 5724bd7..8dd709a 100644 --- a/src/main/java/io/api/etherscan/model/BaseTx.java +++ b/src/main/java/io/api/etherscan/model/BaseTx.java @@ -84,4 +84,21 @@ public boolean equals(Object o) { public int hashCode() { return hash != null ? hash.hashCode() : 0; } + + @Override + public String toString() { + return "BaseTx{" + + "blockNumber=" + blockNumber + + ", timeStamp='" + timeStamp + '\'' + + ", _timeStamp=" + _timeStamp + + ", hash='" + hash + '\'' + + ", from='" + from + '\'' + + ", to='" + to + '\'' + + ", value=" + value + + ", contractAddress='" + contractAddress + '\'' + + ", input='" + input + '\'' + + ", gas=" + gas + + ", gasUsed=" + gasUsed + + '}'; + } } diff --git a/src/main/java/io/api/etherscan/model/Block.java b/src/main/java/io/api/etherscan/model/Block.java index 8ea4389..0406e4e 100644 --- a/src/main/java/io/api/etherscan/model/Block.java +++ b/src/main/java/io/api/etherscan/model/Block.java @@ -49,4 +49,14 @@ public boolean equals(Object o) { public int hashCode() { return (int) (blockNumber ^ (blockNumber >>> 32)); } + + @Override + public String toString() { + return "Block{" + + "blockNumber=" + blockNumber + + ", blockReward=" + blockReward + + ", timeStamp='" + timeStamp + '\'' + + ", _timeStamp=" + _timeStamp + + '}'; + } } diff --git a/src/main/java/io/api/etherscan/model/Log.java b/src/main/java/io/api/etherscan/model/Log.java index 321e808..d39cc61 100644 --- a/src/main/java/io/api/etherscan/model/Log.java +++ b/src/main/java/io/api/etherscan/model/Log.java @@ -97,4 +97,26 @@ public Long getLogIndex() { return _logIndex; } // + + @Override + public String toString() { + return "Log{" + + "blockNumber='" + blockNumber + '\'' + + ", _blockNumber=" + _blockNumber + + ", address='" + address + '\'' + + ", transactionHash='" + transactionHash + '\'' + + ", transactionIndex='" + transactionIndex + '\'' + + ", _transactionIndex=" + _transactionIndex + + ", timeStamp='" + timeStamp + '\'' + + ", _timeStamp=" + _timeStamp + + ", data='" + data + '\'' + + ", gasPrice='" + gasPrice + '\'' + + ", _gasPrice=" + _gasPrice + + ", gasUsed='" + gasUsed + '\'' + + ", _gasUsed=" + _gasUsed + + ", topics=" + topics + + ", logIndex='" + logIndex + '\'' + + ", _logIndex=" + _logIndex + + '}'; + } } diff --git a/src/main/java/io/api/etherscan/model/Price.java b/src/main/java/io/api/etherscan/model/Price.java index ea1d6a5..fec7846 100644 --- a/src/main/java/io/api/etherscan/model/Price.java +++ b/src/main/java/io/api/etherscan/model/Price.java @@ -37,4 +37,16 @@ public LocalDateTime btcTimestamp() { _ethbtc_timestamp = LocalDateTime.ofEpochSecond(Long.valueOf(ethbtc_timestamp), 0, ZoneOffset.UTC); return _ethbtc_timestamp; } + + @Override + 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/api/etherscan/model/Status.java b/src/main/java/io/api/etherscan/model/Status.java index cfaed07..7e05f85 100644 --- a/src/main/java/io/api/etherscan/model/Status.java +++ b/src/main/java/io/api/etherscan/model/Status.java @@ -21,4 +21,12 @@ public boolean haveError() { public String getErrDescription() { return errDescription; } + + @Override + public String toString() { + return "Status{" + + "isError=" + isError + + ", errDescription='" + errDescription + '\'' + + '}'; + } } diff --git a/src/main/java/io/api/etherscan/model/TokenBalance.java b/src/main/java/io/api/etherscan/model/TokenBalance.java index 54d025a..b40ae0c 100644 --- a/src/main/java/io/api/etherscan/model/TokenBalance.java +++ b/src/main/java/io/api/etherscan/model/TokenBalance.java @@ -38,4 +38,11 @@ public int hashCode() { result = 31 * result + (tokenContract != null ? tokenContract.hashCode() : 0); return result; } + + @Override + public String toString() { + return "TokenBalance{" + + "tokenContract='" + tokenContract + '\'' + + '}'; + } } diff --git a/src/main/java/io/api/etherscan/model/Tx.java b/src/main/java/io/api/etherscan/model/Tx.java index 0f2b3c2..e940896 100644 --- a/src/main/java/io/api/etherscan/model/Tx.java +++ b/src/main/java/io/api/etherscan/model/Tx.java @@ -54,4 +54,18 @@ public long getConfirmations() { return confirmations; } // + + @Override + public String toString() { + return "Tx{" + + "nonce=" + nonce + + ", blockHash='" + blockHash + '\'' + + ", transactionIndex=" + transactionIndex + + ", gasPrice=" + gasPrice + + ", cumulativeGasUsed=" + cumulativeGasUsed + + ", confirmations=" + confirmations + + ", isError='" + isError + '\'' + + ", txreceipt_status='" + txreceipt_status + '\'' + + '}'; + } } diff --git a/src/main/java/io/api/etherscan/model/TxInternal.java b/src/main/java/io/api/etherscan/model/TxInternal.java index d40d656..024b4bb 100644 --- a/src/main/java/io/api/etherscan/model/TxInternal.java +++ b/src/main/java/io/api/etherscan/model/TxInternal.java @@ -30,4 +30,14 @@ public String getErrCode() { return errCode; } // + + @Override + public String toString() { + return "TxInternal{" + + "type='" + type + '\'' + + ", traceId=" + traceId + + ", isError=" + isError + + ", errCode='" + errCode + '\'' + + '}'; + } } diff --git a/src/main/java/io/api/etherscan/model/TxToken.java b/src/main/java/io/api/etherscan/model/TxToken.java index edd04b6..466083a 100644 --- a/src/main/java/io/api/etherscan/model/TxToken.java +++ b/src/main/java/io/api/etherscan/model/TxToken.java @@ -55,4 +55,19 @@ public long getConfirmations() { return confirmations; } // + + @Override + public String toString() { + return "TxToken{" + + "nonce=" + nonce + + ", blockHash='" + blockHash + '\'' + + ", tokenName='" + tokenName + '\'' + + ", tokenSymbol='" + tokenSymbol + '\'' + + ", tokenDecimal='" + tokenDecimal + '\'' + + ", transactionIndex=" + transactionIndex + + ", gasPrice=" + gasPrice + + ", cumulativeGasUsed=" + cumulativeGasUsed + + ", confirmations=" + confirmations + + '}'; + } } diff --git a/src/main/java/io/api/etherscan/model/Uncle.java b/src/main/java/io/api/etherscan/model/Uncle.java index e8e9fd6..e6e5eb2 100644 --- a/src/main/java/io/api/etherscan/model/Uncle.java +++ b/src/main/java/io/api/etherscan/model/Uncle.java @@ -27,4 +27,13 @@ public int getUnclePosition() { return unclePosition; } // + + @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 index 59b4ef4..6b7eb4f 100644 --- a/src/main/java/io/api/etherscan/model/UncleBlock.java +++ b/src/main/java/io/api/etherscan/model/UncleBlock.java @@ -53,4 +53,13 @@ public int hashCode() { result = (int) (31 * result + getBlockNumber()); return result; } + + @Override + public String toString() { + return "UncleBlock{" + + "blockMiner='" + blockMiner + '\'' + + ", uncles=" + uncles + + ", uncleInclusionReward='" + uncleInclusionReward + '\'' + + '}'; + } } diff --git a/src/main/java/io/api/etherscan/model/Wei.java b/src/main/java/io/api/etherscan/model/Wei.java index 6bf0def..cf5f465 100644 --- a/src/main/java/io/api/etherscan/model/Wei.java +++ b/src/main/java/io/api/etherscan/model/Wei.java @@ -52,4 +52,11 @@ public boolean equals(Object o) { public int hashCode() { return result != null ? result.hashCode() : 0; } + + @Override + public String toString() { + return "Wei{" + + "result=" + result + + '}'; + } } diff --git a/src/main/java/io/api/etherscan/model/proxy/BlockProxy.java b/src/main/java/io/api/etherscan/model/proxy/BlockProxy.java index 90bc83e..414a797 100644 --- a/src/main/java/io/api/etherscan/model/proxy/BlockProxy.java +++ b/src/main/java/io/api/etherscan/model/proxy/BlockProxy.java @@ -135,4 +135,55 @@ public List getTransactions() { return transactions; } // + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + BlockProxy that = (BlockProxy) o; + + if (number != null ? !number.equals(that.number) : that.number != null) return false; + if (hash != null ? !hash.equals(that.hash) : that.hash != null) return false; + return parentHash != null ? parentHash.equals(that.parentHash) : that.parentHash == null; + } + + @Override + public int hashCode() { + int result = number != null ? number.hashCode() : 0; + result = 31 * result + (hash != null ? hash.hashCode() : 0); + result = 31 * result + (parentHash != null ? parentHash.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "BlockProxy{" + + "number='" + number + '\'' + + ", _number=" + _number + + ", hash='" + hash + '\'' + + ", parentHash='" + parentHash + '\'' + + ", stateRoot='" + stateRoot + '\'' + + ", size='" + size + '\'' + + ", _size=" + _size + + ", difficulty='" + difficulty + '\'' + + ", totalDifficulty='" + totalDifficulty + '\'' + + ", timestamp='" + timestamp + '\'' + + ", _timestamp=" + _timestamp + + ", miner='" + miner + '\'' + + ", nonce='" + nonce + '\'' + + ", extraData='" + extraData + '\'' + + ", logsBloom='" + logsBloom + '\'' + + ", mixHash='" + mixHash + '\'' + + ", gasUsed='" + gasUsed + '\'' + + ", _gasUsed=" + _gasUsed + + ", gasLimit='" + gasLimit + '\'' + + ", _gasLimit=" + _gasLimit + + ", sha3Uncles='" + sha3Uncles + '\'' + + ", uncles=" + uncles + + ", receiptsRoot='" + receiptsRoot + '\'' + + ", transactionsRoot='" + transactionsRoot + '\'' + + ", transactions=" + transactions + + '}'; + } } diff --git a/src/main/java/io/api/etherscan/model/proxy/ReceiptProxy.java b/src/main/java/io/api/etherscan/model/proxy/ReceiptProxy.java index e96a3df..b6e2bcc 100644 --- a/src/main/java/io/api/etherscan/model/proxy/ReceiptProxy.java +++ b/src/main/java/io/api/etherscan/model/proxy/ReceiptProxy.java @@ -89,4 +89,47 @@ public String getLogsBloom() { return logsBloom; } // + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ReceiptProxy that = (ReceiptProxy) o; + + if (blockNumber != null ? !blockNumber.equals(that.blockNumber) : that.blockNumber != null) return false; + if (transactionHash != null ? !transactionHash.equals(that.transactionHash) : that.transactionHash != null) + return false; + return transactionIndex != null ? transactionIndex.equals(that.transactionIndex) : that.transactionIndex == null; + } + + @Override + public int hashCode() { + int result = blockNumber != null ? blockNumber.hashCode() : 0; + result = 31 * result + (transactionHash != null ? transactionHash.hashCode() : 0); + result = 31 * result + (transactionIndex != null ? transactionIndex.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "ReceiptProxy{" + + "root='" + root + '\'' + + ", from='" + from + '\'' + + ", to='" + to + '\'' + + ", blockNumber='" + blockNumber + '\'' + + ", _blockNumber=" + _blockNumber + + ", blockHash='" + blockHash + '\'' + + ", transactionHash='" + transactionHash + '\'' + + ", transactionIndex='" + transactionIndex + '\'' + + ", _transactionIndex=" + _transactionIndex + + ", gasUsed='" + gasUsed + '\'' + + ", _gasUsed=" + _gasUsed + + ", cumulativeGasUsed='" + cumulativeGasUsed + '\'' + + ", _cumulativeGasUsed=" + _cumulativeGasUsed + + ", contractAddress='" + contractAddress + '\'' + + ", logs=" + logs + + ", logsBloom='" + logsBloom + '\'' + + '}'; + } } diff --git a/src/main/java/io/api/etherscan/model/proxy/TxProxy.java b/src/main/java/io/api/etherscan/model/proxy/TxProxy.java index 087b441..c45e679 100644 --- a/src/main/java/io/api/etherscan/model/proxy/TxProxy.java +++ b/src/main/java/io/api/etherscan/model/proxy/TxProxy.java @@ -99,4 +99,49 @@ public Long getBlockNumber() { return _blockNumber; } // + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + TxProxy txProxy = (TxProxy) o; + + if (hash != null ? !hash.equals(txProxy.hash) : txProxy.hash != null) return false; + if (blockHash != null ? !blockHash.equals(txProxy.blockHash) : txProxy.blockHash != null) return false; + return blockNumber != null ? blockNumber.equals(txProxy.blockNumber) : txProxy.blockNumber == null; + } + + @Override + public int hashCode() { + int result = hash != null ? hash.hashCode() : 0; + result = 31 * result + (blockHash != null ? blockHash.hashCode() : 0); + result = 31 * result + (blockNumber != null ? blockNumber.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "TxProxy{" + + "to='" + to + '\'' + + ", hash='" + hash + '\'' + + ", transactionIndex='" + transactionIndex + '\'' + + ", _transactionIndex=" + _transactionIndex + + ", from='" + from + '\'' + + ", v='" + v + '\'' + + ", input='" + input + '\'' + + ", s='" + s + '\'' + + ", r='" + r + '\'' + + ", nonce='" + nonce + '\'' + + ", _nonce=" + _nonce + + ", value='" + value + '\'' + + ", gas='" + gas + '\'' + + ", _gas=" + _gas + + ", gasPrice='" + gasPrice + '\'' + + ", _gasPrice=" + _gasPrice + + ", blockHash='" + blockHash + '\'' + + ", blockNumber='" + blockNumber + '\'' + + ", _blockNumber=" + _blockNumber + + '}'; + } } diff --git a/src/main/java/io/api/etherscan/model/utility/BaseResponseTO.java b/src/main/java/io/api/etherscan/model/utility/BaseResponseTO.java index 3c289f6..144deef 100644 --- a/src/main/java/io/api/etherscan/model/utility/BaseResponseTO.java +++ b/src/main/java/io/api/etherscan/model/utility/BaseResponseTO.java @@ -14,9 +14,7 @@ public abstract class BaseResponseTO { private String message; public int getStatus() { - return (BasicUtils.isEmpty(status)) - ? -1 - : Integer.valueOf(status); + return (BasicUtils.isEmpty(status)) ? -1 : Integer.valueOf(status); } public String getMessage() { diff --git a/src/main/java/io/api/etherscan/util/BasicUtils.java b/src/main/java/io/api/etherscan/util/BasicUtils.java index cd980d9..cec41e5 100644 --- a/src/main/java/io/api/etherscan/util/BasicUtils.java +++ b/src/main/java/io/api/etherscan/util/BasicUtils.java @@ -31,7 +31,7 @@ public static boolean isEmpty(String value) { } public static boolean isBlank(String value) { - return value == null || value.isEmpty() || value.trim().isEmpty(); + return isEmpty(value) || value.trim().isEmpty(); } public static boolean isEmpty(Collection collection) { @@ -39,8 +39,8 @@ public static boolean isEmpty(Collection collection) { } public static BlockParam compensateBlocks(long startBlock, long endBlock) { - long startCompensated = compensateMinBlock(startBlock); - long endCompensated = compensateMaxBlock(endBlock); + long startCompensated = compensateMinBlock(startBlock); + long endCompensated = compensateMaxBlock(endBlock); final long startFinal = (startCompensated > endCompensated) ? endCompensated @@ -79,7 +79,7 @@ public static boolean isNotHex(String value) { public static BigInteger parseHex(String hex) { try { - if(BasicUtils.isEmpty(hex)) + if (BasicUtils.isEmpty(hex)) return BigInteger.valueOf(0); final String formatted = (hex.length() > 2 && hex.charAt(0) == '0' && hex.charAt(1) == 'x') @@ -93,23 +93,26 @@ public static BigInteger parseHex(String hex) { } public static void validateAddress(String address) { - if(isNotAddress(address)) + if (isNotAddress(address)) throw new InvalidAddressException("Address [" + address + "] is not Ethereum based."); } public static void validateTxHash(String txhash) { - if(isNotTxHash(txhash)) + if (isNotTxHash(txhash)) throw new InvalidTxHashException("TxHash [" + txhash + "] is not Ethereum based."); } public static void validateTxResponse(T response) { - if(response == null) + if (response == null) throw new EtherScanException("EtherScan responded with null value"); - if(response.getStatus() != 1 - && !response.getMessage().startsWith("No tra") - && !response.getMessage().startsWith("No rec")) - throw new EtherScanException(response.getMessage() + ", with status " + response.getStatus()); + if (response.getStatus() != 1) { + if (response.getMessage() == null) { + throw new EtherScanException("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.getMessage() + ", with status " + response.getStatus()); + } + } } public static void validateAddresses(List addresses) { @@ -121,7 +124,7 @@ public static void validateAddresses(List addresses) { @NotNull public static List> partition(List list, int pairSize) { - if(isEmpty(list)) + if (isEmpty(list)) return Collections.emptyList(); final List> partitioned = new ArrayList<>(); @@ -132,14 +135,14 @@ public static List> partition(List list, int pairSize) { while (iterator.hasNext()) { temp.add(iterator.next()); - if(++counter > pairSize) { + if (++counter > pairSize) { partitioned.add(temp); temp = new ArrayList<>(); counter = 0; } } - if(!temp.isEmpty()) + if (!temp.isEmpty()) partitioned.add(temp); return partitioned; diff --git a/src/test/java/io/api/etherscan/account/AccountBalanceListTest.java b/src/test/java/io/api/etherscan/account/AccountBalanceListTest.java index 766b17e..e2f5ae0 100644 --- a/src/test/java/io/api/etherscan/account/AccountBalanceListTest.java +++ b/src/test/java/io/api/etherscan/account/AccountBalanceListTest.java @@ -31,8 +31,15 @@ public void correct() { assertFalse(balances.isEmpty()); assertEquals(2, balances.size()); for(Balance balance : balances) { + assertNotNull(balance.getAddress()); + assertNotNull(balance.getGwei()); + assertNotNull(balance.getKwei()); + assertNotNull(balance.getMwei()); + assertNotNull(balance.getEther()); + assertNotNull(balance.getGwei()); assertNotNull(balance.getAddress()); assertNotEquals(0, balance.getWei()); + assertNotNull(balance.toString()); } } diff --git a/src/test/java/io/api/etherscan/account/AccountBalanceTest.java b/src/test/java/io/api/etherscan/account/AccountBalanceTest.java index c810171..21eca22 100644 --- a/src/test/java/io/api/etherscan/account/AccountBalanceTest.java +++ b/src/test/java/io/api/etherscan/account/AccountBalanceTest.java @@ -75,6 +75,7 @@ public void correct() { assertNotNull(balance.getEther()); assertNotNull(balance.getAddress()); assertNotEquals(0, balance.getWei()); + assertNotNull(balance.toString()); } @Test(expected = InvalidAddressException.class) diff --git a/src/test/java/io/api/etherscan/account/AccountMinedBlocksTest.java b/src/test/java/io/api/etherscan/account/AccountMinedBlocksTest.java index b62db85..40bcfc4 100644 --- a/src/test/java/io/api/etherscan/account/AccountMinedBlocksTest.java +++ b/src/test/java/io/api/etherscan/account/AccountMinedBlocksTest.java @@ -71,6 +71,7 @@ public void correct() { assertBlocks(blocks); Block block = new Block(); assertFalse(blocks.get(0).equals(block)); + assertNotNull(block.toString()); } @Test(expected = InvalidAddressException.class) diff --git a/src/test/java/io/api/etherscan/contract/ContractApiTest.java b/src/test/java/io/api/etherscan/contract/ContractApiTest.java index 990c268..23e08f2 100644 --- a/src/test/java/io/api/etherscan/contract/ContractApiTest.java +++ b/src/test/java/io/api/etherscan/contract/ContractApiTest.java @@ -22,6 +22,7 @@ public void correct() { assertNotNull(abi); assertTrue(abi.isVerified()); assertTrue(abi.haveAbi()); + assertNotNull(abi.getContractAbi()); } @Test(expected = InvalidAddressException.class) diff --git a/src/test/java/io/api/util/UtilTests.java b/src/test/java/io/api/util/UtilTests.java index 4579010..f19e188 100644 --- a/src/test/java/io/api/util/UtilTests.java +++ b/src/test/java/io/api/util/UtilTests.java @@ -2,6 +2,8 @@ import com.google.gson.Gson; import io.api.etherscan.error.EtherScanException; +import io.api.etherscan.error.NoResponseException; +import io.api.etherscan.error.ParseException; import io.api.etherscan.model.utility.StringResponseTO; import io.api.etherscan.util.BasicUtils; import org.junit.Assert; @@ -10,6 +12,8 @@ import java.util.ArrayList; import java.util.List; +import static io.api.etherscan.util.BasicUtils.*; + /** * ! NO DESCRIPTION ! * @@ -22,18 +26,76 @@ public class UtilTests extends Assert { public void responseValidateEmpty() { String response = "{\"status\":\"0\",\"message\":\"No ether\",\"result\":\"status\"}"; StringResponseTO responseTO = new Gson().fromJson(response, StringResponseTO.class); - BasicUtils.validateTxResponse(responseTO); + validateTxResponse(responseTO); } @Test(expected = EtherScanException.class) public void responseValidateNullable() { - BasicUtils.validateTxResponse(null); + validateTxResponse(null); } @Test public void partitionEmpty() { ArrayList list = new ArrayList<>(); - List> lists = BasicUtils.partition(list, 12); + List> lists = partition(list, 12); + assertTrue(lists.isEmpty()); + } + + @Test + public void partitionNullParam() { + List> lists = partition(null, 12); assertTrue(lists.isEmpty()); } + + @Test + public 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() { + boolean result = isEmpty(new ArrayList<>()); + assertTrue(result); + } + + @Test + public void isNotAddressNull() { + boolean result = isNotAddress(""); + assertTrue(result); + } + + @Test + public void isNotHexNull() { + boolean result = isNotHex(""); + assertTrue(result); + } + + @Test(expected = EtherScanException.class) + public void isResponseStatusInvalidThrows() { + StringResponseTO responseTO = new StringResponseTO(); + validateTxResponse(responseTO); + } + + @Test(expected = EtherScanException.class) + public void isResponseNullThrows() { + validateTxResponse(null); + } + + @Test(expected = ParseException.class) + public void isThrowParseException() { + throw new ParseException("Test", null); + } + + @Test(expected = NoResponseException.class) + public void isThrowNoResponseException() { + throw new NoResponseException("Test"); + } } From 406ddcc377ae69b092b36233ee0d8473c17fbe93 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Mon, 10 Dec 2018 17:46:17 +0300 Subject: [PATCH 002/112] Code coverage improvements --- src/test/java/io/api/etherscan/EtherScanApiTest.java | 8 +++----- .../io/api/etherscan/account/AccountTokenBalanceTest.java | 1 + .../etherscan/account/AccountTxInternalByHashTest.java | 1 + .../io/api/etherscan/account/AccountTxInternalTest.java | 1 + .../java/io/api/etherscan/account/AccountTxTokenTest.java | 1 + .../java/io/api/etherscan/account/AccountTxsTest.java | 1 + src/test/java/io/api/etherscan/block/BlockApiTest.java | 1 + .../java/io/api/etherscan/contract/ContractApiTest.java | 1 + src/test/java/io/api/etherscan/logs/LogsApiTest.java | 1 + .../java/io/api/etherscan/proxy/ProxyBlockApiTest.java | 1 + .../io/api/etherscan/proxy/ProxyBlockUncleApiTest.java | 1 + src/test/java/io/api/etherscan/proxy/ProxyTxApiTest.java | 1 + .../io/api/etherscan/proxy/ProxyTxReceiptApiTest.java | 1 + .../io/api/etherscan/statistic/StatisticPriceApiTest.java | 1 + .../api/etherscan/statistic/StatisticSupplyApiTest.java | 1 + .../api/etherscan/transaction/TransactionExecApiTest.java | 1 + 16 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/test/java/io/api/etherscan/EtherScanApiTest.java b/src/test/java/io/api/etherscan/EtherScanApiTest.java index 9c3169a..b7fab99 100644 --- a/src/test/java/io/api/etherscan/EtherScanApiTest.java +++ b/src/test/java/io/api/etherscan/EtherScanApiTest.java @@ -34,14 +34,12 @@ public void validKey() { @Test(expected = ApiKeyException.class) public void emptyKey() { - String emptyKey = ""; - EtherScanApi api = new EtherScanApi(emptyKey, network); + new EtherScanApi("", network); } @Test(expected = ApiKeyException.class) public void blankKey() { - String blankKey = " "; - EtherScanApi api = new EtherScanApi(blankKey, network); + new EtherScanApi(" ", network); } @Test(expected = ApiException.class) @@ -71,6 +69,6 @@ public void timeout() { Supplier supplier = () -> new HttpExecutor(300, 300); EtherScanApi api = new EtherScanApi(EthNetwork.KOVAN, supplier); List blocks = api.account().minedBlocks("0x0010f94b296A852aAac52EA6c5Ac72e03afD032D"); - assertNotNull(api); + assertNotNull(blocks); } } diff --git a/src/test/java/io/api/etherscan/account/AccountTokenBalanceTest.java b/src/test/java/io/api/etherscan/account/AccountTokenBalanceTest.java index df9dc0a..a1499ec 100644 --- a/src/test/java/io/api/etherscan/account/AccountTokenBalanceTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTokenBalanceTest.java @@ -91,6 +91,7 @@ public void correct() { assertNotNull(balance.getAddress()); assertNotNull(balance.getContract()); assertNotEquals(0, balance.getWei()); + assertNotNull(balance.toString()); TokenBalance balance1 = new TokenBalance("", BigInteger.ONE, ""); assertFalse(balance.equals(balance1)); diff --git a/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java b/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java index dbd1f02..18aac45 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java @@ -87,6 +87,7 @@ public void correct() { assertFalse(txs.get(0).haveError()); assertNotEquals(-1, txs.get(0).getTraceId()); assertTrue(BasicUtils.isEmpty(txs.get(0).getErrCode())); + assertNotNull(txs.get(0).toString()); } @Test(expected = InvalidTxHashException.class) diff --git a/src/test/java/io/api/etherscan/account/AccountTxInternalTest.java b/src/test/java/io/api/etherscan/account/AccountTxInternalTest.java index e850088..a80be28 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxInternalTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTxInternalTest.java @@ -24,6 +24,7 @@ public void correct() { assertNotNull(txs); assertEquals(66, txs.size()); assertTxs(txs); + assertNotNull(txs.get(0).toString()); } @Test diff --git a/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java b/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java index d8096f7..3df0c4b 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java @@ -24,6 +24,7 @@ public void correct() { assertNotNull(txs); assertEquals(8, txs.size()); assertTxs(txs); + assertNotNull(txs.get(0).toString()); } @Test diff --git a/src/test/java/io/api/etherscan/account/AccountTxsTest.java b/src/test/java/io/api/etherscan/account/AccountTxsTest.java index 1fbe7ab..c7d4ff9 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxsTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTxsTest.java @@ -36,6 +36,7 @@ public void correct() { assertNotNull(txs.get(0).getContractAddress()); assertNotNull(txs.get(0).getInput()); assertNotEquals(txs.get(0).hashCode(), txs.get(1).hashCode()); + assertNotNull(txs.get(0).toString()); } @Test diff --git a/src/test/java/io/api/etherscan/block/BlockApiTest.java b/src/test/java/io/api/etherscan/block/BlockApiTest.java index 8502ee1..71cdec8 100644 --- a/src/test/java/io/api/etherscan/block/BlockApiTest.java +++ b/src/test/java/io/api/etherscan/block/BlockApiTest.java @@ -28,6 +28,7 @@ public void correct() { assertFalse(uncles.get().getUncles().isEmpty()); assertNotNull(uncles.get().getUncles().get(0).getBlockreward()); assertNotNull(uncles.get().getUncles().get(0).getMiner()); + assertNotNull(uncles.get().toString()); assertNotEquals(0, uncles.get().hashCode()); diff --git a/src/test/java/io/api/etherscan/contract/ContractApiTest.java b/src/test/java/io/api/etherscan/contract/ContractApiTest.java index 23e08f2..96c992e 100644 --- a/src/test/java/io/api/etherscan/contract/ContractApiTest.java +++ b/src/test/java/io/api/etherscan/contract/ContractApiTest.java @@ -23,6 +23,7 @@ public void correct() { assertTrue(abi.isVerified()); assertTrue(abi.haveAbi()); assertNotNull(abi.getContractAbi()); + assertNotNull(abi.toString()); } @Test(expected = InvalidAddressException.class) diff --git a/src/test/java/io/api/etherscan/logs/LogsApiTest.java b/src/test/java/io/api/etherscan/logs/LogsApiTest.java index 1ddfad4..65a1d49 100644 --- a/src/test/java/io/api/etherscan/logs/LogsApiTest.java +++ b/src/test/java/io/api/etherscan/logs/LogsApiTest.java @@ -79,6 +79,7 @@ public void validateQuery() { assertNotNull(logs.get(0).getTopics()); assertNotNull(logs.get(0).getLogIndex()); assertNotNull(logs.get(0).getGasPrice()); + assertNotNull(logs.get(0).toString()); } } } diff --git a/src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java index 1355e87..181714b 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java @@ -43,6 +43,7 @@ public void correct() { assertNotNull(proxy.getTransactionsRoot()); assertNotNull(proxy.getReceiptsRoot()); assertNotNull(proxy.getUncles()); + assertNotNull(proxy.toString()); } @Test diff --git a/src/test/java/io/api/etherscan/proxy/ProxyBlockUncleApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyBlockUncleApiTest.java index 56002d3..3ccac29 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyBlockUncleApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyBlockUncleApiTest.java @@ -22,6 +22,7 @@ public void correct() { Optional block = api.proxy().blockUncle(603183, 0); assertTrue(block.isPresent()); assertNotNull(block.get().getHash()); + assertNotNull(block.get().toString()); } @Test diff --git a/src/test/java/io/api/etherscan/proxy/ProxyTxApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyTxApiTest.java index 6a87b9b..8edc183 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyTxApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyTxApiTest.java @@ -27,6 +27,7 @@ public void correctByHash() { assertNotNull(tx.get().getTo()); assertNotNull(tx.get().getHash()); assertNotNull(tx.get().getNonce()); + assertNotNull(tx.get().toString()); } @Test diff --git a/src/test/java/io/api/etherscan/proxy/ProxyTxReceiptApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyTxReceiptApiTest.java index fd6e89b..addb774 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyTxReceiptApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyTxReceiptApiTest.java @@ -35,6 +35,7 @@ public void correct() { assertNotNull(infoProxy.get().getLogs()); assertNotNull(infoProxy.get().getLogsBloom()); assertNull(infoProxy.get().getContractAddress()); + assertNotNull(infoProxy.get().toString()); } @Test(expected = InvalidTxHashException.class) diff --git a/src/test/java/io/api/etherscan/statistic/StatisticPriceApiTest.java b/src/test/java/io/api/etherscan/statistic/StatisticPriceApiTest.java index 81632fc..7581f1d 100644 --- a/src/test/java/io/api/etherscan/statistic/StatisticPriceApiTest.java +++ b/src/test/java/io/api/etherscan/statistic/StatisticPriceApiTest.java @@ -23,5 +23,6 @@ public void correct() { assertNotNull(price.usdTimestamp()); assertNotEquals(0, price.inBtc()); assertNotEquals(0, price.inUsd()); + assertNotNull(price.toString()); } } diff --git a/src/test/java/io/api/etherscan/statistic/StatisticSupplyApiTest.java b/src/test/java/io/api/etherscan/statistic/StatisticSupplyApiTest.java index 34d47aa..8b7e10a 100644 --- a/src/test/java/io/api/etherscan/statistic/StatisticSupplyApiTest.java +++ b/src/test/java/io/api/etherscan/statistic/StatisticSupplyApiTest.java @@ -24,5 +24,6 @@ public void correct() { assertNotNull(supply.asKwei()); assertNotNull(supply.asMwei()); assertNotNull(supply.asEther()); + assertNotNull(supply.toString()); } } diff --git a/src/test/java/io/api/etherscan/transaction/TransactionExecApiTest.java b/src/test/java/io/api/etherscan/transaction/TransactionExecApiTest.java index e952790..9742c3c 100644 --- a/src/test/java/io/api/etherscan/transaction/TransactionExecApiTest.java +++ b/src/test/java/io/api/etherscan/transaction/TransactionExecApiTest.java @@ -24,6 +24,7 @@ public void correct() { assertTrue(status.isPresent()); assertTrue(status.get().haveError()); assertNotNull(status.get().getErrDescription()); + assertNotNull(status.get().toString()); } @Test(expected = InvalidTxHashException.class) From 9d1fa02f07a4e5b91aa4340e79d45c55368b344c Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Mon, 10 Dec 2018 23:23:36 +0300 Subject: [PATCH 003/112] More code coverage improvements --- src/main/java/io/api/etherscan/App.java | 7 -- .../account/AccountBalanceListTest.java | 2 + .../account/AccountMinedBlocksTest.java | 7 +- .../account/AccountTokenBalanceTest.java | 8 +- .../account/AccountTxInternalByHashTest.java | 3 + .../etherscan/account/AccountTxTokenTest.java | 2 + .../api/etherscan/account/AccountTxsTest.java | 3 +- .../io/api/etherscan/block/BlockApiTest.java | 5 +- .../etherscan/contract/ContractApiTest.java | 6 +- .../etherscan/logs/LogQueryBuilderTest.java | 90 ++++--------------- .../io/api/etherscan/logs/LogsApiTest.java | 18 ++-- .../etherscan/proxy/ProxyBlockApiTest.java | 4 + .../api/etherscan/proxy/ProxyTxApiTest.java | 4 + .../proxy/ProxyTxReceiptApiTest.java | 4 + .../statistic/StatisticPriceApiTest.java | 4 + .../statistic/StatisticSupplyApiTest.java | 6 ++ .../transaction/TransactionExecApiTest.java | 6 +- .../TransactionReceiptApiTest.java | 2 +- src/test/java/io/api/util/UtilTests.java | 19 +++- 19 files changed, 98 insertions(+), 102 deletions(-) delete mode 100644 src/main/java/io/api/etherscan/App.java diff --git a/src/main/java/io/api/etherscan/App.java b/src/main/java/io/api/etherscan/App.java deleted file mode 100644 index 3b0a54f..0000000 --- a/src/main/java/io/api/etherscan/App.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.api.etherscan; - -public class App { - public static void main(String[] args) { - - } -} diff --git a/src/test/java/io/api/etherscan/account/AccountBalanceListTest.java b/src/test/java/io/api/etherscan/account/AccountBalanceListTest.java index e2f5ae0..9992962 100644 --- a/src/test/java/io/api/etherscan/account/AccountBalanceListTest.java +++ b/src/test/java/io/api/etherscan/account/AccountBalanceListTest.java @@ -30,6 +30,8 @@ public void correct() { assertNotNull(balances); assertFalse(balances.isEmpty()); assertEquals(2, balances.size()); + assertNotEquals(balances.get(0), balances.get(1)); + assertNotEquals(balances.get(0).hashCode(), balances.get(1).hashCode()); for(Balance balance : balances) { assertNotNull(balance.getAddress()); assertNotNull(balance.getGwei()); diff --git a/src/test/java/io/api/etherscan/account/AccountMinedBlocksTest.java b/src/test/java/io/api/etherscan/account/AccountMinedBlocksTest.java index 40bcfc4..35a0b9c 100644 --- a/src/test/java/io/api/etherscan/account/AccountMinedBlocksTest.java +++ b/src/test/java/io/api/etherscan/account/AccountMinedBlocksTest.java @@ -69,9 +69,10 @@ public void correct() { assertEquals(blocksMined, blocks.size()); assertBlocks(blocks); - Block block = new Block(); - assertFalse(blocks.get(0).equals(block)); - assertNotNull(block.toString()); + assertNotNull(blocks.get(0).toString()); + + assertNotEquals(blocks.get(0), blocks.get(1)); + assertNotEquals(blocks.get(0).hashCode(), blocks.get(1).hashCode()); } @Test(expected = InvalidAddressException.class) diff --git a/src/test/java/io/api/etherscan/account/AccountTokenBalanceTest.java b/src/test/java/io/api/etherscan/account/AccountTokenBalanceTest.java index a1499ec..cf75287 100644 --- a/src/test/java/io/api/etherscan/account/AccountTokenBalanceTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTokenBalanceTest.java @@ -94,12 +94,12 @@ public void correct() { assertNotNull(balance.toString()); TokenBalance balance1 = new TokenBalance("", BigInteger.ONE, ""); - assertFalse(balance.equals(balance1)); - assertFalse(balance.hashCode() == balance1.hashCode()); + assertNotEquals(balance, balance1); + assertNotEquals(balance.hashCode(), balance1.hashCode()); TokenBalance balance2 = new TokenBalance("125161", balance.getWei(), balance.getContract()); - assertFalse(balance.equals(balance2)); - assertFalse(balance.hashCode() == balance2.hashCode()); + assertNotEquals(balance, balance2); + assertNotEquals(balance.hashCode(), balance2.hashCode()); } @Test(expected = InvalidAddressException.class) diff --git a/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java b/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java index 18aac45..a8d895b 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java @@ -88,6 +88,9 @@ public void correct() { assertNotEquals(-1, txs.get(0).getTraceId()); assertTrue(BasicUtils.isEmpty(txs.get(0).getErrCode())); assertNotNull(txs.get(0).toString()); + + assertNotEquals(txs.get(0), txs.get(1)); + assertNotEquals(txs.get(0).hashCode(), txs.get(1).hashCode()); } @Test(expected = InvalidTxHashException.class) diff --git a/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java b/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java index 3df0c4b..e4a8904 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java @@ -25,6 +25,8 @@ public void correct() { assertEquals(8, txs.size()); assertTxs(txs); assertNotNull(txs.get(0).toString()); + assertNotEquals(txs.get(0), txs.get(1)); + assertNotEquals(txs.get(0).hashCode(), txs.get(1).hashCode()); } @Test diff --git a/src/test/java/io/api/etherscan/account/AccountTxsTest.java b/src/test/java/io/api/etherscan/account/AccountTxsTest.java index c7d4ff9..7e9e05b 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxsTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTxsTest.java @@ -35,8 +35,9 @@ public void correct() { assertNotNull(txs.get(0).getValue()); assertNotNull(txs.get(0).getContractAddress()); assertNotNull(txs.get(0).getInput()); - assertNotEquals(txs.get(0).hashCode(), txs.get(1).hashCode()); assertNotNull(txs.get(0).toString()); + assertNotEquals(txs.get(0), txs.get(1)); + assertNotEquals(txs.get(0).hashCode(), txs.get(1).hashCode()); } @Test diff --git a/src/test/java/io/api/etherscan/block/BlockApiTest.java b/src/test/java/io/api/etherscan/block/BlockApiTest.java index 71cdec8..52ad5f9 100644 --- a/src/test/java/io/api/etherscan/block/BlockApiTest.java +++ b/src/test/java/io/api/etherscan/block/BlockApiTest.java @@ -30,10 +30,9 @@ public void correct() { assertNotNull(uncles.get().getUncles().get(0).getMiner()); assertNotNull(uncles.get().toString()); - assertNotEquals(0, uncles.get().hashCode()); - UncleBlock empty = new UncleBlock(); - assertFalse(uncles.get().equals(empty)); + assertNotEquals(empty.hashCode(), uncles.get().hashCode()); + assertNotEquals(uncles, empty); assertTrue(empty.isEmpty()); } diff --git a/src/test/java/io/api/etherscan/contract/ContractApiTest.java b/src/test/java/io/api/etherscan/contract/ContractApiTest.java index 96c992e..7ecf26f 100644 --- a/src/test/java/io/api/etherscan/contract/ContractApiTest.java +++ b/src/test/java/io/api/etherscan/contract/ContractApiTest.java @@ -24,11 +24,15 @@ public void correct() { assertTrue(abi.haveAbi()); assertNotNull(abi.getContractAbi()); assertNotNull(abi.toString()); + + Abi empty = Abi.verified("asg"); + assertNotEquals(empty, abi); + assertNotEquals(empty.hashCode(), abi.hashCode()); } @Test(expected = InvalidAddressException.class) public void invalidParamWithError() { - Abi abi = api.contract().contractAbi("0xBBbc244D798123fDe783fCc1C72d3Bb8C189413"); + api.contract().contractAbi("0xBBbc244D798123fDe783fCc1C72d3Bb8C189413"); } @Test diff --git a/src/test/java/io/api/etherscan/logs/LogQueryBuilderTest.java b/src/test/java/io/api/etherscan/logs/LogQueryBuilderTest.java index 5dd4ddf..1bba08d 100644 --- a/src/test/java/io/api/etherscan/logs/LogQueryBuilderTest.java +++ b/src/test/java/io/api/etherscan/logs/LogQueryBuilderTest.java @@ -28,22 +28,16 @@ public void singleCorrect() { @Test(expected = InvalidAddressException.class) public void singleInCorrectAddress() { - LogQuery single = LogQueryBuilder.with("033990122638b9132ca29c723bdf037f1a891a70c") + LogQueryBuilder.with("033990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") .build(); - - assertNotNull(single); - assertNotNull(single.getParams()); } @Test(expected = LogQueryException.class) public void singleInCorrectTopic() { - LogQuery single = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") + LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("6516=") .build(); - - assertNotNull(single); - assertNotNull(single.getParams()); } @Test @@ -60,14 +54,11 @@ public void tupleCorrect() { @Test(expected = LogQueryException.class) public void tupleInCorrectOp() { - LogQuery tuple = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224) + LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224) .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000") .setOpTopic0_1(null) .build(); - - assertNotNull(tuple); - assertNotNull(tuple.getParams()); } @Test @@ -87,7 +78,7 @@ public void tripleCorrect() { @Test(expected = LogQueryException.class) public void tripleInCorrectOp() { - LogQuery triple = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) + LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "0x72657075746174696f6e00000000000000000000000000000000000000000000") @@ -95,14 +86,11 @@ public void tripleInCorrectOp() { .setOpTopic0_2(null) .setOpTopic1_2(LogOp.AND) .build(); - - assertNotNull(triple); - assertNotNull(triple.getParams()); } @Test(expected = LogQueryException.class) public void tripleInCorrectTopic1() { - LogQuery triple = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) + LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) .topic(null, "0x72657075746174696f6e00000000000000000000000000000000000000000000", "0x72657075746174696f6e00000000000000000000000000000000000000000000") @@ -110,14 +98,11 @@ public void tripleInCorrectTopic1() { .setOpTopic0_2(null) .setOpTopic1_2(LogOp.AND) .build(); - - assertNotNull(triple); - assertNotNull(triple.getParams()); } @Test(expected = LogQueryException.class) public void tripleInCorrectTopic2() { - LogQuery triple = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) + LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "null", "0x72657075746174696f6e00000000000000000000000000000000000000000000") @@ -125,14 +110,11 @@ public void tripleInCorrectTopic2() { .setOpTopic0_2(null) .setOpTopic1_2(LogOp.AND) .build(); - - assertNotNull(triple); - assertNotNull(triple.getParams()); } @Test(expected = LogQueryException.class) public void tripleInCorrectTopic3() { - LogQuery triple = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) + LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "null") @@ -140,9 +122,6 @@ public void tripleInCorrectTopic3() { .setOpTopic0_2(null) .setOpTopic1_2(LogOp.AND) .build(); - - assertNotNull(triple); - assertNotNull(triple.getParams()); } @Test @@ -166,7 +145,7 @@ public void quadroCorrect() { @Test(expected = LogQueryException.class) public void quadroIncorrectTopic2() { - LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") + LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", null, "0x72657075746174696f6e00000000000000000000000000000000000000000000", @@ -178,38 +157,29 @@ public void quadroIncorrectTopic2() { .setOpTopic1_3(LogOp.OR) .setOpTopic2_3(LogOp.OR) .build(); - - assertNotNull(quadro); - assertNotNull(quadro.getParams()); } @Test(expected = LogQueryException.class) public void tupleIncorrectTopic2() { - LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") + LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", null) .setOpTopic0_1(LogOp.AND) .build(); - - assertNotNull(quadro); - assertNotNull(quadro.getParams()); } @Test(expected = LogQueryException.class) public void tupleIncorrectTopic1() { - LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") + LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic(null, "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") .setOpTopic0_1(LogOp.AND) .build(); - - assertNotNull(quadro); - assertNotNull(quadro.getParams()); } @Test(expected = LogQueryException.class) public void quadroIncorrectOp1() { - LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") + LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", null, "0x72657075746174696f6e00000000000000000000000000000000000000000000", @@ -221,14 +191,11 @@ public void quadroIncorrectOp1() { .setOpTopic1_3(LogOp.OR) .setOpTopic2_3(LogOp.OR) .build(); - - assertNotNull(quadro); - assertNotNull(quadro.getParams()); } @Test(expected = LogQueryException.class) public void quadroIncorrectOp2() { - LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") + LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", null, "0x72657075746174696f6e00000000000000000000000000000000000000000000", @@ -240,14 +207,11 @@ public void quadroIncorrectOp2() { .setOpTopic1_3(LogOp.OR) .setOpTopic2_3(LogOp.OR) .build(); - - assertNotNull(quadro); - assertNotNull(quadro.getParams()); } @Test(expected = LogQueryException.class) public void quadroIncorrectOp3() { - LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") + LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", null, "0x72657075746174696f6e00000000000000000000000000000000000000000000", @@ -259,14 +223,11 @@ public void quadroIncorrectOp3() { .setOpTopic1_3(LogOp.OR) .setOpTopic2_3(LogOp.OR) .build(); - - assertNotNull(quadro); - assertNotNull(quadro.getParams()); } @Test(expected = LogQueryException.class) public void quadroInCorrectAgainTopic() { - LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") + LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "0x72657075746174696f6e00000000000000000000000000000000000000000000", @@ -278,14 +239,11 @@ public void quadroInCorrectAgainTopic() { .setOpTopic1_3(LogOp.OR) .setOpTopic2_3(LogOp.OR) .build(); - - assertNotNull(quadro); - assertNotNull(quadro.getParams()); } @Test(expected = LogQueryException.class) public void quadroInCorrectOp4() { - LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") + LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "", @@ -297,14 +255,11 @@ public void quadroInCorrectOp4() { .setOpTopic1_3(LogOp.OR) .setOpTopic2_3(LogOp.OR) .build(); - - assertNotNull(quadro); - assertNotNull(quadro.getParams()); } @Test(expected = LogQueryException.class) public void quadroInCorrectOp5() { - LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") + LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "", @@ -316,14 +271,11 @@ public void quadroInCorrectOp5() { .setOpTopic1_3(null) .setOpTopic2_3(LogOp.OR) .build(); - - assertNotNull(quadro); - assertNotNull(quadro.getParams()); } @Test(expected = LogQueryException.class) public void quadroInCorrectOp6() { - LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") + LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "", @@ -335,14 +287,11 @@ public void quadroInCorrectOp6() { .setOpTopic1_3(LogOp.OR) .setOpTopic2_3(null) .build(); - - assertNotNull(quadro); - assertNotNull(quadro.getParams()); } @Test(expected = LogQueryException.class) public void quadroInCorrectTopic() { - LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") + LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "", @@ -354,8 +303,5 @@ public void quadroInCorrectTopic() { .setOpTopic1_3(LogOp.OR) .setOpTopic2_3(LogOp.OR) .build(); - - assertNotNull(quadro); - assertNotNull(quadro.getParams()); } } diff --git a/src/test/java/io/api/etherscan/logs/LogsApiTest.java b/src/test/java/io/api/etherscan/logs/LogsApiTest.java index 65a1d49..d562321 100644 --- a/src/test/java/io/api/etherscan/logs/LogsApiTest.java +++ b/src/test/java/io/api/etherscan/logs/LogsApiTest.java @@ -56,11 +56,11 @@ public static Collection data() { .setOpTopic0_1(LogOp.OR) .build(); - return Arrays.asList(new Object[][] { - { single, 423}, - { singleInvalidAddr, 0}, - { tupleAnd, 1}, - { tupleOr, 425} + return Arrays.asList(new Object[][]{ + {single, 423}, + {singleInvalidAddr, 0}, + {tupleAnd, 1}, + {tupleOr, 425} }); } @@ -68,7 +68,13 @@ public static Collection data() { public void validateQuery() { List logs = api.logs().logs(query); assertEquals(logsSize, logs.size()); - if(logsSize > 0) { + + if (logsSize > 0) { + if(logsSize > 1) { + assertNotEquals(logs.get(0), logs.get(1)); + assertNotEquals(logs.get(0).hashCode(), logs.get(1).hashCode()); + } + assertNotNull(logs.get(0).getAddress()); assertNotNull(logs.get(0).getBlockNumber()); assertNotNull(logs.get(0).getData()); diff --git a/src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java index 181714b..43b4851 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java @@ -44,6 +44,10 @@ public void correct() { assertNotNull(proxy.getReceiptsRoot()); assertNotNull(proxy.getUncles()); assertNotNull(proxy.toString()); + + BlockProxy empty = new BlockProxy(); + assertNotEquals(proxy, empty); + assertNotEquals(proxy.hashCode(), empty.hashCode()); } @Test diff --git a/src/test/java/io/api/etherscan/proxy/ProxyTxApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyTxApiTest.java index 8edc183..c7f8920 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyTxApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyTxApiTest.java @@ -28,6 +28,10 @@ public void correctByHash() { assertNotNull(tx.get().getHash()); assertNotNull(tx.get().getNonce()); assertNotNull(tx.get().toString()); + + TxProxy empty = new TxProxy(); + assertNotEquals(tx.get(), empty); + assertNotEquals(tx.get().hashCode(), empty.hashCode()); } @Test diff --git a/src/test/java/io/api/etherscan/proxy/ProxyTxReceiptApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyTxReceiptApiTest.java index addb774..a530bdb 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyTxReceiptApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyTxReceiptApiTest.java @@ -36,6 +36,10 @@ public void correct() { assertNotNull(infoProxy.get().getLogsBloom()); assertNull(infoProxy.get().getContractAddress()); assertNotNull(infoProxy.get().toString()); + + ReceiptProxy empty = new ReceiptProxy(); + assertNotEquals(empty, infoProxy.get()); + assertNotEquals(empty.hashCode(), infoProxy.get().hashCode()); } @Test(expected = InvalidTxHashException.class) diff --git a/src/test/java/io/api/etherscan/statistic/StatisticPriceApiTest.java b/src/test/java/io/api/etherscan/statistic/StatisticPriceApiTest.java index 7581f1d..f92755e 100644 --- a/src/test/java/io/api/etherscan/statistic/StatisticPriceApiTest.java +++ b/src/test/java/io/api/etherscan/statistic/StatisticPriceApiTest.java @@ -24,5 +24,9 @@ public void correct() { assertNotEquals(0, price.inBtc()); assertNotEquals(0, price.inUsd()); assertNotNull(price.toString()); + + Price empty = new Price(); + assertNotEquals(price, empty); + assertNotEquals(price.hashCode(), empty.hashCode()); } } diff --git a/src/test/java/io/api/etherscan/statistic/StatisticSupplyApiTest.java b/src/test/java/io/api/etherscan/statistic/StatisticSupplyApiTest.java index 8b7e10a..4d1eecb 100644 --- a/src/test/java/io/api/etherscan/statistic/StatisticSupplyApiTest.java +++ b/src/test/java/io/api/etherscan/statistic/StatisticSupplyApiTest.java @@ -5,6 +5,8 @@ import org.junit.Assert; import org.junit.Test; +import java.math.BigInteger; + /** * ! NO DESCRIPTION ! * @@ -25,5 +27,9 @@ public void correct() { assertNotNull(supply.asMwei()); assertNotNull(supply.asEther()); assertNotNull(supply.toString()); + + Supply empty = new Supply(BigInteger.ONE); + assertNotEquals(supply, empty); + assertNotEquals(supply.hashCode(), empty.hashCode()); } } diff --git a/src/test/java/io/api/etherscan/transaction/TransactionExecApiTest.java b/src/test/java/io/api/etherscan/transaction/TransactionExecApiTest.java index 9742c3c..2f36d79 100644 --- a/src/test/java/io/api/etherscan/transaction/TransactionExecApiTest.java +++ b/src/test/java/io/api/etherscan/transaction/TransactionExecApiTest.java @@ -25,11 +25,15 @@ public void correct() { assertTrue(status.get().haveError()); assertNotNull(status.get().getErrDescription()); assertNotNull(status.get().toString()); + + Status empty = new Status(); + assertNotEquals(empty, status.get()); + assertNotEquals(empty.hashCode(), status.get().hashCode()); } @Test(expected = InvalidTxHashException.class) public void invalidParamWithError() { - Optional status = api.txs().execStatus("0xb513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b"); + api.txs().execStatus("0xb513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b"); } @Test diff --git a/src/test/java/io/api/etherscan/transaction/TransactionReceiptApiTest.java b/src/test/java/io/api/etherscan/transaction/TransactionReceiptApiTest.java index 2ec3485..c716c8a 100644 --- a/src/test/java/io/api/etherscan/transaction/TransactionReceiptApiTest.java +++ b/src/test/java/io/api/etherscan/transaction/TransactionReceiptApiTest.java @@ -26,7 +26,7 @@ public void correct() { @Test(expected = InvalidTxHashException.class) public void invalidParamWithError() { - Optional status = api.txs().receiptStatus("0x13c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76"); + api.txs().receiptStatus("0x13c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76"); } @Test diff --git a/src/test/java/io/api/util/UtilTests.java b/src/test/java/io/api/util/UtilTests.java index f19e188..870e7a2 100644 --- a/src/test/java/io/api/util/UtilTests.java +++ b/src/test/java/io/api/util/UtilTests.java @@ -5,7 +5,6 @@ import io.api.etherscan.error.NoResponseException; import io.api.etherscan.error.ParseException; import io.api.etherscan.model.utility.StringResponseTO; -import io.api.etherscan.util.BasicUtils; import org.junit.Assert; import org.junit.Test; @@ -62,7 +61,8 @@ public void isEmptyCollectionNull() { @Test public void isEmptyCollectionEmpty() { - boolean result = isEmpty(new ArrayList<>()); + ArrayList list = new ArrayList<>(); + boolean result = isEmpty(list); assertTrue(result); } @@ -78,6 +78,18 @@ public void isNotHexNull() { assertTrue(result); } + @Test + public void isNotAddressInvalid() { + boolean result = isNotAddress("125125"); + assertTrue(result); + } + + @Test + public void isNotHexInvalid() { + boolean result = isNotHex("1215%"); + assertTrue(result); + } + @Test(expected = EtherScanException.class) public void isResponseStatusInvalidThrows() { StringResponseTO responseTO = new StringResponseTO(); @@ -86,7 +98,8 @@ public void isResponseStatusInvalidThrows() { @Test(expected = EtherScanException.class) public void isResponseNullThrows() { - validateTxResponse(null); + StringResponseTO responseTO = null; + validateTxResponse(responseTO); } @Test(expected = ParseException.class) From ea81c28308e2e4a758e43dc27053213e218c2305 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Mon, 10 Dec 2018 23:50:11 +0300 Subject: [PATCH 004/112] This codecov coverage.. It kills me. --- .../etherscan/account/AccountTxTokenTest.java | 16 ++-- .../io/api/etherscan/block/BlockApiTest.java | 24 ++--- .../etherscan/logs/LogQueryBuilderTest.java | 96 ++++++++++++++----- .../api/etherscan/proxy/ProxyTxApiTest.java | 1 + src/test/java/io/api/util/UtilTests.java | 5 - 5 files changed, 96 insertions(+), 46 deletions(-) diff --git a/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java b/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java index e4a8904..74d6e76 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java @@ -20,10 +20,12 @@ public class AccountTxTokenTest extends Assert { @Test public void correct() { - List txs = api.account().txsToken("0x36ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); + List txs = api.account().txsToken("0x05fBf1E3f105df6a4553f3C7f2ed93070A4BAB46"); assertNotNull(txs); - assertEquals(8, txs.size()); + assertEquals(106, txs.size()); assertTxs(txs); + assertNotEquals(0, txs.get(0).getGasPrice()); + assertNotEquals(-1, txs.get(0).getNonce()); assertNotNull(txs.get(0).toString()); assertNotEquals(txs.get(0), txs.get(1)); assertNotEquals(txs.get(0).hashCode(), txs.get(1).hashCode()); @@ -33,7 +35,7 @@ public void correct() { public void correctStartBlock() { List txs = api.account().txsToken("0x36ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7", 5578167); assertNotNull(txs); - assertEquals(6, txs.size()); + assertEquals(9, txs.size()); assertTxs(txs); } @@ -47,7 +49,7 @@ public void correctStartBlockEndBlock() { @Test(expected = InvalidAddressException.class) public void invalidParamWithError() { - List txs = api.account().txsToken("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); + api.account().txsToken("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); } @Test @@ -66,10 +68,10 @@ private void assertTxs(List txs) { assertNotNull(tx.getTo()); assertNotNull(tx.getTimeStamp()); assertNotNull(tx.getTokenDecimal()); - assertNotEquals(0,(tx.getConfirmations())); + assertNotEquals(-1,(tx.getConfirmations())); assertNotNull(tx.getGasUsed()); - assertNotEquals(0,(tx.getCumulativeGasUsed())); - assertNotEquals(0, tx.getTransactionIndex()); + assertNotEquals(-1 ,tx.getCumulativeGasUsed()); + assertNotEquals(-1, tx.getTransactionIndex()); } } } diff --git a/src/test/java/io/api/etherscan/block/BlockApiTest.java b/src/test/java/io/api/etherscan/block/BlockApiTest.java index 52ad5f9..2500d3b 100644 --- a/src/test/java/io/api/etherscan/block/BlockApiTest.java +++ b/src/test/java/io/api/etherscan/block/BlockApiTest.java @@ -19,20 +19,20 @@ public class BlockApiTest extends Assert { @Test public void correct() { - Optional uncles = api.block().uncles(2165403); - assertTrue(uncles.isPresent()); - assertFalse(uncles.get().isEmpty()); - assertNotNull(uncles.get().getBlockMiner()); - assertNotNull(uncles.get().getUncleInclusionReward()); - assertNotNull(uncles.get().getUncles()); - assertFalse(uncles.get().getUncles().isEmpty()); - assertNotNull(uncles.get().getUncles().get(0).getBlockreward()); - assertNotNull(uncles.get().getUncles().get(0).getMiner()); - assertNotNull(uncles.get().toString()); + Optional uncle = api.block().uncles(2165403); + assertTrue(uncle.isPresent()); + assertFalse(uncle.get().isEmpty()); + assertNotNull(uncle.get().getBlockMiner()); + assertNotNull(uncle.get().getUncleInclusionReward()); + assertNotNull(uncle.get().getUncles()); + assertFalse(uncle.get().getUncles().isEmpty()); + assertNotNull(uncle.get().getUncles().get(0).getBlockreward()); + assertNotNull(uncle.get().getUncles().get(0).getMiner()); + assertNotNull(uncle.get().toString()); UncleBlock empty = new UncleBlock(); - assertNotEquals(empty.hashCode(), uncles.get().hashCode()); - assertNotEquals(uncles, empty); + assertNotEquals(uncle.get().hashCode(), empty.hashCode()); + assertNotEquals(uncle.get(), empty); assertTrue(empty.isEmpty()); } diff --git a/src/test/java/io/api/etherscan/logs/LogQueryBuilderTest.java b/src/test/java/io/api/etherscan/logs/LogQueryBuilderTest.java index 1bba08d..17b3fd4 100644 --- a/src/test/java/io/api/etherscan/logs/LogQueryBuilderTest.java +++ b/src/test/java/io/api/etherscan/logs/LogQueryBuilderTest.java @@ -61,6 +61,24 @@ public void tupleInCorrectOp() { .build(); } + @Test(expected = LogQueryException.class) + public void tupleIncorrectTopic2() { + LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") + .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + null) + .setOpTopic0_1(LogOp.AND) + .build(); + } + + @Test(expected = LogQueryException.class) + public void tupleIncorrectTopic1() { + LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") + .topic(null, + "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") + .setOpTopic0_1(LogOp.AND) + .build(); + } + @Test public void tripleCorrect() { LogQuery triple = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) @@ -104,7 +122,7 @@ public void tripleInCorrectTopic1() { public void tripleInCorrectTopic2() { LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - "null", + null, "0x72657075746174696f6e00000000000000000000000000000000000000000000") .setOpTopic0_1(LogOp.AND) .setOpTopic0_2(null) @@ -117,13 +135,49 @@ public void tripleInCorrectTopic3() { LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", - "null") + null) + .setOpTopic0_1(LogOp.AND) + .setOpTopic0_2(null) + .setOpTopic1_2(LogOp.AND) + .build(); + } + + @Test(expected = LogQueryException.class) + public void tripleInCorrectOp1() { + LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) + .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0x72657075746174696f6e00000000000000000000000000000000000000000000", + "0x72657075746174696f6e00000000000000000000000000000000000000000000") + .setOpTopic0_1(null) + .setOpTopic0_2(LogOp.AND) + .setOpTopic1_2(LogOp.AND) + .build(); + } + + @Test(expected = LogQueryException.class) + public void tripleInCorrectOp2() { + 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 tripleInCorrectOp3() { + LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) + .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0x72657075746174696f6e00000000000000000000000000000000000000000000", + "0x72657075746174696f6e00000000000000000000000000000000000000000000") + .setOpTopic0_1(LogOp.AND) + .setOpTopic0_2(LogOp.AND) + .setOpTopic1_2(null) + .build(); + } + @Test public void quadroCorrect() { LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") @@ -160,20 +214,18 @@ public void quadroIncorrectTopic2() { } @Test(expected = LogQueryException.class) - public void tupleIncorrectTopic2() { - LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - null) - .setOpTopic0_1(LogOp.AND) - .build(); - } - - @Test(expected = LogQueryException.class) - public void tupleIncorrectTopic1() { + public void quadroIncorrectTopic1() { LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic(null, - "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") + "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0x72657075746174696f6e00000000000000000000000000000000000000000000", + "0x72657075746174696f6e00000000000000000000000000000000000000000000") .setOpTopic0_1(LogOp.AND) + .setOpTopic0_2(LogOp.OR) + .setOpTopic0_3(LogOp.AND) + .setOpTopic1_2(LogOp.OR) + .setOpTopic1_3(LogOp.OR) + .setOpTopic2_3(LogOp.OR) .build(); } @@ -181,7 +233,7 @@ public void tupleIncorrectTopic1() { public void quadroIncorrectOp1() { LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - null, + "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "0x72657075746174696f6e00000000000000000000000000000000000000000000") .setOpTopic0_1(null) @@ -197,7 +249,7 @@ public void quadroIncorrectOp1() { public void quadroIncorrectOp2() { LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - null, + "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "0x72657075746174696f6e00000000000000000000000000000000000000000000") .setOpTopic0_1(LogOp.AND) @@ -213,7 +265,7 @@ public void quadroIncorrectOp2() { public void quadroIncorrectOp3() { LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - null, + "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "0x72657075746174696f6e00000000000000000000000000000000000000000000") .setOpTopic0_1(LogOp.AND) @@ -246,8 +298,8 @@ public void quadroInCorrectOp4() { LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", - "", - "") + "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") .setOpTopic0_1(LogOp.AND) .setOpTopic0_2(LogOp.OR) .setOpTopic0_3(LogOp.AND) @@ -262,8 +314,8 @@ public void quadroInCorrectOp5() { LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", - "", - "") + "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") .setOpTopic0_1(LogOp.AND) .setOpTopic0_2(LogOp.OR) .setOpTopic0_3(LogOp.AND) @@ -278,8 +330,8 @@ public void quadroInCorrectOp6() { LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", - "", - "") + "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") .setOpTopic0_1(LogOp.AND) .setOpTopic0_2(LogOp.OR) .setOpTopic0_3(LogOp.AND) diff --git a/src/test/java/io/api/etherscan/proxy/ProxyTxApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyTxApiTest.java index c7f8920..1a36372 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyTxApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyTxApiTest.java @@ -27,6 +27,7 @@ public void correctByHash() { assertNotNull(tx.get().getTo()); assertNotNull(tx.get().getHash()); assertNotNull(tx.get().getNonce()); + assertNotNull(tx.get().getBlockNumber()); assertNotNull(tx.get().toString()); TxProxy empty = new TxProxy(); diff --git a/src/test/java/io/api/util/UtilTests.java b/src/test/java/io/api/util/UtilTests.java index 870e7a2..a8874ae 100644 --- a/src/test/java/io/api/util/UtilTests.java +++ b/src/test/java/io/api/util/UtilTests.java @@ -28,11 +28,6 @@ public void responseValidateEmpty() { validateTxResponse(responseTO); } - @Test(expected = EtherScanException.class) - public void responseValidateNullable() { - validateTxResponse(null); - } - @Test public void partitionEmpty() { ArrayList list = new ArrayList<>(); From 4cd9af62ec879e0377490ea57e09419e5ace0765 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Tue, 11 Dec 2018 23:53:34 +0300 Subject: [PATCH 005/112] More more more codecov coverage improvements --- .../io/api/etherscan/core/impl/BasicProvider.java | 4 ++-- .../api/etherscan/core/impl/ProxyApiProvider.java | 12 +++--------- .../api/etherscan/error/NoResponseException.java | 14 -------------- .../java/io/api/etherscan/EtherScanApiTest.java | 2 +- .../etherscan/account/AccountBalanceListTest.java | 8 ++++++++ .../etherscan/account/AccountTokenBalanceTest.java | 5 ----- src/test/java/io/api/util/UtilTests.java | 6 ------ 7 files changed, 14 insertions(+), 37 deletions(-) delete mode 100644 src/main/java/io/api/etherscan/error/NoResponseException.java 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 8ab61a6..5b95f68 100644 --- a/src/main/java/io/api/etherscan/core/impl/BasicProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/BasicProvider.java @@ -1,7 +1,7 @@ package io.api.etherscan.core.impl; import com.google.gson.Gson; -import io.api.etherscan.error.NoResponseException; +import io.api.etherscan.error.EtherScanException; import io.api.etherscan.error.ParseException; import io.api.etherscan.executor.IHttpExecutor; import io.api.etherscan.manager.IQueueManager; @@ -51,7 +51,7 @@ String getRequest(final String urlParameters) { final String url = baseUrl + module + urlParameters; final String result = executor.get(url); if (BasicUtils.isEmpty(result)) - throw new NoResponseException("Server returned null value for GET request at URL - " + url); + throw new EtherScanException("Server returned null value for GET request at URL - " + url); return result; } 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 640bcad..b28eccd 100644 --- a/src/main/java/io/api/etherscan/core/impl/ProxyApiProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/ProxyApiProvider.java @@ -145,9 +145,7 @@ public Optional txSendRaw(final String hexEncodedTx) throws ApiException throw new EtherScanException("Error occurred with code " + response.getError().getCode() + " with message " + response.getError().getMessage()); - return (BasicUtils.isEmpty(response.getResult())) - ? Optional.empty() - : Optional.of(response.getResult()); + return Optional.ofNullable(response.getResult()); } @NotNull @@ -169,9 +167,7 @@ public Optional call(final String address, final String data) throws Api final String urlParams = ACT_CALL_PARAM + TO_PARAM + address + DATA_PARAM + data + TAG_LAST_PARAM; final StringProxyTO response = getRequest(urlParams, StringProxyTO.class); - return (BasicUtils.isEmpty(response.getResult())) - ? Optional.empty() - : Optional.of(response.getResult()); + return Optional.ofNullable (response.getResult()); } @NotNull @@ -181,9 +177,7 @@ public Optional code(final String address) throws ApiException { final String urlParams = ACT_CODE_PARAM + ADDRESS_PARAM + address + TAG_LAST_PARAM; final StringProxyTO response = getRequest(urlParams, StringProxyTO.class); - return (BasicUtils.isEmpty(response.getResult())) - ? Optional.empty() - : Optional.of(response.getResult()); + return Optional.ofNullable(response.getResult()); } @NotNull diff --git a/src/main/java/io/api/etherscan/error/NoResponseException.java b/src/main/java/io/api/etherscan/error/NoResponseException.java deleted file mode 100644 index 737be73..0000000 --- a/src/main/java/io/api/etherscan/error/NoResponseException.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.api.etherscan.error; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 03.11.2018 - */ -public class NoResponseException extends ApiException { - - public NoResponseException(String message) { - super(message); - } -} diff --git a/src/test/java/io/api/etherscan/EtherScanApiTest.java b/src/test/java/io/api/etherscan/EtherScanApiTest.java index b7fab99..9458276 100644 --- a/src/test/java/io/api/etherscan/EtherScanApiTest.java +++ b/src/test/java/io/api/etherscan/EtherScanApiTest.java @@ -34,7 +34,7 @@ public void validKey() { @Test(expected = ApiKeyException.class) public void emptyKey() { - new EtherScanApi("", network); + new EtherScanApi(""); } @Test(expected = ApiKeyException.class) diff --git a/src/test/java/io/api/etherscan/account/AccountBalanceListTest.java b/src/test/java/io/api/etherscan/account/AccountBalanceListTest.java index 9992962..f5a1ebd 100644 --- a/src/test/java/io/api/etherscan/account/AccountBalanceListTest.java +++ b/src/test/java/io/api/etherscan/account/AccountBalanceListTest.java @@ -71,7 +71,15 @@ public void invalidParamWithError() { addresses.add("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9"); addresses.add("C9F32CE1127e44C51cbD182D6364F3D707Fd0d47"); + api.account().balances(addresses); + } + + @Test + public void emptyParamList() { + List addresses = new ArrayList<>(); List balances = api.account().balances(addresses); + assertNotNull(balances); + assertTrue(balances.isEmpty()); } @Test diff --git a/src/test/java/io/api/etherscan/account/AccountTokenBalanceTest.java b/src/test/java/io/api/etherscan/account/AccountTokenBalanceTest.java index cf75287..2c747a0 100644 --- a/src/test/java/io/api/etherscan/account/AccountTokenBalanceTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTokenBalanceTest.java @@ -11,7 +11,6 @@ import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; -import java.math.BigInteger; import java.util.Arrays; import java.util.Collection; @@ -93,10 +92,6 @@ public void correct() { assertNotEquals(0, balance.getWei()); assertNotNull(balance.toString()); - TokenBalance balance1 = new TokenBalance("", BigInteger.ONE, ""); - assertNotEquals(balance, balance1); - assertNotEquals(balance.hashCode(), balance1.hashCode()); - TokenBalance balance2 = new TokenBalance("125161", balance.getWei(), balance.getContract()); assertNotEquals(balance, balance2); assertNotEquals(balance.hashCode(), balance2.hashCode()); diff --git a/src/test/java/io/api/util/UtilTests.java b/src/test/java/io/api/util/UtilTests.java index a8874ae..53939cc 100644 --- a/src/test/java/io/api/util/UtilTests.java +++ b/src/test/java/io/api/util/UtilTests.java @@ -2,7 +2,6 @@ import com.google.gson.Gson; import io.api.etherscan.error.EtherScanException; -import io.api.etherscan.error.NoResponseException; import io.api.etherscan.error.ParseException; import io.api.etherscan.model.utility.StringResponseTO; import org.junit.Assert; @@ -101,9 +100,4 @@ public void isResponseNullThrows() { public void isThrowParseException() { throw new ParseException("Test", null); } - - @Test(expected = NoResponseException.class) - public void isThrowNoResponseException() { - throw new NoResponseException("Test"); - } } From 76fe4791be22421275c97b03a13dd7737cdc16b9 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Tue, 5 Mar 2019 15:32:30 +0300 Subject: [PATCH 006/112] Gorli & TOBALABA networks support 1.0.1 release --- README.md | 14 ++++-- pom.xml | 2 +- .../api/etherscan/core/impl/EtherScanApi.java | 4 +- .../java/io/api/etherscan/model/BaseTx.java | 26 +++++++++- .../io/api/etherscan/model/EthNetwork.java | 2 + src/main/java/io/api/etherscan/model/Log.java | 49 +++++++++++++++++++ .../java/io/api/etherscan/model/Price.java | 33 +++++++++++++ .../java/io/api/etherscan/model/Status.java | 18 +++++++ src/main/java/io/api/etherscan/model/Tx.java | 33 +++++++++++++ .../io/api/etherscan/model/TxInternal.java | 24 +++++++++ .../java/io/api/etherscan/model/TxToken.java | 34 +++++++++++++ .../java/io/api/etherscan/model/Uncle.java | 20 ++++++++ .../io/api/etherscan/EtherScanApiTest.java | 16 ++++++ .../account/AccountMinedBlocksTest.java | 6 ++- .../account/AccountTxInternalByHashTest.java | 6 ++- .../etherscan/account/AccountTxTokenTest.java | 2 +- 16 files changed, 276 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index b288b45..a65cedd 100644 --- a/README.md +++ b/README.md @@ -14,14 +14,14 @@ Library supports all available EtherScan *API* calls for all available *Ethereum com.github.goodforgod java-etherscan-api - 1.0.0 + 1.0.1 ``` **Gradle** ```groovy dependencies { - compile 'com.github.goodforgod:java-etherscan-api:1.0.0' + compile 'com.github.goodforgod:java-etherscan-api:1.0.1' } ``` @@ -40,8 +40,12 @@ dependencies { - [Version History](#version-history) ## Mainnet and Testnets -API support Ethereum: *[MAINNET](https://etherscan.io), [ROPSTEN](https://ropsten.etherscan.io), -[KOVAN](https://kovan.etherscan.io), [RINKEBY](https://rinkeby.etherscan.io)* networks. +API support Ethereum: *[MAINNET](https://etherscan.io), + [ROPSTEN](https://ropsten.etherscan.io), + [KOVAN](https://kovan.etherscan.io), + [RINKEBY](https://rinkeby.etherscan.io), + [GORLI](https://goerli.etherscan.io), + [TOBALABA](https://tobalaba.etherscan.com)* networks. ```java EtherScanApi api = new EtherScanApi(EthNetwork.MAINNET); // Default EtherScanApi apiRinkeby = new EtherScanApi(EthNetwork.RINKEBY); @@ -161,6 +165,8 @@ Token API methods migrated to [Account](#account-api) & [Stats](#stats-api) resp ## Version History +**1.0.1** - Gorli & TOBALABA networks support. + **1.0.0** - Initial project with all API functionality, for all available networks, with tests coverage for all cases. ## License diff --git a/pom.xml b/pom.xml index 20315d9..7795cce 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.github.goodforgod java-etherscan-api - 1.0.0 + 1.0.1 jar ${project.groupId}:${project.artifactId} 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 87899f1..587c2a9 100644 --- a/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java +++ b/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java @@ -70,7 +70,9 @@ public EtherScanApi(final String apiKey, : new QueueManager(5, 1); final IHttpExecutor executor = executorSupplier.get(); - final String baseUrl = "https://" + network.getDomain() + ".etherscan.io/api" + "?apikey=" + apiKey; + + final String ending = (EthNetwork.TOBALABA.equals(network)) ? "com" : "io"; + final String baseUrl = "https://" + network.getDomain() + ".etherscan." + ending + "/api" + "?apikey=" + apiKey; this.account = new AccountApiProvider(masterQueue, baseUrl, executor); this.block = new BlockApiProvider(masterQueue, baseUrl, executor); diff --git a/src/main/java/io/api/etherscan/model/BaseTx.java b/src/main/java/io/api/etherscan/model/BaseTx.java index 8dd709a..85b8f83 100644 --- a/src/main/java/io/api/etherscan/model/BaseTx.java +++ b/src/main/java/io/api/etherscan/model/BaseTx.java @@ -77,12 +77,34 @@ public boolean equals(Object o) { BaseTx baseTx = (BaseTx) o; - return hash != null ? hash.equals(baseTx.hash) : baseTx.hash == null; + if (blockNumber != baseTx.blockNumber) return false; + if (timeStamp != null ? !timeStamp.equals(baseTx.timeStamp) : baseTx.timeStamp != null) return false; + if (_timeStamp != null ? !_timeStamp.equals(baseTx._timeStamp) : baseTx._timeStamp != null) return false; + if (hash != null ? !hash.equals(baseTx.hash) : baseTx.hash != null) return false; + if (from != null ? !from.equals(baseTx.from) : baseTx.from != null) return false; + if (to != null ? !to.equals(baseTx.to) : baseTx.to != null) return false; + if (value != null ? !value.equals(baseTx.value) : baseTx.value != null) return false; + if (contractAddress != null ? !contractAddress.equals(baseTx.contractAddress) : baseTx.contractAddress != null) + return false; + if (input != null ? !input.equals(baseTx.input) : baseTx.input != null) return false; + if (gas != null ? !gas.equals(baseTx.gas) : baseTx.gas != null) return false; + return gasUsed != null ? gasUsed.equals(baseTx.gasUsed) : baseTx.gasUsed == null; } @Override public int hashCode() { - return hash != null ? hash.hashCode() : 0; + int result = (int) (blockNumber ^ (blockNumber >>> 32)); + result = 31 * result + (timeStamp != null ? timeStamp.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); + result = 31 * result + (contractAddress != null ? contractAddress.hashCode() : 0); + result = 31 * result + (input != null ? input.hashCode() : 0); + result = 31 * result + (gas != null ? gas.hashCode() : 0); + result = 31 * result + (gasUsed != null ? gasUsed.hashCode() : 0); + return result; } @Override diff --git a/src/main/java/io/api/etherscan/model/EthNetwork.java b/src/main/java/io/api/etherscan/model/EthNetwork.java index 54fef96..d10aead 100644 --- a/src/main/java/io/api/etherscan/model/EthNetwork.java +++ b/src/main/java/io/api/etherscan/model/EthNetwork.java @@ -10,6 +10,8 @@ public enum EthNetwork { MAINNET("api"), ROPSTEN("api-ropsten"), KOVAN("api-kovan"), + TOBALABA("api-tobalaba"), + GORLI("api-goerli"), RINKEBY("api-rinkeby"); private final String domain; diff --git a/src/main/java/io/api/etherscan/model/Log.java b/src/main/java/io/api/etherscan/model/Log.java index d39cc61..85479d9 100644 --- a/src/main/java/io/api/etherscan/model/Log.java +++ b/src/main/java/io/api/etherscan/model/Log.java @@ -98,6 +98,55 @@ public Long getLogIndex() { } // + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Log log = (Log) o; + + if (blockNumber != null ? !blockNumber.equals(log.blockNumber) : log.blockNumber != null) return false; + if (_blockNumber != null ? !_blockNumber.equals(log._blockNumber) : log._blockNumber != null) return false; + if (address != null ? !address.equals(log.address) : log.address != null) return false; + if (transactionHash != null ? !transactionHash.equals(log.transactionHash) : log.transactionHash != null) + return false; + if (transactionIndex != null ? !transactionIndex.equals(log.transactionIndex) : log.transactionIndex != null) + return false; + if (_transactionIndex != null ? !_transactionIndex.equals(log._transactionIndex) : log._transactionIndex != null) + return false; + if (timeStamp != null ? !timeStamp.equals(log.timeStamp) : log.timeStamp != null) return false; + if (_timeStamp != null ? !_timeStamp.equals(log._timeStamp) : log._timeStamp != null) return false; + if (data != null ? !data.equals(log.data) : log.data != null) return false; + if (gasPrice != null ? !gasPrice.equals(log.gasPrice) : log.gasPrice != null) return false; + if (_gasPrice != null ? !_gasPrice.equals(log._gasPrice) : log._gasPrice != null) return false; + if (gasUsed != null ? !gasUsed.equals(log.gasUsed) : log.gasUsed != null) return false; + if (_gasUsed != null ? !_gasUsed.equals(log._gasUsed) : log._gasUsed != null) return false; + if (topics != null ? !topics.equals(log.topics) : log.topics != null) return false; + if (logIndex != null ? !logIndex.equals(log.logIndex) : log.logIndex != null) return false; + return _logIndex != null ? _logIndex.equals(log._logIndex) : log._logIndex == null; + } + + @Override + public int hashCode() { + int result = blockNumber != null ? blockNumber.hashCode() : 0; + result = 31 * 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 + (transactionIndex != null ? transactionIndex.hashCode() : 0); + result = 31 * result + (_transactionIndex != null ? _transactionIndex.hashCode() : 0); + result = 31 * result + (timeStamp != null ? timeStamp.hashCode() : 0); + result = 31 * result + (_timeStamp != null ? _timeStamp.hashCode() : 0); + result = 31 * result + (data != null ? data.hashCode() : 0); + result = 31 * result + (gasPrice != null ? gasPrice.hashCode() : 0); + result = 31 * result + (_gasPrice != null ? _gasPrice.hashCode() : 0); + result = 31 * result + (gasUsed != null ? gasUsed.hashCode() : 0); + result = 31 * result + (_gasUsed != null ? _gasUsed.hashCode() : 0); + result = 31 * result + (topics != null ? topics.hashCode() : 0); + result = 31 * result + (logIndex != null ? logIndex.hashCode() : 0); + result = 31 * result + (_logIndex != null ? _logIndex.hashCode() : 0); + return result; + } + @Override public String toString() { return "Log{" + diff --git a/src/main/java/io/api/etherscan/model/Price.java b/src/main/java/io/api/etherscan/model/Price.java index fec7846..dcb9c4c 100644 --- a/src/main/java/io/api/etherscan/model/Price.java +++ b/src/main/java/io/api/etherscan/model/Price.java @@ -38,6 +38,39 @@ public LocalDateTime btcTimestamp() { return _ethbtc_timestamp; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Price price = (Price) o; + + if (Double.compare(price.ethusd, ethusd) != 0) return false; + if (Double.compare(price.ethbtc, ethbtc) != 0) return false; + if (ethusd_timestamp != null ? !ethusd_timestamp.equals(price.ethusd_timestamp) : price.ethusd_timestamp != null) + return false; + if (ethbtc_timestamp != null ? !ethbtc_timestamp.equals(price.ethbtc_timestamp) : price.ethbtc_timestamp != null) + return false; + if (_ethusd_timestamp != null ? !_ethusd_timestamp.equals(price._ethusd_timestamp) : price._ethusd_timestamp != null) + return false; + return _ethbtc_timestamp != null ? _ethbtc_timestamp.equals(price._ethbtc_timestamp) : price._ethbtc_timestamp == null; + } + + @Override + public int hashCode() { + int result; + long temp; + temp = Double.doubleToLongBits(ethusd); + result = (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(ethbtc); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + result = 31 * result + (ethusd_timestamp != null ? ethusd_timestamp.hashCode() : 0); + result = 31 * result + (ethbtc_timestamp != null ? ethbtc_timestamp.hashCode() : 0); + result = 31 * result + (_ethusd_timestamp != null ? _ethusd_timestamp.hashCode() : 0); + result = 31 * result + (_ethbtc_timestamp != null ? _ethbtc_timestamp.hashCode() : 0); + return result; + } + @Override public String toString() { return "Price{" + diff --git a/src/main/java/io/api/etherscan/model/Status.java b/src/main/java/io/api/etherscan/model/Status.java index 7e05f85..71a4ebb 100644 --- a/src/main/java/io/api/etherscan/model/Status.java +++ b/src/main/java/io/api/etherscan/model/Status.java @@ -22,6 +22,24 @@ public String getErrDescription() { return errDescription; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Status status = (Status) o; + + if (isError != status.isError) return false; + return errDescription != null ? errDescription.equals(status.errDescription) : status.errDescription == null; + } + + @Override + public int hashCode() { + int result = isError; + result = 31 * result + (errDescription != null ? errDescription.hashCode() : 0); + return result; + } + @Override public String toString() { return "Status{" + diff --git a/src/main/java/io/api/etherscan/model/Tx.java b/src/main/java/io/api/etherscan/model/Tx.java index e940896..9bd2faa 100644 --- a/src/main/java/io/api/etherscan/model/Tx.java +++ b/src/main/java/io/api/etherscan/model/Tx.java @@ -55,6 +55,39 @@ public long getConfirmations() { } // + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + + Tx tx = (Tx) o; + + if (nonce != tx.nonce) return false; + if (transactionIndex != tx.transactionIndex) return false; + if (confirmations != tx.confirmations) return false; + if (blockHash != null ? !blockHash.equals(tx.blockHash) : tx.blockHash != null) return false; + if (gasPrice != null ? !gasPrice.equals(tx.gasPrice) : tx.gasPrice != null) return false; + if (cumulativeGasUsed != null ? !cumulativeGasUsed.equals(tx.cumulativeGasUsed) : tx.cumulativeGasUsed != null) + return false; + if (isError != null ? !isError.equals(tx.isError) : tx.isError != null) return false; + return txreceipt_status != null ? txreceipt_status.equals(tx.txreceipt_status) : tx.txreceipt_status == null; + } + + @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 + (gasPrice != null ? gasPrice.hashCode() : 0); + result = 31 * result + (cumulativeGasUsed != null ? cumulativeGasUsed.hashCode() : 0); + result = 31 * result + (int) (confirmations ^ (confirmations >>> 32)); + result = 31 * result + (isError != null ? isError.hashCode() : 0); + result = 31 * result + (txreceipt_status != null ? txreceipt_status.hashCode() : 0); + return result; + } + @Override public String toString() { return "Tx{" + diff --git a/src/main/java/io/api/etherscan/model/TxInternal.java b/src/main/java/io/api/etherscan/model/TxInternal.java index 024b4bb..f505fd1 100644 --- a/src/main/java/io/api/etherscan/model/TxInternal.java +++ b/src/main/java/io/api/etherscan/model/TxInternal.java @@ -31,6 +31,30 @@ public String getErrCode() { } // + @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; + + TxInternal that = (TxInternal) o; + + if (traceId != that.traceId) return false; + if (isError != that.isError) return false; + if (type != null ? !type.equals(that.type) : that.type != null) return false; + return errCode != null ? errCode.equals(that.errCode) : that.errCode == null; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (type != null ? type.hashCode() : 0); + result = 31 * result + (int) (traceId ^ (traceId >>> 32)); + result = 31 * result + isError; + result = 31 * result + (errCode != null ? errCode.hashCode() : 0); + return result; + } + @Override public String toString() { return "TxInternal{" + diff --git a/src/main/java/io/api/etherscan/model/TxToken.java b/src/main/java/io/api/etherscan/model/TxToken.java index 466083a..0046e02 100644 --- a/src/main/java/io/api/etherscan/model/TxToken.java +++ b/src/main/java/io/api/etherscan/model/TxToken.java @@ -56,6 +56,40 @@ public long getConfirmations() { } // + @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; + + TxToken txToken = (TxToken) o; + + if (nonce != txToken.nonce) return false; + if (transactionIndex != txToken.transactionIndex) return false; + if (gasPrice != txToken.gasPrice) return false; + if (cumulativeGasUsed != txToken.cumulativeGasUsed) return false; + if (confirmations != txToken.confirmations) return false; + if (blockHash != null ? !blockHash.equals(txToken.blockHash) : txToken.blockHash != null) return false; + if (tokenName != null ? !tokenName.equals(txToken.tokenName) : txToken.tokenName != null) return false; + if (tokenSymbol != null ? !tokenSymbol.equals(txToken.tokenSymbol) : txToken.tokenSymbol != null) return false; + return tokenDecimal != null ? tokenDecimal.equals(txToken.tokenDecimal) : txToken.tokenDecimal == null; + } + + @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 + (tokenName != null ? tokenName.hashCode() : 0); + result = 31 * result + (tokenSymbol != null ? tokenSymbol.hashCode() : 0); + result = 31 * result + (tokenDecimal != null ? tokenDecimal.hashCode() : 0); + result = 31 * result + transactionIndex; + result = 31 * result + (int) (gasPrice ^ (gasPrice >>> 32)); + result = 31 * result + (int) (cumulativeGasUsed ^ (cumulativeGasUsed >>> 32)); + result = 31 * result + (int) (confirmations ^ (confirmations >>> 32)); + return result; + } + @Override public String toString() { return "TxToken{" + diff --git a/src/main/java/io/api/etherscan/model/Uncle.java b/src/main/java/io/api/etherscan/model/Uncle.java index e6e5eb2..bf5145d 100644 --- a/src/main/java/io/api/etherscan/model/Uncle.java +++ b/src/main/java/io/api/etherscan/model/Uncle.java @@ -28,6 +28,26 @@ public int getUnclePosition() { } // + @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{" + diff --git a/src/test/java/io/api/etherscan/EtherScanApiTest.java b/src/test/java/io/api/etherscan/EtherScanApiTest.java index 9458276..2d0d978 100644 --- a/src/test/java/io/api/etherscan/EtherScanApiTest.java +++ b/src/test/java/io/api/etherscan/EtherScanApiTest.java @@ -56,6 +56,22 @@ public void noTimeoutOnRead() { assertNotNull(balance); } + @Test + public void noTimeoutOnReadGroli() { + Supplier supplier = () -> new HttpExecutor(300); + EtherScanApi api = new EtherScanApi(EthNetwork.GORLI, supplier); + Balance balance = api.account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); + assertNotNull(balance); + } + + @Test + public void noTimeoutOnReadTobalala() { + Supplier supplier = () -> new HttpExecutor(30000); + EtherScanApi api = new EtherScanApi(EthNetwork.TOBALABA, supplier); + Balance balance = api.account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); + assertNotNull(balance); + } + @Test public void noTimeoutUnlimitedAwait() { Supplier supplier = () -> new HttpExecutor(-30, -300); diff --git a/src/test/java/io/api/etherscan/account/AccountMinedBlocksTest.java b/src/test/java/io/api/etherscan/account/AccountMinedBlocksTest.java index 35a0b9c..5f44b68 100644 --- a/src/test/java/io/api/etherscan/account/AccountMinedBlocksTest.java +++ b/src/test/java/io/api/etherscan/account/AccountMinedBlocksTest.java @@ -71,8 +71,10 @@ public void correct() { assertBlocks(blocks); assertNotNull(blocks.get(0).toString()); - assertNotEquals(blocks.get(0), blocks.get(1)); - assertNotEquals(blocks.get(0).hashCode(), blocks.get(1).hashCode()); + if(blocks.size() > 1) { + assertNotEquals(blocks.get(0), blocks.get(1)); + assertNotEquals(blocks.get(0).hashCode(), blocks.get(1).hashCode()); + } } @Test(expected = InvalidAddressException.class) diff --git a/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java b/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java index a8d895b..4727317 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java @@ -89,8 +89,10 @@ public void correct() { assertTrue(BasicUtils.isEmpty(txs.get(0).getErrCode())); assertNotNull(txs.get(0).toString()); - assertNotEquals(txs.get(0), txs.get(1)); - assertNotEquals(txs.get(0).hashCode(), txs.get(1).hashCode()); + if(txs.size() > 9) { + assertNotEquals(txs.get(0), txs.get(9)); + assertNotEquals(txs.get(0).hashCode(), txs.get(9).hashCode()); + } } @Test(expected = InvalidTxHashException.class) diff --git a/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java b/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java index 74d6e76..52fae6e 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java @@ -35,7 +35,7 @@ public void correct() { public void correctStartBlock() { List txs = api.account().txsToken("0x36ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7", 5578167); assertNotNull(txs); - assertEquals(9, txs.size()); + assertEquals(11, txs.size()); assertTxs(txs); } From 0848007d891df89842299cda0816e8f8baba090f Mon Sep 17 00:00:00 2001 From: GoodforGod Date: Sat, 9 Mar 2019 17:37:21 +0300 Subject: [PATCH 007/112] pom html javadoc specified --- pom.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7795cce..b219976 100644 --- a/pom.xml +++ b/pom.xml @@ -53,7 +53,7 @@ 3.0.2 1.6 3.0.1 - 2.10.4 + 3.0.1 1.6.8 2.7 @@ -149,6 +149,9 @@ org.apache.maven.plugins maven-javadoc-plugin ${maven-javadoc-plugin-version} + + -html5 + attach-javadocs From 348ee668a3f444049352e5c54100c14721499df2 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sat, 9 Mar 2019 18:15:11 +0300 Subject: [PATCH 008/112] Javadoc fixes --- pom.xml | 8 ++-- .../io/api/etherscan/core/IAccountApi.java | 41 +++++++++++++++---- .../java/io/api/etherscan/core/IBlockApi.java | 6 ++- .../io/api/etherscan/core/IContractApi.java | 6 ++- .../java/io/api/etherscan/core/ILogsApi.java | 5 ++- .../java/io/api/etherscan/core/IProxyApi.java | 29 +++++++++++++ .../io/api/etherscan/core/IStatisticApi.java | 16 ++++++-- .../api/etherscan/core/ITransactionApi.java | 13 ++++-- .../api/etherscan/core/impl/EtherScanApi.java | 21 ---------- .../api/etherscan/manager/IQueueManager.java | 1 + .../java/io/api/manager/QueueManagerTest.java | 6 +-- 11 files changed, 108 insertions(+), 44 deletions(-) diff --git a/pom.xml b/pom.xml index b219976..149beba 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.github.goodforgod java-etherscan-api - 1.0.1 + 1.0.1-SNAPSHOT jar ${project.groupId}:${project.artifactId} @@ -149,9 +149,6 @@ org.apache.maven.plugins maven-javadoc-plugin ${maven-javadoc-plugin-version} - - -html5 - attach-javadocs @@ -160,6 +157,9 @@ + + -html5 + diff --git a/src/main/java/io/api/etherscan/core/IAccountApi.java b/src/main/java/io/api/etherscan/core/IAccountApi.java index 1033aaf..c86feb1 100644 --- a/src/main/java/io/api/etherscan/core/IAccountApi.java +++ b/src/main/java/io/api/etherscan/core/IAccountApi.java @@ -15,34 +15,61 @@ */ public interface IAccountApi { - /** Address ETH balance */ + /** + * Address ETH balance + * @param address to look for + * @return balance + */ @NotNull Balance balance(String address) throws ApiException; - /** ERC20 token balance for address */ + /** + * ERC20 token balance for address + * @param address to look for + * @param contract for token + * @return token balance + */ @NotNull TokenBalance balance(String address, String contract) throws ApiException; /** * Maximum 20 address for single batch request - * If address > 20, then there will be more than 1 request performed + * If addresses more than 20, then there will be MORE than 1 request performed + * @param addresses to look for + * @return balance[0] for address[0], etc */ @NotNull List balances(List addresses) throws ApiException; - /** All txs */ + /** + * Find all txs + * @param address to look for txs + * @return tx info + */ @NotNull List txs(String address) throws ApiException; @NotNull List txs(String address, long startBlock) throws ApiException; @NotNull List txs(String address, long startBlock, long endBlock) throws ApiException; - /** All internal txs */ + /** + * All internal txs + * @param address to look for + * @return internal tx + */ @NotNull List txsInternal(String address) throws ApiException; @NotNull List txsInternal(String address, long startBlock) throws ApiException; @NotNull List txsInternal(String address, long startBlock, long endBlock) throws ApiException; @NotNull List txsInternalByHash(String txhash); - /** All token txs */ + /** + * All token txs + * @param address to look for + * @return token txs + */ @NotNull List txsToken(String address) throws ApiException; @NotNull List txsToken(String address, long startBlock) throws ApiException; @NotNull List txsToken(String address, long startBlock, long endBlock) throws ApiException; - /** All blocks mined by address */ + /** + * All blocks mined by address + * @param address to look for + * @return mined blocks + */ @NotNull List minedBlocks(String address) throws ApiException; } diff --git a/src/main/java/io/api/etherscan/core/IBlockApi.java b/src/main/java/io/api/etherscan/core/IBlockApi.java index 61bc7b5..f248a98 100644 --- a/src/main/java/io/api/etherscan/core/IBlockApi.java +++ b/src/main/java/io/api/etherscan/core/IBlockApi.java @@ -15,6 +15,10 @@ */ public interface IBlockApi { - /** Return uncle blocks */ + /** + * Return uncle blocks + * @param blockNumber block number + * @return uncle block + */ @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 index 8a0a972..c9fdfe2 100644 --- a/src/main/java/io/api/etherscan/core/IContractApi.java +++ b/src/main/java/io/api/etherscan/core/IContractApi.java @@ -13,6 +13,10 @@ */ public interface IContractApi { - /** Get Verified Contract Sources */ + /** + * Get Verified Contract Sources + * @param address to look for + * @return abi for contract + */ @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 index e4d9636..80f5813 100644 --- a/src/main/java/io/api/etherscan/core/ILogsApi.java +++ b/src/main/java/io/api/etherscan/core/ILogsApi.java @@ -17,8 +17,11 @@ public interface ILogsApi { /** - * alternative to the native eth_getLogs + * Alternative to the native eth_getLogs * Read at EtherScan API description for full info! + * @see LogQuery + * @param query for logs (check etherscan specs) + * @return logs */ @NotNull List logs(LogQuery query) throws ApiException; } diff --git a/src/main/java/io/api/etherscan/core/IProxyApi.java b/src/main/java/io/api/etherscan/core/IProxyApi.java index 3416797..b9ad863 100644 --- a/src/main/java/io/api/etherscan/core/IProxyApi.java +++ b/src/main/java/io/api/etherscan/core/IProxyApi.java @@ -21,66 +21,90 @@ public interface IProxyApi { /** * Returns the number of most recent block * eth_blockNumber + * @return last block number */ long blockNoLast(); /** * Returns information about a block by block number * eth_getBlockByNumber + * @param blockNo block number + * @return block info */ @NotNull Optional block(long blockNo) throws ApiException; /** * Returns information about a uncle by block number * eth_getUncleByBlockNumberAndIndex + * @param blockNo block number + * @param index uncle block index + * @return block info */ @NotNull Optional blockUncle(long blockNo, long index) throws ApiException; /** * Returns the information about a transaction requested by transaction hash * eth_getTransactionByHash + * @param txhash tx hash + * @return tx info */ @NotNull Optional tx(String txhash) throws ApiException; /** * Returns information about a transaction by block number and transaction index position * eth_getTransactionByBlockNumberAndIndex + * @param blockNo block number + * @param index tx index in block + * @return tx info */ @NotNull Optional tx(long blockNo, long index) throws ApiException; /** * Returns the number of transactions in a block from a block matching the given block number * eth_getBlockTransactionCountByNumber + * @param blockNo block number + * @return tx count in block */ int txCount(long blockNo) throws ApiException; /** * Returns the number of transactions sent from an address * eth_getTransactionCount + * @param address to look for + * @return tx send count */ int txSendCount(String address) throws ApiException; /** * Creates new message call transaction or a contract creation for signed transactions * eth_sendRawTransaction + * @param hexEncodedTx tx as hex + * @return result (check eth grpc info) */ @NotNull Optional txSendRaw(String hexEncodedTx) throws ApiException; /** * Returns the receipt of a transaction by transaction hash * eth_getTransactionReceipt + * @param txhash tx hash + * @return receipt */ @NotNull Optional txReceipt(String txhash) throws ApiException; /** * Executes a new message call immediately without creating a transaction on the block chain * eth_call + * @param address to look for + * @param data in tx for call + * @return result (check eth grpc info) */ @NotNull Optional call(String address, String data) throws ApiException; /** * Returns code at a given address * eth_getCode + * @param address to look for + * @return result (check eth grpc info) */ @NotNull Optional code(String address) throws ApiException; @@ -88,12 +112,16 @@ public interface IProxyApi { * (**experimental) * Returns the value from a storage position at a given address * eth_getStorageAt + * @param address to look for + * @param position storage position + * @return result (check eth grpc info) */ @NotNull Optional storageAt(String address, long position) throws ApiException; /** * Returns the current price per gas in wei * eth_gasPrice + * @return price */ @NotNull BigInteger gasPrice() throws ApiException; @@ -101,6 +129,7 @@ public interface IProxyApi { * Makes a call or transaction, which won't be added to the blockchain and returns the used gas, * which can be used for estimating the used gas * eth_estimateGas + * @return gas estimate */ @NotNull BigInteger gasEstimated() throws ApiException; @NotNull BigInteger gasEstimated(String hexData) throws ApiException; diff --git a/src/main/java/io/api/etherscan/core/IStatisticApi.java b/src/main/java/io/api/etherscan/core/IStatisticApi.java index 03e1704..718ffed 100644 --- a/src/main/java/io/api/etherscan/core/IStatisticApi.java +++ b/src/main/java/io/api/etherscan/core/IStatisticApi.java @@ -16,12 +16,22 @@ */ public interface IStatisticApi { - /** ERC20 token total Supply */ + /** + * ERC20 token total Supply + * @param contract to look for + * @return token supply + */ @NotNull BigInteger supply(String contract) throws ApiException; - /** Eth total supply */ + /** + * Eth total supply + * @return ETH supply + */ @NotNull Supply supply() throws ApiException; - /** Eth last USD and BTC price */ + /** + * Eth last USD and BTC price + * @return last price + */ @NotNull Price lastPrice() throws ApiException; } diff --git a/src/main/java/io/api/etherscan/core/ITransactionApi.java b/src/main/java/io/api/etherscan/core/ITransactionApi.java index 8b9047a..4a591ea 100644 --- a/src/main/java/io/api/etherscan/core/ITransactionApi.java +++ b/src/main/java/io/api/etherscan/core/ITransactionApi.java @@ -15,12 +15,19 @@ */ public interface ITransactionApi { - /** Check Contract Execution Status (if there was an error during contract execution) */ + /** + * Check Contract Execution Status (if there was an error during contract execution) + * @param txhash to look for + * @return exec status + */ @NotNull Optional execStatus(String txhash) throws ApiException; - /** Check Transaction Receipt Status (Only applicable for Post Byzantium fork transactions) + /** + * Check Transaction Receipt Status (Only applicable for Post Byzantium fork transactions) * 0 = Fail, 1 = Pass * empty value for pre-byzantium fork - * */ + * @param txhash to look for + * @return status as boolean + */ @NotNull Optional receiptStatus(String txhash) 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 587c2a9..1f4d0b4 100644 --- a/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java +++ b/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java @@ -83,57 +83,36 @@ public EtherScanApi(final String apiKey, this.txs = new TransactionApiProvider(masterQueue, baseUrl, executor); } - /** - * API for interactions with account and address - */ @NotNull public IAccountApi account() { return account; } - /** - * API for verifying contract ABI - */ @NotNull public IContractApi contract() { return contract; } - /** - * [BETA] API for interaction with tx statuses - */ @NotNull public ITransactionApi txs() { return txs; } - /** - * [BETA] API for getting block rewards and uncles - */ @NotNull public IBlockApi block() { return block; } - /** - * [BETA] API for interaction with eth_getLogs - */ @NotNull public ILogsApi logs() { return logs; } - /** - * API for interacting with geth/proxy etherscan - */ @NotNull public IProxyApi proxy() { return proxy; } - /** - * API for eth price and supply statistic - */ @NotNull public IStatisticApi stats() { return stats; diff --git a/src/main/java/io/api/etherscan/manager/IQueueManager.java b/src/main/java/io/api/etherscan/manager/IQueueManager.java index a54a255..00745ff 100644 --- a/src/main/java/io/api/etherscan/manager/IQueueManager.java +++ b/src/main/java/io/api/etherscan/manager/IQueueManager.java @@ -12,6 +12,7 @@ public interface IQueueManager { /** * Waits in queue for chance to take turn + * @return is turn available */ boolean takeTurn(); } diff --git a/src/test/java/io/api/manager/QueueManagerTest.java b/src/test/java/io/api/manager/QueueManagerTest.java index 41d7d03..61d83cb 100644 --- a/src/test/java/io/api/manager/QueueManagerTest.java +++ b/src/test/java/io/api/manager/QueueManagerTest.java @@ -43,9 +43,9 @@ public void queueManagerWithDelay() { public void queueManagerTimeout() { IQueueManager queueManager = new QueueManager(1, 3); assertTrue(queueManager.takeTurn()); - long start = System.currentTimeMillis(); + final long start = System.currentTimeMillis(); queueManager.takeTurn(); - long end = System.currentTimeMillis(); - assertEquals(3, (end - start) / 1000); + final long end = System.currentTimeMillis(); + assertEquals(3, (end - start) / 950); } } From 9a34b3ebb2a1fb65a071f9c034cc5937835eea50 Mon Sep 17 00:00:00 2001 From: GoodforGod Date: Sat, 9 Mar 2019 18:29:16 +0300 Subject: [PATCH 009/112] 1.0.1 release --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 149beba..3c6548c 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.github.goodforgod java-etherscan-api - 1.0.1-SNAPSHOT + 1.0.1 jar ${project.groupId}:${project.artifactId} From 97197ad214f2f78c2c84800af2d162b9916b16b3 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sat, 9 Mar 2019 18:54:04 +0300 Subject: [PATCH 010/112] Release 1.0.1 --- src/test/java/io/api/util/BasicUtilsTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/io/api/util/BasicUtilsTests.java b/src/test/java/io/api/util/BasicUtilsTests.java index 53939cc..1b1753e 100644 --- a/src/test/java/io/api/util/BasicUtilsTests.java +++ b/src/test/java/io/api/util/BasicUtilsTests.java @@ -18,7 +18,7 @@ * @author GoodforGod * @since 13.11.2018 */ -public class UtilTests extends Assert { +public class BasicUtilsTests extends Assert { @Test(expected = EtherScanException.class) public void responseValidateEmpty() { From 83284bad88838837175f66e8495948ad7b9e614f Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sat, 9 Mar 2019 19:17:17 +0300 Subject: [PATCH 011/112] Minor models fixes --- pom.xml | 2 +- .../java/io/api/etherscan/model/BaseTx.java | 18 +------------ src/main/java/io/api/etherscan/model/Log.java | 26 +------------------ .../java/io/api/etherscan/model/Price.java | 10 +------ src/main/java/io/api/etherscan/model/Tx.java | 13 ++-------- .../io/api/etherscan/model/TxInternal.java | 2 +- .../java/io/api/etherscan/model/TxToken.java | 2 +- .../io/api/etherscan/block/BlockApiTest.java | 1 + 8 files changed, 9 insertions(+), 65 deletions(-) diff --git a/pom.xml b/pom.xml index 3c6548c..a9bcfa8 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.github.goodforgod java-etherscan-api - 1.0.1 + 1.0.2 jar ${project.groupId}:${project.artifactId} diff --git a/src/main/java/io/api/etherscan/model/BaseTx.java b/src/main/java/io/api/etherscan/model/BaseTx.java index 85b8f83..a757ad2 100644 --- a/src/main/java/io/api/etherscan/model/BaseTx.java +++ b/src/main/java/io/api/etherscan/model/BaseTx.java @@ -79,31 +79,16 @@ public boolean equals(Object o) { if (blockNumber != baseTx.blockNumber) return false; if (timeStamp != null ? !timeStamp.equals(baseTx.timeStamp) : baseTx.timeStamp != null) return false; - if (_timeStamp != null ? !_timeStamp.equals(baseTx._timeStamp) : baseTx._timeStamp != null) return false; if (hash != null ? !hash.equals(baseTx.hash) : baseTx.hash != null) return false; - if (from != null ? !from.equals(baseTx.from) : baseTx.from != null) return false; - if (to != null ? !to.equals(baseTx.to) : baseTx.to != null) return false; - if (value != null ? !value.equals(baseTx.value) : baseTx.value != null) return false; - if (contractAddress != null ? !contractAddress.equals(baseTx.contractAddress) : baseTx.contractAddress != null) - return false; - if (input != null ? !input.equals(baseTx.input) : baseTx.input != null) return false; - if (gas != null ? !gas.equals(baseTx.gas) : baseTx.gas != null) return false; - return gasUsed != null ? gasUsed.equals(baseTx.gasUsed) : baseTx.gasUsed == null; + return value != null ? value.equals(baseTx.value) : baseTx.value == null; } @Override public int hashCode() { int result = (int) (blockNumber ^ (blockNumber >>> 32)); result = 31 * result + (timeStamp != null ? timeStamp.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); - result = 31 * result + (contractAddress != null ? contractAddress.hashCode() : 0); - result = 31 * result + (input != null ? input.hashCode() : 0); - result = 31 * result + (gas != null ? gas.hashCode() : 0); - result = 31 * result + (gasUsed != null ? gasUsed.hashCode() : 0); return result; } @@ -112,7 +97,6 @@ public String toString() { return "BaseTx{" + "blockNumber=" + blockNumber + ", timeStamp='" + timeStamp + '\'' + - ", _timeStamp=" + _timeStamp + ", hash='" + hash + '\'' + ", from='" + from + '\'' + ", to='" + to + '\'' + diff --git a/src/main/java/io/api/etherscan/model/Log.java b/src/main/java/io/api/etherscan/model/Log.java index 85479d9..cf485fd 100644 --- a/src/main/java/io/api/etherscan/model/Log.java +++ b/src/main/java/io/api/etherscan/model/Log.java @@ -106,44 +106,20 @@ public boolean equals(Object o) { Log log = (Log) o; if (blockNumber != null ? !blockNumber.equals(log.blockNumber) : log.blockNumber != null) return false; - if (_blockNumber != null ? !_blockNumber.equals(log._blockNumber) : log._blockNumber != null) return false; if (address != null ? !address.equals(log.address) : log.address != null) return false; if (transactionHash != null ? !transactionHash.equals(log.transactionHash) : log.transactionHash != null) return false; - if (transactionIndex != null ? !transactionIndex.equals(log.transactionIndex) : log.transactionIndex != null) - return false; - if (_transactionIndex != null ? !_transactionIndex.equals(log._transactionIndex) : log._transactionIndex != null) - return false; if (timeStamp != null ? !timeStamp.equals(log.timeStamp) : log.timeStamp != null) return false; - if (_timeStamp != null ? !_timeStamp.equals(log._timeStamp) : log._timeStamp != null) return false; - if (data != null ? !data.equals(log.data) : log.data != null) return false; - if (gasPrice != null ? !gasPrice.equals(log.gasPrice) : log.gasPrice != null) return false; - if (_gasPrice != null ? !_gasPrice.equals(log._gasPrice) : log._gasPrice != null) return false; - if (gasUsed != null ? !gasUsed.equals(log.gasUsed) : log.gasUsed != null) return false; - if (_gasUsed != null ? !_gasUsed.equals(log._gasUsed) : log._gasUsed != null) return false; - if (topics != null ? !topics.equals(log.topics) : log.topics != null) return false; - if (logIndex != null ? !logIndex.equals(log.logIndex) : log.logIndex != null) return false; - return _logIndex != null ? _logIndex.equals(log._logIndex) : log._logIndex == null; + return logIndex != null ? logIndex.equals(log.logIndex) : log.logIndex == null; } @Override public int hashCode() { int result = blockNumber != null ? blockNumber.hashCode() : 0; - result = 31 * 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 + (transactionIndex != null ? transactionIndex.hashCode() : 0); - result = 31 * result + (_transactionIndex != null ? _transactionIndex.hashCode() : 0); result = 31 * result + (timeStamp != null ? timeStamp.hashCode() : 0); - result = 31 * result + (_timeStamp != null ? _timeStamp.hashCode() : 0); - result = 31 * result + (data != null ? data.hashCode() : 0); - result = 31 * result + (gasPrice != null ? gasPrice.hashCode() : 0); - result = 31 * result + (_gasPrice != null ? _gasPrice.hashCode() : 0); - result = 31 * result + (gasUsed != null ? gasUsed.hashCode() : 0); - result = 31 * result + (_gasUsed != null ? _gasUsed.hashCode() : 0); - result = 31 * result + (topics != null ? topics.hashCode() : 0); result = 31 * result + (logIndex != null ? logIndex.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 dcb9c4c..f9839e2 100644 --- a/src/main/java/io/api/etherscan/model/Price.java +++ b/src/main/java/io/api/etherscan/model/Price.java @@ -49,11 +49,7 @@ public boolean equals(Object o) { 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; - if (ethbtc_timestamp != null ? !ethbtc_timestamp.equals(price.ethbtc_timestamp) : price.ethbtc_timestamp != null) - 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 (ethbtc_timestamp != null ? !ethbtc_timestamp.equals(price.ethbtc_timestamp) : price.ethbtc_timestamp != null); } @Override @@ -66,8 +62,6 @@ public int hashCode() { 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; } @@ -78,8 +72,6 @@ public String toString() { ", 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/api/etherscan/model/Tx.java b/src/main/java/io/api/etherscan/model/Tx.java index 9bd2faa..68f00f1 100644 --- a/src/main/java/io/api/etherscan/model/Tx.java +++ b/src/main/java/io/api/etherscan/model/Tx.java @@ -65,13 +65,8 @@ public boolean equals(Object o) { if (nonce != tx.nonce) return false; if (transactionIndex != tx.transactionIndex) return false; - if (confirmations != tx.confirmations) return false; if (blockHash != null ? !blockHash.equals(tx.blockHash) : tx.blockHash != null) return false; - if (gasPrice != null ? !gasPrice.equals(tx.gasPrice) : tx.gasPrice != null) return false; - if (cumulativeGasUsed != null ? !cumulativeGasUsed.equals(tx.cumulativeGasUsed) : tx.cumulativeGasUsed != null) - return false; - if (isError != null ? !isError.equals(tx.isError) : tx.isError != null) return false; - return txreceipt_status != null ? txreceipt_status.equals(tx.txreceipt_status) : tx.txreceipt_status == null; + return isError != null ? isError.equals(tx.isError) : tx.isError == null; } @Override @@ -80,11 +75,7 @@ public int hashCode() { result = 31 * result + (int) (nonce ^ (nonce >>> 32)); result = 31 * result + (blockHash != null ? blockHash.hashCode() : 0); result = 31 * result + transactionIndex; - result = 31 * result + (gasPrice != null ? gasPrice.hashCode() : 0); - result = 31 * result + (cumulativeGasUsed != null ? cumulativeGasUsed.hashCode() : 0); - result = 31 * result + (int) (confirmations ^ (confirmations >>> 32)); result = 31 * result + (isError != null ? isError.hashCode() : 0); - result = 31 * result + (txreceipt_status != null ? txreceipt_status.hashCode() : 0); return result; } @@ -99,6 +90,6 @@ public String toString() { ", confirmations=" + confirmations + ", isError='" + isError + '\'' + ", txreceipt_status='" + txreceipt_status + '\'' + - '}'; + "} " + super.toString(); } } diff --git a/src/main/java/io/api/etherscan/model/TxInternal.java b/src/main/java/io/api/etherscan/model/TxInternal.java index f505fd1..693bb40 100644 --- a/src/main/java/io/api/etherscan/model/TxInternal.java +++ b/src/main/java/io/api/etherscan/model/TxInternal.java @@ -62,6 +62,6 @@ public String toString() { ", traceId=" + traceId + ", isError=" + isError + ", errCode='" + errCode + '\'' + - '}'; + "} " + super.toString(); } } diff --git a/src/main/java/io/api/etherscan/model/TxToken.java b/src/main/java/io/api/etherscan/model/TxToken.java index 0046e02..18867fd 100644 --- a/src/main/java/io/api/etherscan/model/TxToken.java +++ b/src/main/java/io/api/etherscan/model/TxToken.java @@ -102,6 +102,6 @@ public String toString() { ", gasPrice=" + gasPrice + ", cumulativeGasUsed=" + cumulativeGasUsed + ", confirmations=" + confirmations + - '}'; + "} " + super.toString(); } } diff --git a/src/test/java/io/api/etherscan/block/BlockApiTest.java b/src/test/java/io/api/etherscan/block/BlockApiTest.java index 2500d3b..c1a4427 100644 --- a/src/test/java/io/api/etherscan/block/BlockApiTest.java +++ b/src/test/java/io/api/etherscan/block/BlockApiTest.java @@ -28,6 +28,7 @@ public void correct() { assertFalse(uncle.get().getUncles().isEmpty()); assertNotNull(uncle.get().getUncles().get(0).getBlockreward()); assertNotNull(uncle.get().getUncles().get(0).getMiner()); + assertNotEquals(-1, uncle.get().getUncles().get(0).getUnclePosition()); assertNotNull(uncle.get().toString()); UncleBlock empty = new UncleBlock(); From 0a27cf62ea2747c700c8e072b79773e3df5ebf31 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Thu, 4 Apr 2019 23:44:19 +0300 Subject: [PATCH 012/112] Test coverage improvements --- .../io/api/etherscan/account/AccountTxTokenTest.java | 4 ++++ .../java/io/api/etherscan/account/AccountTxsTest.java | 1 + .../java/io/api/etherscan/block/BlockApiTest.java | 11 +++++++++++ src/test/java/io/api/etherscan/logs/LogsApiTest.java | 3 +++ 4 files changed, 19 insertions(+) diff --git a/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java b/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java index 52fae6e..411c609 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java @@ -27,8 +27,12 @@ public void correct() { assertNotEquals(0, txs.get(0).getGasPrice()); assertNotEquals(-1, txs.get(0).getNonce()); assertNotNull(txs.get(0).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 diff --git a/src/test/java/io/api/etherscan/account/AccountTxsTest.java b/src/test/java/io/api/etherscan/account/AccountTxsTest.java index 7e9e05b..5c0ad48 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxsTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTxsTest.java @@ -38,6 +38,7 @@ public void correct() { assertNotNull(txs.get(0).toString()); assertNotEquals(txs.get(0), txs.get(1)); assertNotEquals(txs.get(0).hashCode(), txs.get(1).hashCode()); + assertEquals(txs.get(1), txs.get(1)); } @Test diff --git a/src/test/java/io/api/etherscan/block/BlockApiTest.java b/src/test/java/io/api/etherscan/block/BlockApiTest.java index c1a4427..c459b1a 100644 --- a/src/test/java/io/api/etherscan/block/BlockApiTest.java +++ b/src/test/java/io/api/etherscan/block/BlockApiTest.java @@ -35,6 +35,17 @@ public void correct() { assertNotEquals(uncle.get().hashCode(), empty.hashCode()); assertNotEquals(uncle.get(), empty); assertTrue(empty.isEmpty()); + + if(uncle.get().getUncles().size() > 0) { + assertNotEquals(-1, uncle.get().getUncles().get(0).getUnclePosition()); + assertEquals(uncle.get().getUncles().get(0), uncle.get().getUncles().get(0)); + assertEquals(uncle.get().getUncles().get(0).hashCode(), uncle.get().getUncles().get(0).hashCode()); + } + + if(uncle.get().getUncles().size() > 1) { + assertNotEquals(uncle.get().getUncles().get(1), uncle.get().getUncles().get(0)); + assertNotEquals(uncle.get().getUncles().get(1).hashCode(), uncle.get().getUncles().get(0).hashCode()); + } } @Test diff --git a/src/test/java/io/api/etherscan/logs/LogsApiTest.java b/src/test/java/io/api/etherscan/logs/LogsApiTest.java index d562321..4a3f9e1 100644 --- a/src/test/java/io/api/etherscan/logs/LogsApiTest.java +++ b/src/test/java/io/api/etherscan/logs/LogsApiTest.java @@ -86,6 +86,9 @@ public void validateQuery() { assertNotNull(logs.get(0).getLogIndex()); assertNotNull(logs.get(0).getGasPrice()); assertNotNull(logs.get(0).toString()); + + assertEquals(logs.get(0), logs.get(0)); + assertEquals(logs.get(0).hashCode(), logs.get(0).hashCode()); } } } From 87776ca233aadda50df58505fdfe94db52bf13ef Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sat, 13 Apr 2019 12:59:49 +0300 Subject: [PATCH 013/112] HttpExecutor post method improvements --- .../etherscan/executor/impl/HttpExecutor.java | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) 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 6894b0c..3a33515 100644 --- a/src/main/java/io/api/etherscan/executor/impl/HttpExecutor.java +++ b/src/main/java/io/api/etherscan/executor/impl/HttpExecutor.java @@ -6,12 +6,13 @@ import io.api.etherscan.util.BasicUtils; import java.io.BufferedReader; -import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStreamReader; +import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.SocketTimeoutException; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import java.util.zip.GZIPInputStream; @@ -103,14 +104,16 @@ 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.isEmpty(dataToPost)) ? "0" : String.valueOf(dataToPost.length()); - connection.setRequestProperty("content-length", contentLength); + final String contentLength = (BasicUtils.isBlank(dataToPost)) ? "0" : String.valueOf(dataToPost.length()); + connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); + connection.setRequestProperty("Content-Length", contentLength); + connection.setFixedLengthStreamingMode(dataToPost.length()); connection.setDoOutput(true); - DataOutputStream wr = new DataOutputStream(connection.getOutputStream()); - wr.writeBytes(dataToPost); - wr.flush(); - wr.close(); + connection.connect(); + try (OutputStream os = connection.getOutputStream()) { + os.write(dataToPost.getBytes(StandardCharsets.UTF_8)); + } final int status = connection.getResponseCode(); if (status == HTTP_MOVED_TEMP || status == HTTP_MOVED_PERM) { @@ -141,13 +144,13 @@ private String readData(final HttpURLConnection connection) throws IOException { } private InputStreamReader getStreamReader(final HttpURLConnection connection) throws IOException { - final boolean haveEncoding = connection.getContentEncoding() != null; - - if (haveEncoding && "gzip".equals(connection.getContentEncoding())) - return new InputStreamReader(new GZIPInputStream(connection.getInputStream()), "utf-8"); - else if (haveEncoding && "deflate".equals(connection.getContentEncoding())) - return new InputStreamReader(new InflaterInputStream(connection.getInputStream()), "utf-8"); - else - return new InputStreamReader(connection.getInputStream(), "utf-8"); + switch (String.valueOf(connection.getContentEncoding())) { + case "gzip": + return new InputStreamReader(new GZIPInputStream(connection.getInputStream()), "utf-8"); + case "deflate": + return new InputStreamReader(new InflaterInputStream(connection.getInputStream()), "utf-8"); + default: + return new InputStreamReader(connection.getInputStream(), "utf-8"); + } } } From 03f2538f33bcab81fc881fc2007e10a89242dcef Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sat, 13 Apr 2019 13:01:41 +0300 Subject: [PATCH 014/112] Coverage improvements --- .../java/io/api/etherscan/model/TxToken.java | 34 ------------------- .../etherscan/account/AccountTxTokenTest.java | 2 ++ 2 files changed, 2 insertions(+), 34 deletions(-) diff --git a/src/main/java/io/api/etherscan/model/TxToken.java b/src/main/java/io/api/etherscan/model/TxToken.java index 18867fd..985066b 100644 --- a/src/main/java/io/api/etherscan/model/TxToken.java +++ b/src/main/java/io/api/etherscan/model/TxToken.java @@ -56,40 +56,6 @@ public long getConfirmations() { } // - @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; - - TxToken txToken = (TxToken) o; - - if (nonce != txToken.nonce) return false; - if (transactionIndex != txToken.transactionIndex) return false; - if (gasPrice != txToken.gasPrice) return false; - if (cumulativeGasUsed != txToken.cumulativeGasUsed) return false; - if (confirmations != txToken.confirmations) return false; - if (blockHash != null ? !blockHash.equals(txToken.blockHash) : txToken.blockHash != null) return false; - if (tokenName != null ? !tokenName.equals(txToken.tokenName) : txToken.tokenName != null) return false; - if (tokenSymbol != null ? !tokenSymbol.equals(txToken.tokenSymbol) : txToken.tokenSymbol != null) return false; - return tokenDecimal != null ? tokenDecimal.equals(txToken.tokenDecimal) : txToken.tokenDecimal == null; - } - - @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 + (tokenName != null ? tokenName.hashCode() : 0); - result = 31 * result + (tokenSymbol != null ? tokenSymbol.hashCode() : 0); - result = 31 * result + (tokenDecimal != null ? tokenDecimal.hashCode() : 0); - result = 31 * result + transactionIndex; - result = 31 * result + (int) (gasPrice ^ (gasPrice >>> 32)); - result = 31 * result + (int) (cumulativeGasUsed ^ (cumulativeGasUsed >>> 32)); - result = 31 * result + (int) (confirmations ^ (confirmations >>> 32)); - return result; - } - @Override public String toString() { return "TxToken{" + diff --git a/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java b/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java index 411c609..48c08ef 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java @@ -28,6 +28,8 @@ public void correct() { 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()); From 44a5fe4f50776775a2c8da617258ae5017481000 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sat, 13 Apr 2019 13:25:27 +0300 Subject: [PATCH 015/112] 1.0.2 Release --- README.md | 6 ++++-- src/main/java/io/api/etherscan/model/BaseTx.java | 6 +++++- src/main/java/io/api/etherscan/model/TxInternal.java | 6 +----- .../java/io/api/etherscan/account/AccountTxTokenTest.java | 6 +++--- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index a65cedd..f62f766 100644 --- a/README.md +++ b/README.md @@ -14,14 +14,14 @@ Library supports all available EtherScan *API* calls for all available *Ethereum com.github.goodforgod java-etherscan-api - 1.0.1 + 1.0.2 ``` **Gradle** ```groovy dependencies { - compile 'com.github.goodforgod:java-etherscan-api:1.0.1' + compile 'com.github.goodforgod:java-etherscan-api:1.0.2' } ``` @@ -165,6 +165,8 @@ Token API methods migrated to [Account](#account-api) & [Stats](#stats-api) resp ## Version History +**1.0.2** - Minor http client improvements. + **1.0.1** - Gorli & TOBALABA networks support. **1.0.0** - Initial project with all API functionality, for all available networks, with tests coverage for all cases. diff --git a/src/main/java/io/api/etherscan/model/BaseTx.java b/src/main/java/io/api/etherscan/model/BaseTx.java index a757ad2..af2286f 100644 --- a/src/main/java/io/api/etherscan/model/BaseTx.java +++ b/src/main/java/io/api/etherscan/model/BaseTx.java @@ -73,13 +73,15 @@ public BigInteger getGasUsed() { @Override public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (!(o instanceof BaseTx)) return false; BaseTx baseTx = (BaseTx) o; if (blockNumber != baseTx.blockNumber) return false; if (timeStamp != null ? !timeStamp.equals(baseTx.timeStamp) : baseTx.timeStamp != null) return false; if (hash != null ? !hash.equals(baseTx.hash) : baseTx.hash != null) return false; + if (from != null ? !from.equals(baseTx.from) : baseTx.from != null) return false; + if (to != null ? !to.equals(baseTx.to) : baseTx.to != null) return false; return value != null ? value.equals(baseTx.value) : baseTx.value == null; } @@ -88,6 +90,8 @@ 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; } diff --git a/src/main/java/io/api/etherscan/model/TxInternal.java b/src/main/java/io/api/etherscan/model/TxInternal.java index 693bb40..1d9d8a8 100644 --- a/src/main/java/io/api/etherscan/model/TxInternal.java +++ b/src/main/java/io/api/etherscan/model/TxInternal.java @@ -34,23 +34,19 @@ public String getErrCode() { @Override public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (!(o instanceof TxInternal)) return false; if (!super.equals(o)) return false; TxInternal that = (TxInternal) o; if (traceId != that.traceId) return false; - if (isError != that.isError) return false; - if (type != null ? !type.equals(that.type) : that.type != null) return false; return errCode != null ? errCode.equals(that.errCode) : that.errCode == null; } @Override public int hashCode() { int result = super.hashCode(); - result = 31 * result + (type != null ? type.hashCode() : 0); result = 31 * result + (int) (traceId ^ (traceId >>> 32)); - result = 31 * result + isError; result = 31 * result + (errCode != null ? errCode.hashCode() : 0); return result; } diff --git a/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java b/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java index 48c08ef..7bdf2d6 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java @@ -20,14 +20,14 @@ public class AccountTxTokenTest extends Assert { @Test public void correct() { - List txs = api.account().txsToken("0x05fBf1E3f105df6a4553f3C7f2ed93070A4BAB46"); + List txs = api.account().txsToken("0xE376F69ED2218076682e2b3B7b9099eC50aD68c4"); assertNotNull(txs); - assertEquals(106, txs.size()); + assertEquals(3, txs.size()); assertTxs(txs); assertNotEquals(0, txs.get(0).getGasPrice()); assertNotEquals(-1, txs.get(0).getNonce()); - assertNotNull(txs.get(0).toString()); + assertNotNull(txs.get(0).toString()); assertNotEquals(txs.get(0).toString(), txs.get(1).toString()); assertNotEquals(txs.get(0), txs.get(1)); From 00cff5f15bf5604faeb32e28d5333512892b9d66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=B4=80=C9=B4=E1=B4=9B=E1=B4=8F=C9=B4=CA=8F?= Date: Sat, 20 Apr 2019 17:19:11 +0300 Subject: [PATCH 016/112] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 27 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 +++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..e68bb35 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,27 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: GoodforGod + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..21bb70c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: GoodforGod + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From 05bc02028eda65ab50f0e819eaa120a60d1c29d1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Oct 2020 10:49:02 +0000 Subject: [PATCH 017/112] Bump junit from 4.12 to 4.13.1 Bumps [junit](https://github.com/junit-team/junit4) from 4.12 to 4.13.1. - [Release notes](https://github.com/junit-team/junit4/releases) - [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.12.md) - [Commits](https://github.com/junit-team/junit4/compare/r4.12...r4.13.1) Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a9bcfa8..6c51f28 100644 --- a/pom.xml +++ b/pom.xml @@ -57,7 +57,7 @@ 1.6.8 2.7 - 4.12 + 4.13.1 2.8.5 From 63beec482a8f29122da1c8649e3df042a6084066 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 18 Oct 2020 13:30:55 +0300 Subject: [PATCH 018/112] Minor improvements and code style checks --- .../io/api/etherscan/core/impl/EtherScanApi.java | 2 +- .../io/api/etherscan/error/ConnectionException.java | 2 -- .../io/api/etherscan/executor/impl/HttpExecutor.java | 12 +++++------- .../etherscan/account/AccountBalanceListTest.java | 5 +++-- .../io/api/etherscan/account/AccountBalanceTest.java | 3 ++- 5 files changed, 11 insertions(+), 13 deletions(-) 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 1f4d0b4..a3979df 100644 --- a/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java +++ b/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java @@ -65,7 +65,7 @@ public EtherScanApi(final String apiKey, throw new ApiException("Ethereum Network is set to NULL value"); // EtherScan 5request\sec limit support by queue manager - final IQueueManager masterQueue = (apiKey.equals("YourApiKeyToken")) + final IQueueManager masterQueue = "YourApiKeyToken".equals(apiKey) ? new FakeQueueManager() : new QueueManager(5, 1); diff --git a/src/main/java/io/api/etherscan/error/ConnectionException.java b/src/main/java/io/api/etherscan/error/ConnectionException.java index c22955c..ec4bd8e 100644 --- a/src/main/java/io/api/etherscan/error/ConnectionException.java +++ b/src/main/java/io/api/etherscan/error/ConnectionException.java @@ -1,8 +1,6 @@ package io.api.etherscan.error; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 29.10.2018 */ 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 3a33515..6b1d1e1 100644 --- a/src/main/java/io/api/etherscan/executor/impl/HttpExecutor.java +++ b/src/main/java/io/api/etherscan/executor/impl/HttpExecutor.java @@ -96,7 +96,7 @@ public String get(final String urlAsString) { } catch (SocketTimeoutException e) { throw new ApiTimeoutException("Timeout: Could not establish connection for " + connectTimeout + " millis", e); } catch (Exception e) { - throw new ConnectionException(e.getLocalizedMessage(), e); + throw new ConnectionException(e.getMessage(), e); } } @@ -126,7 +126,7 @@ public String post(final String urlAsString, final String dataToPost) { } catch (SocketTimeoutException e) { throw new ApiTimeoutException("Timeout: Could not establish connection for " + connectTimeout + " millis", e); } catch (Exception e) { - throw new ConnectionException(e.getLocalizedMessage(), e); + throw new ConnectionException(e.getMessage(), e); } } @@ -136,8 +136,6 @@ private String readData(final HttpURLConnection connection) throws IOException { String inputLine; while ((inputLine = in.readLine()) != null) content.append(inputLine); - - in.close(); } return content.toString(); @@ -146,11 +144,11 @@ private String readData(final HttpURLConnection connection) throws IOException { private InputStreamReader getStreamReader(final HttpURLConnection connection) throws IOException { switch (String.valueOf(connection.getContentEncoding())) { case "gzip": - return new InputStreamReader(new GZIPInputStream(connection.getInputStream()), "utf-8"); + return new InputStreamReader(new GZIPInputStream(connection.getInputStream()), StandardCharsets.UTF_8); case "deflate": - return new InputStreamReader(new InflaterInputStream(connection.getInputStream()), "utf-8"); + return new InputStreamReader(new InflaterInputStream(connection.getInputStream()), StandardCharsets.UTF_8); default: - return new InputStreamReader(connection.getInputStream(), "utf-8"); + return new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8); } } } diff --git a/src/test/java/io/api/etherscan/account/AccountBalanceListTest.java b/src/test/java/io/api/etherscan/account/AccountBalanceListTest.java index f5a1ebd..3dac0c0 100644 --- a/src/test/java/io/api/etherscan/account/AccountBalanceListTest.java +++ b/src/test/java/io/api/etherscan/account/AccountBalanceListTest.java @@ -7,6 +7,7 @@ import org.junit.Assert; import org.junit.Test; +import java.math.BigInteger; import java.util.ArrayList; import java.util.List; @@ -18,7 +19,7 @@ */ public class AccountBalanceListTest extends Assert { - private EtherScanApi api = new EtherScanApi(); + private final EtherScanApi api = new EtherScanApi(); @Test public void correct() { @@ -40,7 +41,7 @@ public void correct() { assertNotNull(balance.getEther()); assertNotNull(balance.getGwei()); assertNotNull(balance.getAddress()); - assertNotEquals(0, balance.getWei()); + assertNotEquals(BigInteger.ZERO, balance.getWei()); assertNotNull(balance.toString()); } } diff --git a/src/test/java/io/api/etherscan/account/AccountBalanceTest.java b/src/test/java/io/api/etherscan/account/AccountBalanceTest.java index 21eca22..2aaa0cb 100644 --- a/src/test/java/io/api/etherscan/account/AccountBalanceTest.java +++ b/src/test/java/io/api/etherscan/account/AccountBalanceTest.java @@ -10,6 +10,7 @@ import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; +import java.math.BigInteger; import java.util.Arrays; import java.util.Collection; @@ -74,7 +75,7 @@ public void correct() { assertNotNull(balance.getGwei()); assertNotNull(balance.getEther()); assertNotNull(balance.getAddress()); - assertNotEquals(0, balance.getWei()); + assertNotEquals(BigInteger.ZERO, balance.getWei()); assertNotNull(balance.toString()); } From 670ce918b639d27e0bcbe6f0c3603784d4bb0a7d Mon Sep 17 00:00:00 2001 From: iSnow <139699+iSnow@users.noreply.github.com> Date: Thu, 9 Jul 2020 22:01:21 +0200 Subject: [PATCH 019/112] Method to get an event time stamp as milliseconds since the Unix epoch to avoid time zone calculations (cherry picked from commit d27d01a51d0d2bab653ee0f11b37bce7282ea55d) --- .gitignore | 32 ++++++++++++++++++- src/main/java/io/api/etherscan/model/Log.java | 15 ++++++++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 1062418..719a4de 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,32 @@ -.idea/ +# Compiled class files +*.class + +# Log file +*.log +**/.log + +# IntelliJ *.iml +/.idea + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +# other +/bin/ +/.classpath +/.project +/target/ +/out/ +/.DS_Store +/.settings/ + diff --git a/src/main/java/io/api/etherscan/model/Log.java b/src/main/java/io/api/etherscan/model/Log.java index cf485fd..f1535b3 100644 --- a/src/main/java/io/api/etherscan/model/Log.java +++ b/src/main/java/io/api/etherscan/model/Log.java @@ -60,12 +60,25 @@ public LocalDateTime getTimeStamp() { if(_timeStamp == null && !BasicUtils.isEmpty(timeStamp)) { long formatted = (timeStamp.charAt(0) == '0' && timeStamp.charAt(1) == 'x') ? BasicUtils.parseHex(timeStamp).longValue() - : Long.valueOf(timeStamp); + : Long.parseLong(timeStamp); _timeStamp = LocalDateTime.ofEpochSecond(formatted, 0, ZoneOffset.UTC); } return _timeStamp; } + /** + * + * @return + */ + public Long getTimeStampAsMillis() { + if (BasicUtils.isEmpty(timeStamp)) { + return null; + } + return (timeStamp.charAt(0) == '0' && timeStamp.charAt(1) == 'x') + ? BasicUtils.parseHex(timeStamp).longValue() + : Long.parseLong(timeStamp) * 1000; + } + public String getData() { return data; } From 4a570d203b864795ea62a5be55c35e0516e4ade3 Mon Sep 17 00:00:00 2001 From: iSnow <139699+iSnow@users.noreply.github.com> Date: Thu, 9 Jul 2020 22:11:13 +0200 Subject: [PATCH 020/112] Method to get an event time stamp as milliseconds since the Unix epoch to avoid time zone calculations (cherry picked from commit 8de0601e788773696f583bbe4467c938ac494e73) --- src/main/java/io/api/etherscan/model/Log.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/api/etherscan/model/Log.java b/src/main/java/io/api/etherscan/model/Log.java index f1535b3..2fa58ee 100644 --- a/src/main/java/io/api/etherscan/model/Log.java +++ b/src/main/java/io/api/etherscan/model/Log.java @@ -67,16 +67,18 @@ public LocalDateTime getTimeStamp() { } /** - * - * @return + * Return the "timeStamp" field of the event record as a long-int representing the milliseconds + * since the Unix epoch (1970-01-01 00:00:00). + * @return milliseconds between Unix epoch and `timeStamp`. If field is empty or null, returns null */ public Long getTimeStampAsMillis() { if (BasicUtils.isEmpty(timeStamp)) { return null; } - return (timeStamp.charAt(0) == '0' && timeStamp.charAt(1) == 'x') + long tsSecs = (timeStamp.charAt(0) == '0' && timeStamp.charAt(1) == 'x') ? BasicUtils.parseHex(timeStamp).longValue() - : Long.parseLong(timeStamp) * 1000; + : Long.parseLong(timeStamp); + return tsSecs * 1000; } public String getData() { From c1e995fa0b95160bf6b57e573f994fbf3514098b Mon Sep 17 00:00:00 2001 From: iSnow <139699+iSnow@users.noreply.github.com> Date: Tue, 14 Jul 2020 15:07:56 +0200 Subject: [PATCH 021/112] Method to get all events without a topic filter (cherry picked from commit 235be0d2021420d920a44b60979c7143e5e9d60c) --- .../api/etherscan/model/query/impl/LogQueryBuilder.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 d4e10be..1397f15 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 @@ -2,6 +2,7 @@ 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; /** @@ -12,7 +13,7 @@ * @author GoodforGod * @since 31.10.2018 */ -public class LogQueryBuilder { +public class LogQueryBuilder implements IQueryBuilder { private static final long MIN_BLOCK = 0; private static final long MAX_BLOCK = 99999999999L; @@ -75,4 +76,9 @@ public LogTopicQuadro topic(String topic0, String topic1, String topic2, String 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); + } } From 1e039952fdce04a0c23c755e777f604fc7551cd6 Mon Sep 17 00:00:00 2001 From: iSnow <139699+iSnow@users.noreply.github.com> Date: Tue, 14 Jul 2020 22:05:33 +0200 Subject: [PATCH 022/112] Higher-level API to read well-known ETH events as polymorphic classes (cherry picked from commit ec0cf80dc4d79f47f638ea5d56e793ee27b6f358) --- .../io/api/etherscan/core/IEventsApi.java | 28 ++++++++++ .../core/impl/EventsApiProvider.java | 55 +++++++++++++++++++ .../etherscan/error/EventModelException.java | 11 ++++ .../io/api/etherscan/model/event/IEvent.java | 32 +++++++++++ .../model/event/impl/ApprovalEvent.java | 10 ++++ .../model/event/impl/DepositEvent.java | 10 ++++ .../api/etherscan/model/event/impl/Event.java | 42 ++++++++++++++ .../etherscan/model/event/impl/MintEvent.java | 11 ++++ .../etherscan/model/event/impl/SyncEvent.java | 10 ++++ .../model/event/impl/TransferErc20Event.java | 31 +++++++++++ .../model/event/impl/WithdrawEvent.java | 10 ++++ 11 files changed, 250 insertions(+) create mode 100644 src/main/java/io/api/etherscan/core/IEventsApi.java create mode 100644 src/main/java/io/api/etherscan/core/impl/EventsApiProvider.java create mode 100644 src/main/java/io/api/etherscan/error/EventModelException.java create mode 100644 src/main/java/io/api/etherscan/model/event/IEvent.java create mode 100644 src/main/java/io/api/etherscan/model/event/impl/ApprovalEvent.java create mode 100644 src/main/java/io/api/etherscan/model/event/impl/DepositEvent.java create mode 100644 src/main/java/io/api/etherscan/model/event/impl/Event.java create mode 100644 src/main/java/io/api/etherscan/model/event/impl/MintEvent.java create mode 100644 src/main/java/io/api/etherscan/model/event/impl/SyncEvent.java create mode 100644 src/main/java/io/api/etherscan/model/event/impl/TransferErc20Event.java create mode 100644 src/main/java/io/api/etherscan/model/event/impl/WithdrawEvent.java diff --git a/src/main/java/io/api/etherscan/core/IEventsApi.java b/src/main/java/io/api/etherscan/core/IEventsApi.java new file mode 100644 index 0000000..c5f3665 --- /dev/null +++ b/src/main/java/io/api/etherscan/core/IEventsApi.java @@ -0,0 +1,28 @@ +package io.api.etherscan.core; + +import io.api.etherscan.error.ApiException; +import io.api.etherscan.model.Log; +import io.api.etherscan.model.event.IEvent; +import io.api.etherscan.model.query.impl.LogQuery; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * EtherScan - API Descriptions + * https://etherscan.io/apis#logs + */ +public interface IEventsApi { + + /** + * This is a high-level alternative to the ILogsApi and an 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 events(LogQuery query) throws ApiException; +} diff --git a/src/main/java/io/api/etherscan/core/impl/EventsApiProvider.java b/src/main/java/io/api/etherscan/core/impl/EventsApiProvider.java new file mode 100644 index 0000000..b99a934 --- /dev/null +++ b/src/main/java/io/api/etherscan/core/impl/EventsApiProvider.java @@ -0,0 +1,55 @@ +package io.api.etherscan.core.impl; + +import io.api.etherscan.core.IEventsApi; +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.event.IEvent; +import io.api.etherscan.model.event.impl.Event; +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 java.util.stream.Collectors; + +/** + * Logs API Implementation + * + * @see IEventsApi + * + */ +public class EventsApiProvider extends BasicProvider implements IEventsApi { + + private static final String ACT_LOGS_PARAM = ACT_PREFIX + "getLogs"; + + EventsApiProvider(final IQueueManager queue, + final String baseUrl, + final IHttpExecutor executor) { + super(queue, "logs", baseUrl, executor); + } + + @NotNull + @Override + public List events(final LogQuery query) throws ApiException { + final String urlParams = ACT_LOGS_PARAM + query.getParams(); + final LogResponseTO response = getRequest(urlParams, LogResponseTO.class); + BasicUtils.validateTxResponse(response); + + if (BasicUtils.isEmpty(response.getResult())) { + return Collections.emptyList(); + }; + return response + .getResult() + .stream() + .map((log) -> { + String eventTypeHash = log.getTopics().get(0); + return IEvent.createEvent(eventTypeHash, log); + }) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/io/api/etherscan/error/EventModelException.java b/src/main/java/io/api/etherscan/error/EventModelException.java new file mode 100644 index 0000000..5c3e17e --- /dev/null +++ b/src/main/java/io/api/etherscan/error/EventModelException.java @@ -0,0 +1,11 @@ +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/model/event/IEvent.java b/src/main/java/io/api/etherscan/model/event/IEvent.java new file mode 100644 index 0000000..0fb0e65 --- /dev/null +++ b/src/main/java/io/api/etherscan/model/event/IEvent.java @@ -0,0 +1,32 @@ +package io.api.etherscan.model.event; + +import io.api.etherscan.error.ApiException; +import io.api.etherscan.error.EventModelException; +import io.api.etherscan.model.Log; + +import java.util.HashMap; +import java.util.Map; + +public interface IEvent { + static final Map> subTypes = new HashMap<>(); + + void setLog(Log log); + + static void registerEventType(String typeHash, Class clazz) { + subTypes.put(typeHash, clazz); + } + + static IEvent createEvent(String typeHash, Log log) { + if (null == typeHash) { + throw new EventModelException("Event type hash cannot be null"); + } + Class clazz = subTypes.get(typeHash); + try { + IEvent evt = (IEvent) clazz.newInstance(); + evt.setLog(log); + return evt; + } catch (InstantiationException | IllegalAccessException ex) { + throw new ApiException("Client-side error instantiating Event object", ex); + } + } +} diff --git a/src/main/java/io/api/etherscan/model/event/impl/ApprovalEvent.java b/src/main/java/io/api/etherscan/model/event/impl/ApprovalEvent.java new file mode 100644 index 0000000..915b99c --- /dev/null +++ b/src/main/java/io/api/etherscan/model/event/impl/ApprovalEvent.java @@ -0,0 +1,10 @@ +package io.api.etherscan.model.event.impl; + +import io.api.etherscan.model.event.IEvent; + +public class ApprovalEvent extends Event { + static final String eventTypeHash = "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"; + static { + IEvent.registerEventType(ApprovalEvent.eventTypeHash, ApprovalEvent.class); + } +} diff --git a/src/main/java/io/api/etherscan/model/event/impl/DepositEvent.java b/src/main/java/io/api/etherscan/model/event/impl/DepositEvent.java new file mode 100644 index 0000000..31343a5 --- /dev/null +++ b/src/main/java/io/api/etherscan/model/event/impl/DepositEvent.java @@ -0,0 +1,10 @@ +package io.api.etherscan.model.event.impl; + +import io.api.etherscan.model.event.IEvent; + +public class DepositEvent extends Event { + static final String eventTypeHash = "0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c"; + static { + IEvent.registerEventType(DepositEvent.eventTypeHash, DepositEvent.class); + } +} diff --git a/src/main/java/io/api/etherscan/model/event/impl/Event.java b/src/main/java/io/api/etherscan/model/event/impl/Event.java new file mode 100644 index 0000000..dbcffcd --- /dev/null +++ b/src/main/java/io/api/etherscan/model/event/impl/Event.java @@ -0,0 +1,42 @@ +package io.api.etherscan.model.event.impl; + +import io.api.etherscan.error.ApiException; +import io.api.etherscan.error.EventModelException; +import io.api.etherscan.model.Log; +import io.api.etherscan.model.event.IEvent; + +import java.util.HashMap; +import java.util.Map; + +/** + * Base class for a higher-level API on top of {@link Log}. Each Event class has an identifying hash + */ +public class Event implements IEvent { + + static String eventTypeHash; + + private Log log; + + String address; + + public static String getEventTypeHash() { + return eventTypeHash; + } + + public Log getLog() { + return log; + } + + public String getAddress() { + return address; + } + + public void setLog(Log log) { + this.log = log; + } + + public void setAddress(String address) { + this.address = address; + } + +} diff --git a/src/main/java/io/api/etherscan/model/event/impl/MintEvent.java b/src/main/java/io/api/etherscan/model/event/impl/MintEvent.java new file mode 100644 index 0000000..597dd7e --- /dev/null +++ b/src/main/java/io/api/etherscan/model/event/impl/MintEvent.java @@ -0,0 +1,11 @@ +package io.api.etherscan.model.event.impl; + +import io.api.etherscan.model.event.IEvent; + +public class MintEvent extends Event { + static final String eventTypeHash = "0x4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f"; + static { + IEvent.registerEventType(MintEvent.eventTypeHash, MintEvent.class); + } + +} diff --git a/src/main/java/io/api/etherscan/model/event/impl/SyncEvent.java b/src/main/java/io/api/etherscan/model/event/impl/SyncEvent.java new file mode 100644 index 0000000..ff566f4 --- /dev/null +++ b/src/main/java/io/api/etherscan/model/event/impl/SyncEvent.java @@ -0,0 +1,10 @@ +package io.api.etherscan.model.event.impl; + +import io.api.etherscan.model.event.IEvent; + +public class SyncEvent extends Event { + static final String eventTypeHash = "0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1"; + static { + IEvent.registerEventType(SyncEvent.eventTypeHash, SyncEvent.class); + } +} diff --git a/src/main/java/io/api/etherscan/model/event/impl/TransferErc20Event.java b/src/main/java/io/api/etherscan/model/event/impl/TransferErc20Event.java new file mode 100644 index 0000000..a1a8be0 --- /dev/null +++ b/src/main/java/io/api/etherscan/model/event/impl/TransferErc20Event.java @@ -0,0 +1,31 @@ +package io.api.etherscan.model.event.impl; + +import io.api.etherscan.model.event.IEvent; + +public class TransferErc20Event extends Event { + static final String eventTypeHash = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"; + static { + IEvent.registerEventType(TransferErc20Event.eventTypeHash, TransferErc20Event.class); + } + + String fromAddress; + + String toAddress; + + public String getFromAddress() { + return fromAddress; + } + + public void setFromAddress(String fromAddress) { + this.fromAddress = fromAddress; + } + + public String getToAddress() { + return toAddress; + } + + public void setToAddress(String toAddress) { + this.toAddress = toAddress; + } + +} diff --git a/src/main/java/io/api/etherscan/model/event/impl/WithdrawEvent.java b/src/main/java/io/api/etherscan/model/event/impl/WithdrawEvent.java new file mode 100644 index 0000000..3fa442b --- /dev/null +++ b/src/main/java/io/api/etherscan/model/event/impl/WithdrawEvent.java @@ -0,0 +1,10 @@ +package io.api.etherscan.model.event.impl; + +import io.api.etherscan.model.event.IEvent; + +public class WithdrawEvent extends Event { + static final String eventTypeHash = "0x7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65"; + static { + IEvent.registerEventType(WithdrawEvent.eventTypeHash, WithdrawEvent.class); + } +} From d7ab7ecceb2a82e75ea37993b606209d9a2e2a86 Mon Sep 17 00:00:00 2001 From: iSnow <139699+iSnow@users.noreply.github.com> Date: Mon, 5 Oct 2020 17:01:10 +0200 Subject: [PATCH 023/112] Added better handling of communication errors (cherry picked from commit 28932837fc84a959e071cc2a264fc70894ea031a) --- .../java/io/api/etherscan/error/ConnectionException.java | 4 ++++ .../java/io/api/etherscan/executor/impl/HttpExecutor.java | 7 +++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/api/etherscan/error/ConnectionException.java b/src/main/java/io/api/etherscan/error/ConnectionException.java index c22955c..410c0ac 100644 --- a/src/main/java/io/api/etherscan/error/ConnectionException.java +++ b/src/main/java/io/api/etherscan/error/ConnectionException.java @@ -8,6 +8,10 @@ */ 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/executor/impl/HttpExecutor.java b/src/main/java/io/api/etherscan/executor/impl/HttpExecutor.java index 3a33515..c059d27 100644 --- a/src/main/java/io/api/etherscan/executor/impl/HttpExecutor.java +++ b/src/main/java/io/api/etherscan/executor/impl/HttpExecutor.java @@ -18,8 +18,7 @@ import java.util.zip.GZIPInputStream; import java.util.zip.InflaterInputStream; -import static java.net.HttpURLConnection.HTTP_MOVED_PERM; -import static java.net.HttpURLConnection.HTTP_MOVED_TEMP; +import static java.net.HttpURLConnection.*; /** * Http client implementation @@ -88,6 +87,10 @@ public String get(final String urlAsString) { final int status = connection.getResponseCode(); if (status == HTTP_MOVED_TEMP || status == HTTP_MOVED_PERM) { return get(connection.getHeaderField("Location")); + } else if ((status >= HTTP_BAD_REQUEST) && (status < HTTP_INTERNAL_ERROR)) { + throw new ConnectionException("Protocol error: "+connection.getResponseMessage()); + } else if (status >= HTTP_INTERNAL_ERROR) { + throw new ConnectionException("Server error: "+connection.getResponseMessage()); } final String data = readData(connection); From 01b31ff82aa4e9a764a56435d631a627013d900c Mon Sep 17 00:00:00 2001 From: iSnow <139699+iSnow@users.noreply.github.com> Date: Tue, 6 Oct 2020 09:27:35 +0200 Subject: [PATCH 024/112] Removed events stuff again, moved to different library (cherry picked from commit 5c79d665b63806935f6a89e70aefb6fdf28c3f84) --- src/main/java/io/api/etherscan/core/impl/BasicProvider.java | 2 +- src/main/java/io/api/etherscan/error/ParseException.java | 4 +++- 2 files changed, 4 insertions(+), 2 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 5b95f68..67c790b 100644 --- a/src/main/java/io/api/etherscan/core/impl/BasicProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/BasicProvider.java @@ -42,7 +42,7 @@ T convert(final String json, final Class tClass) { try { return gson.fromJson(json, tClass); } catch (Exception e) { - throw new ParseException(e.getMessage(), e.getCause()); + throw new ParseException(e.getMessage(), e.getCause(), json); } } diff --git a/src/main/java/io/api/etherscan/error/ParseException.java b/src/main/java/io/api/etherscan/error/ParseException.java index 81974df..f279fda 100644 --- a/src/main/java/io/api/etherscan/error/ParseException.java +++ b/src/main/java/io/api/etherscan/error/ParseException.java @@ -7,8 +7,10 @@ * @since 29.10.2018 */ public class ParseException extends ApiException { + String json; - public ParseException(String message, Throwable cause) { + public ParseException(String message, Throwable cause, String json) { super(message, cause); + this.json = json; } } From 5729da7d7c1fbffd8e68adf13d7d3ab166793402 Mon Sep 17 00:00:00 2001 From: iSnow <139699+iSnow@users.noreply.github.com> Date: Tue, 6 Oct 2020 09:45:28 +0200 Subject: [PATCH 025/112] Removed events stuff again, moved to different library (cherry picked from commit 50d5799439967f0bb7a21c98d211cde5c604b1d0) --- src/test/java/io/api/util/BasicUtilsTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/io/api/util/BasicUtilsTests.java b/src/test/java/io/api/util/BasicUtilsTests.java index 1b1753e..2c5ad71 100644 --- a/src/test/java/io/api/util/BasicUtilsTests.java +++ b/src/test/java/io/api/util/BasicUtilsTests.java @@ -98,6 +98,6 @@ public void isResponseNullThrows() { @Test(expected = ParseException.class) public void isThrowParseException() { - throw new ParseException("Test", null); + throw new ParseException("Test", null, null); } } From a4e4b0993d9abbe367b5a6b60a098a88d5a9a278 Mon Sep 17 00:00:00 2001 From: iSnow <139699+iSnow@users.noreply.github.com> Date: Tue, 6 Oct 2020 11:23:03 +0200 Subject: [PATCH 026/112] Added support for rate limiting by Etherscan: throw RateLimitException (cherry picked from commit 120ba0fc8712f42e0492b451a8db8243fddece52) --- .../api/etherscan/core/impl/BasicProvider.java | 17 +++++++++++++++++ .../api/etherscan/error/RateLimitException.java | 15 +++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 src/main/java/io/api/etherscan/error/RateLimitException.java 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 67c790b..d242a76 100644 --- a/src/main/java/io/api/etherscan/core/impl/BasicProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/BasicProvider.java @@ -1,12 +1,16 @@ package io.api.etherscan.core.impl; import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; 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.util.BasicUtils; +import java.util.Map; + /** * Base provider for API Implementations * @@ -42,6 +46,19 @@ T convert(final String json, final Class tClass) { try { return gson.fromJson(json, tClass); } catch (Exception e) { + if (e instanceof JsonSyntaxException) { + Map map = gson.fromJson(json, Map.class); + Object statusCode = map.get("status"); + if ((statusCode instanceof String) && (statusCode.equals("0"))) { + Object message = map.get("message"); + if ((message instanceof String) && (message.equals("NOTOK"))) { + Object result = map.get("result"); + if ((result instanceof String) && (result.equals("Max rate limit reached"))) { + throw new RateLimitException ("Max rate limit reached"); + } + } + } + } throw new ParseException(e.getMessage(), e.getCause(), json); } } diff --git a/src/main/java/io/api/etherscan/error/RateLimitException.java b/src/main/java/io/api/etherscan/error/RateLimitException.java new file mode 100644 index 0000000..2562342 --- /dev/null +++ b/src/main/java/io/api/etherscan/error/RateLimitException.java @@ -0,0 +1,15 @@ +package io.api.etherscan.error; + +/** + * ! NO DESCRIPTION ! + * + * @author iSnow + * @since 2020-10-06 + */ +public class RateLimitException extends ApiException { + + public RateLimitException(String message) { + super(message); + } + +} From 33b519f07e50f9bbf0935a1fc65146083cbaa8a6 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 18 Oct 2020 15:46:10 +0300 Subject: [PATCH 027/112] [1.1.0-SNAPSHOT] POM removed Gradle 6.7 wuth gradle wrapper added Gradle configs and properties added codestyle.xml config added github CI added --- .editorconfig | 21 ++ .gitattributes | 41 +++ .github/workflows/gradle.yml | 38 +++ .gitignore | 37 +-- .travis.yml | 14 - README.md | 8 +- build.gradle | 133 +++++++++ config/codestyle.xml | 348 +++++++++++++++++++++++ gradle.properties | 12 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59203 bytes gradle/wrapper/gradle-wrapper.properties | 5 + gradlew | 185 ++++++++++++ gradlew.bat | 89 ++++++ pom.xml | 190 ------------- settings.gradle | 1 + settings.xml | 26 -- 16 files changed, 882 insertions(+), 266 deletions(-) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .github/workflows/gradle.yml delete mode 100644 .travis.yml create mode 100644 build.gradle create mode 100644 config/codestyle.xml create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat delete mode 100644 pom.xml create mode 100644 settings.gradle delete mode 100644 settings.xml diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..5b9451e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,21 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# all-encompassing default settings unless otherwise specified +[*] +end_of_line = lf +charset = utf-8 + +# Yaml +[{*.yml, *.yaml}] +indent_size = 2 +indent_style = space + +# Property files +[*.properties] +indent_size = 2 +indent_style = space + + diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..ccc6fb5 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,41 @@ +# Handle line endings automatically for files detected as text +# 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 +*.df text +*.htm text diff=html +*.html text diff=html eol=lf +*.java text diff=java eol=lf +*.js text +*.json text eol=lf +*.jsp text eol=lf +*.jspf text eol=lf +*.jspx text eol=lf +*.properties text eol=lf +*.sh text eol=lf +*.tld text +*.txt text eol=lf +*.tag text +*.tagx text +*.xml text +*.yml text eol=lf + +# These files are binary and should be left untouched +# (binary is a macro for -text -diff) +*.class binary +*.dll binary +*.ear binary +*.gif binary +*.ico binary +*.jar binary +*.jpg binary +*.jpeg binary +*.png binary +*.so binary +*.war binary \ No newline at end of file diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml new file mode 100644 index 0000000..655e4ad --- /dev/null +++ b/.github/workflows/gradle.yml @@ -0,0 +1,38 @@ +name: Java CI + +on: + push: + branches: + - master + - dev + schedule: + - cron: "0 12 1 * *" + pull_request: + branches: + - master + - dev + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + java: [ '1.8', '11' ] + name: Java ${{ matrix.java }} setup + + steps: + - uses: actions/checkout@v1 + - name: Set up JDK + uses: actions/setup-java@v1 + + with: + java-version: ${{ matrix.java }} + + - name: Build with Gradle + run: ./gradlew build jacocoTestReport + + - name: Analyze with SonarQube + run: ./gradlew sonarqube + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.gitignore b/.gitignore index 719a4de..c48c7a6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,32 +1,7 @@ -# Compiled class files -*.class - -# Log file -*.log -**/.log - -# IntelliJ -*.iml -/.idea - -# Package Files # -*.jar -*.war -*.nar -*.ear -*.zip -*.tar.gz -*.rar - -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* - -# other -/bin/ -/.classpath -/.project -/target/ -/out/ -/.DS_Store /.settings/ - +.idea +.idea/httpRequests +*.iml +.gradle +build +target/ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index ea1440d..0000000 --- a/.travis.yml +++ /dev/null @@ -1,14 +0,0 @@ -language: java - -dist: trusty -sudo: false - -script: - - "mvn cobertura:cobertura" - -after_success: - - bash <(curl -s https://codecov.io/bash) - -jdk: - - oraclejdk8 - - openjdk8 diff --git a/README.md b/README.md index f62f766..733a2ed 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ # java-etherscan-api -![travis](https://travis-ci.org/GoodforGod/java-etherscan-api.svg?branch=master) -[![Maintainability](https://api.codeclimate.com/v1/badges/808997be2e69ff1ae8fe/maintainability)](https://codeclimate.com/github/GoodforGod/java-etherscan-api/maintainability) -[![codecov](https://codecov.io/gh/GoodforGod/java-etherscan-api/branch/master/graph/badge.svg)](https://codecov.io/gh/GoodforGod/java-etherscan-api) +[![Jitpack](https://jitpack.io/v/iSnow/java-etherscan-api.svg)](https://jitpack.io/#GoodforGod/java-etherscan-api) [Etherscan](https://etherscan.io/apis) Java API implementation. @@ -14,14 +12,14 @@ Library supports all available EtherScan *API* calls for all available *Ethereum com.github.goodforgod java-etherscan-api - 1.0.2 + 1.1.0 ``` **Gradle** ```groovy dependencies { - compile 'com.github.goodforgod:java-etherscan-api:1.0.2' + compile 'com.github.goodforgod:java-etherscan-api:1.1.0' } ``` diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..a68159c --- /dev/null +++ b/build.gradle @@ -0,0 +1,133 @@ +plugins { + id 'jacoco' + id 'java-library' + id 'maven-publish' + + id 'org.sonarqube' version '3.0' + id 'com.diffplug.gradle.spotless' version '4.4.0' +} + +repositories { + mavenLocal() + mavenCentral() + jcenter() +} + +group = groupId +version = artifactVersion + +sourceCompatibility = 1.8 +targetCompatibility = 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:20.1.0' + implementation 'com.google.code.gson:gson:2.8.6' + + testImplementation 'junit:junit:4.13.1' +} + +test { + useJUnit() + testLogging { + events "passed", "skipped", "failed" + exceptionFormat "full" + } +} + +tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' + options.incremental = true + options.fork = true +} + +tasks.withType(Test) { + reports.html.enabled = false + reports.junitXml.enabled = false +} + +java { + withJavadocJar() + withSourcesJar() +} + +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + + pom { + name = 'Java Etherscan API' + url = 'https://github.com/GoodforGod/java-etherscan-api' + description = 'Library is a wrapper for EtherScan API.' + + license { + name = 'MIT License' + url = 'https://github.com/GoodforGod/java-etherscan-api/blob/master/LICENSE' + distribution = 'repo' + } + + developer { + id = 'GoodforGod' + name = 'Anton Kurako' + email = 'goodforgod.dev@gmail.com' + url = 'https://github.com/GoodforGod' + } + + scm { + connection = 'scm:git:git://github.com/GoodforGod/java-etherscan-api.git' + developerConnection = 'scm:git:ssh://GoodforGod/java-etherscan-api.git' + url = 'https://github.com/GoodforGod/java-etherscan-api/tree/master' + } + } + } + } + repositories { + maven { + def releasesRepoUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2" + def snapshotsRepoUrl = "https://oss.sonatype.org/content/repositories/snapshots/" + url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl + credentials { + username System.getenv("OSS_USERNAME") + password System.getenv("OSS_PASSWORD") + } + } + } +} + +check.dependsOn jacocoTestReport +jacocoTestReport { + reports { + xml.enabled true + html.destination file("${buildDir}/jacocoHtml") + } +} + +if (project.hasProperty("signing.keyId")) { + apply plugin: 'signing' + signing { + sign publishing.publications.mavenJava + } +} + +javadoc { + options.encoding = "UTF-8" + if (JavaVersion.current().isJava9Compatible()) { + options.addBooleanOption('html5', true) + } +} \ No newline at end of file diff --git a/config/codestyle.xml b/config/codestyle.xml new file mode 100644 index 0000000..0c19beb --- /dev/null +++ b/config/codestyle.xml @@ -0,0 +1,348 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..0c89028 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,12 @@ +groupId=com.github.goodforgod +artifactId=java-etherscan-api +artifactVersion=1.1.0-SNAPSHOT +buildNumber=1 + + +##### GRADLE ##### +org.gradle.daemon=true +org.gradle.parallel=true +org.gradle.configureondemand=true +org.gradle.caching=true +org.gradle.jvmargs=-Dfile.encoding=UTF-8 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..e708b1c023ec8b20f512888fe07c5bd3ff77bb8f GIT binary patch literal 59203 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

K7UpUK$>l@ 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 E2O(~oYXATM literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..be52383 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..4f906e0 --- /dev/null +++ b/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# 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 +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# 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 + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +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 +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 + +# 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 + # 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\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="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 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/pom.xml b/pom.xml deleted file mode 100644 index 6c51f28..0000000 --- a/pom.xml +++ /dev/null @@ -1,190 +0,0 @@ - - - 4.0.0 - - com.github.goodforgod - java-etherscan-api - 1.0.2 - jar - - ${project.groupId}:${project.artifactId} - Library is a wrapper for EtherScan API. - http://maven.apache.org - - - - MIT License - https://github.com/GoodforGod/java-etherscan-api/blob/master/LICENSE - repo - - - - - - Anton Kurako - goodforgod.dev@gmail.com - https://github.com/GoodforGod - - - - - scm:git:git://github.com/GoodforGod/java-etherscan-api.git - scm:git:ssh://GoodforGod/java-etherscan-api.git - https://github.com/GoodforGod/java-etherscan-api/tree/master - - - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - - - UTF-8 - 1.8 - 1.8 - - 3.0.2 - 1.6 - 3.0.1 - 3.0.1 - 1.6.8 - 2.7 - - 4.13.1 - 2.8.5 - - - - - junit - junit - ${junit-version} - test - - - - com.google.code.gson - gson - ${gson-version} - - - - org.jetbrains - annotations - 13.0 - - - - - - release - - - - org.sonatype.plugins - nexus-staging-maven-plugin - ${maven-nexus-staging-maven-plugin-version} - true - - ossrh - https://oss.sonatype.org/ - true - - - - - - - - sign - - - - org.apache.maven.plugins - maven-gpg-plugin - ${maven-gpg-plugin-version} - - - sign-artifacts - verify - - sign - - - - - - - - - - build-extras - - true - - - - - org.apache.maven.plugins - maven-source-plugin - ${maven-source-plugin-version} - - - attach-sources - - jar-no-fork - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - ${maven-javadoc-plugin-version} - - - attach-javadocs - - jar - - - - - -html5 - - - - - - - - - - - org.codehaus.mojo - cobertura-maven-plugin - ${cobertura-plugin-version} - - - html - xml - - - - - - org.apache.maven.plugins - maven-jar-plugin - ${maven-build-plugin-version} - - - - diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..8e004bc --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = artifactId diff --git a/settings.xml b/settings.xml deleted file mode 100644 index ab979c3..0000000 --- a/settings.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - ossrh - ${env.OSSRH_JIRA_USERNAME} - ${env.OSSRH_JIRA_PASSWORD} - - - - - - ossrh - - true - - - gpg - ${env.GPG_KEY_NAME} - ${env.GPG_PASSPHRASE} - - - - \ No newline at end of file From 664cb72a2d876205bcd7f6058913608e0dcc9141 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 18 Oct 2020 15:48:32 +0300 Subject: [PATCH 028/112] [1.1.0-SNAPSHOT] Error handling and error message improved RateLimitException handling added and improved HttpExecutor impl error handling improved IQueueManager contract refactored QueueManager semaphore impl added IQueueManager construct parameter added Default queue manager 1req\7 sec added as other limits throw rate limit --- .../io/api/etherscan/core/IAccountApi.java | 77 ++++++++++----- .../java/io/api/etherscan/core/IBlockApi.java | 7 +- .../io/api/etherscan/core/IContractApi.java | 7 +- .../io/api/etherscan/core/IEventsApi.java | 9 +- .../java/io/api/etherscan/core/ILogsApi.java | 11 ++- .../java/io/api/etherscan/core/IProxyApi.java | 98 +++++++++++-------- .../io/api/etherscan/core/IStatisticApi.java | 15 ++- .../api/etherscan/core/ITransactionApi.java | 17 ++-- .../core/impl/AccountApiProvider.java | 13 ++- .../etherscan/core/impl/BasicProvider.java | 48 ++++++--- .../etherscan/core/impl/BlockApiProvider.java | 2 +- .../core/impl/ContractApiProvider.java | 4 +- .../api/etherscan/core/impl/EtherScanApi.java | 52 ++++++---- .../core/impl/EventsApiProvider.java | 7 +- .../etherscan/core/impl/ProxyApiProvider.java | 15 +-- .../core/impl/StatisticApiProvider.java | 6 +- .../io/api/etherscan/error/ApiException.java | 2 - .../api/etherscan/error/ApiKeyException.java | 2 - .../etherscan/error/ApiTimeoutException.java | 2 - .../etherscan/error/ConnectionException.java | 2 - .../etherscan/error/EtherScanException.java | 13 ++- .../etherscan/error/EventModelException.java | 3 +- .../error/InvalidAddressException.java | 2 - .../error/InvalidDataHexException.java | 2 - .../error/InvalidTxHashException.java | 2 - .../etherscan/error/LogQueryException.java | 2 - .../api/etherscan/error/ParseException.java | 9 +- .../etherscan/error/RateLimitException.java | 3 - .../api/etherscan/executor/IHttpExecutor.java | 2 +- .../etherscan/executor/impl/HttpExecutor.java | 26 ++--- .../api/etherscan/manager/IQueueManager.java | 8 +- .../manager/impl/FakeQueueManager.java | 4 +- .../etherscan/manager/impl/QueueManager.java | 57 +++-------- src/main/java/io/api/etherscan/model/Abi.java | 10 +- .../java/io/api/etherscan/model/Balance.java | 13 ++- .../java/io/api/etherscan/model/BaseTx.java | 27 +++-- .../java/io/api/etherscan/model/Block.java | 12 ++- .../io/api/etherscan/model/EthNetwork.java | 1 + src/main/java/io/api/etherscan/model/Log.java | 39 +++++--- .../java/io/api/etherscan/model/Price.java | 16 +-- .../java/io/api/etherscan/model/Status.java | 9 +- .../io/api/etherscan/model/TokenBalance.java | 9 +- src/main/java/io/api/etherscan/model/Tx.java | 22 +++-- .../io/api/etherscan/model/TxInternal.java | 16 +-- .../java/io/api/etherscan/model/TxToken.java | 4 +- .../java/io/api/etherscan/model/Uncle.java | 16 +-- .../io/api/etherscan/model/UncleBlock.java | 13 ++- src/main/java/io/api/etherscan/model/Wei.java | 10 +- .../io/api/etherscan/model/event/IEvent.java | 1 + .../model/event/impl/ApprovalEvent.java | 1 + .../model/event/impl/DepositEvent.java | 3 +- .../api/etherscan/model/event/impl/Event.java | 8 +- .../etherscan/model/event/impl/MintEvent.java | 3 +- .../etherscan/model/event/impl/SyncEvent.java | 1 + .../model/event/impl/TransferErc20Event.java | 1 + .../model/event/impl/WithdrawEvent.java | 3 +- .../api/etherscan/model/proxy/BlockProxy.java | 26 ++--- .../etherscan/model/proxy/ReceiptProxy.java | 21 ++-- .../io/api/etherscan/model/proxy/TxProxy.java | 26 ++--- .../etherscan/model/query/IQueryBuilder.java | 1 + .../io/api/etherscan/model/query/LogOp.java | 1 + .../etherscan/model/query/impl/LogQuery.java | 3 +- .../model/query/impl/LogQueryBuilder.java | 20 ++-- .../etherscan/model/utility/BlockParam.java | 1 + .../io/api/etherscan/util/BasicUtils.java | 5 +- 65 files changed, 492 insertions(+), 379 deletions(-) diff --git a/src/main/java/io/api/etherscan/core/IAccountApi.java b/src/main/java/io/api/etherscan/core/IAccountApi.java index 7d44d15..6eda869 100644 --- a/src/main/java/io/api/etherscan/core/IAccountApi.java +++ b/src/main/java/io/api/etherscan/core/IAccountApi.java @@ -7,8 +7,7 @@ import java.util.List; /** - * EtherScan - API Descriptions - * https://etherscan.io/apis#accounts + * EtherScan - API Descriptions https://etherscan.io/apis#accounts * * @author GoodforGod * @since 28.10.2018 @@ -17,79 +16,107 @@ public interface IAccountApi { /** * Address ETH balance + * * @param address get balance for * @return balance * @throws ApiException parent exception class */ - @NotNull Balance balance(String address) throws ApiException; + @NotNull + Balance balance(String address) throws ApiException; /** * ERC20 token balance for address - * @param address get balance for + * + * @param address get balance for * @param contract token contract * @return token balance for address * @throws ApiException parent exception class */ - @NotNull TokenBalance balance(String address, String contract) throws ApiException; + @NotNull + TokenBalance balance(String address, String contract) throws ApiException; /** - * Maximum 20 address for single batch request - * If address MORE THAN 20, then there will be more than 1 request performed + * Maximum 20 address for single batch request If address MORE THAN 20, then + * there will be more than 1 request performed + * * @param addresses addresses to get balances for * @return list of balances * @throws ApiException parent exception class */ - @NotNull List balances(List addresses) throws ApiException; + @NotNull + List balances(List addresses) throws ApiException; /** * All txs for given address - * @param address get txs for + * + * @param address get txs for * @param startBlock tx from this blockNumber - * @param endBlock tx to this blockNumber + * @param endBlock tx to this blockNumber * @return txs for address * @throws ApiException parent exception class */ - @NotNull List txs(String address, long startBlock, long endBlock) throws ApiException; - @NotNull List txs(String address, long startBlock) throws ApiException; - @NotNull List txs(String address) throws ApiException; + @NotNull + List txs(String address, long startBlock, long endBlock) throws ApiException; + + @NotNull + List txs(String address, long startBlock) throws ApiException; + + @NotNull + List txs(String address) throws ApiException; /** * All internal txs for given address - * @param address get txs for + * + * @param address get txs for * @param startBlock tx from this blockNumber - * @param endBlock tx to this blockNumber + * @param endBlock tx to this blockNumber * @return txs for address * @throws ApiException parent exception class */ - @NotNull List txsInternal(String address, long startBlock, long endBlock) throws ApiException; - @NotNull List txsInternal(String address, long startBlock) throws ApiException; - @NotNull List txsInternal(String address) throws ApiException; + @NotNull + List txsInternal(String address, long startBlock, long endBlock) throws ApiException; + + @NotNull + List txsInternal(String address, long startBlock) throws ApiException; + + @NotNull + List txsInternal(String address) throws ApiException; /** * All internal tx for given transaction hash + * * @param txhash transaction hash * @return internal txs list * @throws ApiException parent exception class */ - @NotNull List txsInternalByHash(String txhash) throws ApiException; + @NotNull + List txsInternalByHash(String txhash) throws ApiException; /** * All token txs for given address - * @param address get txs for + * + * @param address get txs for * @param startBlock tx from this blockNumber - * @param endBlock tx to this blockNumber + * @param endBlock tx to this blockNumber * @return txs for address * @throws ApiException parent exception class */ - @NotNull List txsToken(String address, long startBlock, long endBlock) throws ApiException; - @NotNull List txsToken(String address, long startBlock) throws ApiException; - @NotNull List txsToken(String address) throws ApiException; + @NotNull + List txsToken(String address, long startBlock, long endBlock) throws ApiException; + + @NotNull + List txsToken(String address, long startBlock) throws ApiException; + + @NotNull + List txsToken(String address) throws ApiException; /** * All blocks mined by address + * * @param address address to search for * @return blocks mined * @throws ApiException parent exception class */ - @NotNull List minedBlocks(String address) throws ApiException; + @NotNull + List minedBlocks(String address) throws ApiException; } diff --git a/src/main/java/io/api/etherscan/core/IBlockApi.java b/src/main/java/io/api/etherscan/core/IBlockApi.java index e33b0d7..7381ac0 100644 --- a/src/main/java/io/api/etherscan/core/IBlockApi.java +++ b/src/main/java/io/api/etherscan/core/IBlockApi.java @@ -7,8 +7,7 @@ import java.util.Optional; /** - * EtherScan - API Descriptions - * https://etherscan.io/apis#blocks + * EtherScan - API Descriptions https://etherscan.io/apis#blocks * * @author GoodforGod * @since 30.10.2018 @@ -17,9 +16,11 @@ 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; + @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 index 5e3c771..3e9388d 100644 --- a/src/main/java/io/api/etherscan/core/IContractApi.java +++ b/src/main/java/io/api/etherscan/core/IContractApi.java @@ -5,8 +5,7 @@ import org.jetbrains.annotations.NotNull; /** - * EtherScan - API Descriptions - * https://etherscan.io/apis#contracts + * EtherScan - API Descriptions https://etherscan.io/apis#contracts * * @author GoodforGod * @since 28.10.2018 @@ -15,9 +14,11 @@ 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; + @NotNull + Abi contractAbi(String address) throws ApiException; } diff --git a/src/main/java/io/api/etherscan/core/IEventsApi.java b/src/main/java/io/api/etherscan/core/IEventsApi.java index c5f3665..12c19db 100644 --- a/src/main/java/io/api/etherscan/core/IEventsApi.java +++ b/src/main/java/io/api/etherscan/core/IEventsApi.java @@ -1,7 +1,6 @@ package io.api.etherscan.core; import io.api.etherscan.error.ApiException; -import io.api.etherscan.model.Log; import io.api.etherscan.model.event.IEvent; import io.api.etherscan.model.query.impl.LogQuery; import org.jetbrains.annotations.NotNull; @@ -9,14 +8,14 @@ import java.util.List; /** - * EtherScan - API Descriptions - * https://etherscan.io/apis#logs + * EtherScan - API Descriptions https://etherscan.io/apis#logs */ public interface IEventsApi { /** - * This is a high-level alternative to the ILogsApi and an alternative to the native eth_getLogs - * Read at EtherScan API description for full info! + * This is a high-level alternative to the ILogsApi and an 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 diff --git a/src/main/java/io/api/etherscan/core/ILogsApi.java b/src/main/java/io/api/etherscan/core/ILogsApi.java index d1901dd..dc7d8d8 100644 --- a/src/main/java/io/api/etherscan/core/ILogsApi.java +++ b/src/main/java/io/api/etherscan/core/ILogsApi.java @@ -8,8 +8,7 @@ import java.util.List; /** - * EtherScan - API Descriptions - * https://etherscan.io/apis#logs + * EtherScan - API Descriptions https://etherscan.io/apis#logs * * @author GoodforGod * @since 30.10.2018 @@ -17,13 +16,15 @@ public interface ILogsApi { /** - * alternative to the native eth_getLogs - * Read at EtherScan API description for full info! + * alternative to the native eth_getLogs Read at EtherScan API description for + * full info! + * * @param query build log query * @return logs according to query * @throws ApiException parent exception class * * @see io.api.etherscan.model.query.impl.LogQueryBuilder */ - @NotNull List logs(LogQuery query) throws ApiException; + @NotNull + List logs(LogQuery query) throws ApiException; } diff --git a/src/main/java/io/api/etherscan/core/IProxyApi.java b/src/main/java/io/api/etherscan/core/IProxyApi.java index 171c752..58a70ed 100644 --- a/src/main/java/io/api/etherscan/core/IProxyApi.java +++ b/src/main/java/io/api/etherscan/core/IProxyApi.java @@ -10,8 +10,7 @@ import java.util.Optional; /** - * EtherScan - API Descriptions - * https://etherscan.io/apis#proxy + * EtherScan - API Descriptions https://etherscan.io/apis#proxy * * @author GoodforGod * @since 30.10.2018 @@ -19,54 +18,62 @@ public interface IProxyApi { /** - * Returns the number of most recent block - * eth_blockNumber + * Returns the number of most recent block eth_blockNumber + * * @return last block number * @throws ApiException parent exception class */ long blockNoLast(); /** - * Returns information about a block by block number - * eth_getBlockByNumber + * Returns information about a block by block number eth_getBlockByNumber + * * @param blockNo block number from 0 to last * @return optional block result * @throws ApiException parent exception class */ - @NotNull Optional block(long blockNo) throws ApiException; + @NotNull + Optional block(long blockNo) throws ApiException; /** * Returns information about a uncle by block number * eth_getUncleByBlockNumberAndIndex + * * @param blockNo block number from 0 to last - * @param index uncle block index + * @param index uncle block index * @return optional block result * @throws ApiException parent exception class */ - @NotNull Optional blockUncle(long blockNo, long index) throws ApiException; + @NotNull + Optional blockUncle(long blockNo, long index) throws ApiException; /** * Returns the information about a transaction requested by transaction hash * eth_getTransactionByHash + * * @param txhash transaction hash * @return optional tx result * @throws ApiException parent exception class */ - @NotNull Optional tx(String txhash) throws ApiException; + @NotNull + Optional tx(String txhash) throws ApiException; /** - * Returns information about a transaction by block number and transaction index position - * eth_getTransactionByBlockNumberAndIndex + * Returns information about a transaction by block number and transaction index + * position eth_getTransactionByBlockNumberAndIndex + * * @param blockNo block number from 0 to last - * @param index tx index in block + * @param index tx index in block * @return optional tx result * @throws ApiException parent exception class */ - @NotNull Optional tx(long blockNo, long index) throws ApiException; + @NotNull + Optional tx(long blockNo, long index) throws ApiException; /** - * Returns the number of transactions in a block from a block matching the given block number - * eth_getBlockTransactionCountByNumber + * Returns the number of transactions in a block from a block matching the given + * block number eth_getBlockTransactionCountByNumber + * * @param blockNo block number from 0 to last * @return transaction amount in block * @throws ApiException parent exception class @@ -76,6 +83,7 @@ public interface IProxyApi { /** * 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 @@ -83,70 +91,82 @@ public interface IProxyApi { int txSendCount(String address) throws ApiException; /** - * Creates new message call transaction or a contract creation for signed transactions - * eth_sendRawTransaction + * Creates new message call transaction or a contract creation for signed + * transactions eth_sendRawTransaction + * * @param hexEncodedTx encoded hex data to send * @return optional string response * @throws ApiException parent exception class */ - @NotNull Optional txSendRaw(String hexEncodedTx) throws ApiException; + @NotNull + Optional txSendRaw(String hexEncodedTx) throws ApiException; /** * Returns the receipt of a transaction by transaction hash * eth_getTransactionReceipt + * * @param txhash transaction hash * @return optional tx receipt * @throws ApiException parent exception class */ - @NotNull Optional txReceipt(String txhash) throws ApiException; + @NotNull + Optional txReceipt(String txhash) throws ApiException; /** - * Executes a new message call immediately without creating a transaction on the block chain - * eth_call + * Executes a new message call immediately without creating a transaction on the + * block chain eth_call + * * @param address to call - * @param data data to call address + * @param data data to call address * @return optional the return value of executed contract. * @throws ApiException parent exception class */ - @NotNull Optional call(String address, String data) throws ApiException; + @NotNull + Optional call(String address, String data) throws ApiException; /** - * Returns code at a given address - * eth_getCode + * 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 */ - @NotNull Optional code(String address) throws ApiException; + @NotNull + Optional code(String address) throws ApiException; /** - * (**experimental) - * Returns the value from a storage position at a given address + * (**experimental) Returns the value from a storage position at a given address * eth_getStorageAt - * @param address to get storage + * + * @param address to get storage * @param position storage position * @return optional the value at this storage position * @throws ApiException parent exception class */ - @NotNull Optional storageAt(String address, long position) throws ApiException; + @NotNull + Optional storageAt(String address, long position) throws ApiException; /** - * Returns the current price per gas in wei - * eth_gasPrice + * Returns the current price per gas in wei eth_gasPrice + * * @return estimated gas price * @throws ApiException parent exception class */ - @NotNull BigInteger gasPrice() throws ApiException; - + @NotNull + BigInteger gasPrice() throws ApiException; /** - * Makes a call or transaction, which won't be added to the blockchain and returns the used gas, - * which can be used for estimating the used gas + * Makes a call or transaction, which won't be added to the blockchain and + * returns the used gas, which can be used for estimating the used gas * eth_estimateGas + * * @param hexData data to calc gas usage for * @return estimated gas usage * @throws ApiException parent exception class */ - @NotNull BigInteger gasEstimated(String hexData) throws ApiException; - @NotNull BigInteger gasEstimated() throws ApiException; + @NotNull + BigInteger gasEstimated(String hexData) throws ApiException; + + @NotNull + BigInteger gasEstimated() throws ApiException; } diff --git a/src/main/java/io/api/etherscan/core/IStatisticApi.java b/src/main/java/io/api/etherscan/core/IStatisticApi.java index c0b838e..1b7ef59 100644 --- a/src/main/java/io/api/etherscan/core/IStatisticApi.java +++ b/src/main/java/io/api/etherscan/core/IStatisticApi.java @@ -8,8 +8,7 @@ import java.math.BigInteger; /** - * EtherScan - API Descriptions - * https://etherscan.io/apis#stats + * EtherScan - API Descriptions https://etherscan.io/apis#stats * * @author GoodforGod * @since 30.10.2018 @@ -18,23 +17,29 @@ 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; + @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; + @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; + @NotNull + Price lastPrice() throws ApiException; } diff --git a/src/main/java/io/api/etherscan/core/ITransactionApi.java b/src/main/java/io/api/etherscan/core/ITransactionApi.java index 3c248f3..51c108c 100644 --- a/src/main/java/io/api/etherscan/core/ITransactionApi.java +++ b/src/main/java/io/api/etherscan/core/ITransactionApi.java @@ -7,8 +7,7 @@ import java.util.Optional; /** - * EtherScan - API Descriptions - * https://etherscan.io/apis#transactions + * EtherScan - API Descriptions https://etherscan.io/apis#transactions * * @author GoodforGod * @since 30.10.2018 @@ -16,18 +15,24 @@ public interface ITransactionApi { /** - * Check Contract Execution Status (if there was an error during contract execution) + * Check Contract Execution Status (if there was an error during contract + * execution) + * * @param txhash transaction hash * @return optional status result * @throws ApiException parent exception class */ - @NotNull Optional execStatus(String txhash) throws ApiException; + @NotNull + Optional execStatus(String txhash) throws ApiException; /** - * Check Transaction Receipt Status (Only applicable for Post Byzantium fork transactions) + * Check Transaction Receipt Status (Only applicable for Post Byzantium fork + * transactions) + * * @param txhash transaction hash * @return 0 = Fail, 1 = Pass, empty value for pre-byzantium fork * @throws ApiException parent exception class */ - @NotNull Optional receiptStatus(String txhash) throws ApiException; + @NotNull + Optional receiptStatus(String txhash) throws ApiException; } diff --git a/src/main/java/io/api/etherscan/core/impl/AccountApiProvider.java b/src/main/java/io/api/etherscan/core/impl/AccountApiProvider.java index 195d0f0..b512019 100644 --- a/src/main/java/io/api/etherscan/core/impl/AccountApiProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/AccountApiProvider.java @@ -62,7 +62,7 @@ public Balance balance(final String address) throws ApiException { 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.getMessage() + ", with status " + response.getStatus()); + throw new EtherScanException(response); return new Balance(address, new BigInteger(response.getResult())); } @@ -76,7 +76,7 @@ public TokenBalance balance(final String address, final String contract) throws 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.getMessage() + ", with status " + response.getStatus()); + throw new EtherScanException(response); return new TokenBalance(address, new BigInteger(response.getResult()), contract); } @@ -97,7 +97,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.getMessage() + ", with status " + response.getStatus()); + throw new EtherScanException(response); if (!BasicUtils.isEmpty(response.getResult())) balances.addAll(response.getResult().stream() @@ -138,8 +138,7 @@ public List txs(final String address, final long startBlock, final long endB } /** - * Generic search for txs using offset api param - * To avoid 10k limit per response + * Generic search for txs using offset api param To avoid 10k limit per response * * @param urlParams Url params for #getRequest() * @param tClass responseListTO class @@ -147,8 +146,8 @@ public List txs(final String address, final long startBlock, final long endB * @param responseListTO type * @return List of T values */ - private List getRequestUsingOffset(final String urlParams, Class tClass) - throws ApiException { + private List getRequestUsingOffset(final String urlParams, + 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 d242a76..f2be0d2 100644 --- a/src/main/java/io/api/etherscan/core/impl/BasicProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/BasicProvider.java @@ -1,15 +1,20 @@ package io.api.etherscan.core.impl; import com.google.gson.Gson; -import com.google.gson.JsonSyntaxException; +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.manager.impl.QueueManager; +import io.api.etherscan.model.utility.StringResponseTO; import io.api.etherscan.util.BasicUtils; +import java.time.LocalTime; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; /** * Base provider for API Implementations @@ -20,7 +25,9 @@ */ abstract class BasicProvider { - static final int MAX_END_BLOCK = 999999999; + private static final Logger logger = Logger.getLogger(QueueManager.class.getName()); + + static final int MAX_END_BLOCK = Integer.MAX_VALUE; static final int MIN_START_BLOCK = 0; static final String ACT_PREFIX = "&action="; @@ -44,27 +51,34 @@ abstract class BasicProvider { T convert(final String json, final Class tClass) { try { - return gson.fromJson(json, tClass); - } catch (Exception e) { - if (e instanceof JsonSyntaxException) { - Map map = gson.fromJson(json, Map.class); - Object statusCode = map.get("status"); - if ((statusCode instanceof String) && (statusCode.equals("0"))) { - Object message = map.get("message"); - if ((message instanceof String) && (message.equals("NOTOK"))) { - Object result = map.get("result"); - if ((result instanceof String) && (result.equals("Max rate limit reached"))) { - throw new RateLimitException ("Max rate limit reached"); - } - } + final T t = gson.fromJson(json, tClass); + if (t instanceof StringResponseTO) { + if (((StringResponseTO) t).getResult().startsWith("Max rate limit reached")) { + throw new RateLimitException(((StringResponseTO) t).getResult()); } } - throw new ParseException(e.getMessage(), e.getCause(), json); + + 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) { + logger.log(Level.SEVERE, "ASKED - " + LocalTime.now()); queue.takeTurn(); + logger.log(Level.SEVERE, "GRANTED - " + LocalTime.now()); final String url = baseUrl + module + urlParameters; final String result = executor.get(url); if (BasicUtils.isEmpty(result)) @@ -74,7 +88,9 @@ String getRequest(final String urlParameters) { } String postRequest(final String urlParameters, final String dataToPost) { + logger.log(Level.SEVERE, "ASKED - " + LocalTime.now()); queue.takeTurn(); + logger.log(Level.SEVERE, "GRANTED - " + LocalTime.now()); final String url = baseUrl + module + urlParameters; return executor.post(url, dataToPost); } 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 d076e18..9f386a7 100644 --- a/src/main/java/io/api/etherscan/core/impl/BlockApiProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/BlockApiProvider.java @@ -36,7 +36,7 @@ public class BlockApiProvider extends BasicProvider implements IBlockApi { public Optional uncles(long blockNumber) throws ApiException { final String urlParam = ACT_BLOCK_PARAM + BLOCKNO_PARAM + blockNumber; final String response = getRequest(urlParam); - if(BasicUtils.isEmpty(response) || response.contains("NOTOK")) + if (BasicUtils.isEmpty(response) || response.contains("NOTOK")) return Optional.empty(); final UncleBlockResponseTO responseTO = convert(response, UncleBlockResponseTO.class); 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 83a6e0a..125087f 100644 --- a/src/main/java/io/api/etherscan/core/impl/ContractApiProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/ContractApiProvider.java @@ -37,8 +37,8 @@ public Abi contractAbi(final String address) throws ApiException { 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.getMessage() + ", with status " + response.getStatus()); + if (response.getStatus() != 1 && "NOTOK".equals(response.getMessage())) + throw new EtherScanException(response); return (response.getResult().startsWith("Contract sou")) ? Abi.nonVerified() 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 1f4d0b4..789794f 100644 --- a/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java +++ b/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java @@ -15,8 +15,7 @@ import java.util.function.Supplier; /** - * EtherScan full API Description - * https://etherscan.io/apis + * EtherScan full API Description https://etherscan.io/apis * * @author GoodforGod * @since 28.10.2018 @@ -25,6 +24,8 @@ public class EtherScanApi { private static final Supplier DEFAULT_SUPPLIER = HttpExecutor::new; + public static final String DEFAULT_KEY = "YourApiKeyToken"; + private final IAccountApi account; private final IBlockApi block; private final IContractApi contract; @@ -34,11 +35,11 @@ public class EtherScanApi { private final ITransactionApi txs; public EtherScanApi() { - this("YourApiKeyToken", EthNetwork.MAINNET); + this(DEFAULT_KEY, EthNetwork.MAINNET); } public EtherScanApi(final EthNetwork network) { - this("YourApiKeyToken", network); + this(DEFAULT_KEY, network); } public EtherScanApi(final String apiKey) { @@ -47,7 +48,13 @@ public EtherScanApi(final String apiKey) { public EtherScanApi(final EthNetwork network, final Supplier executorSupplier) { - this("YourApiKeyToken", network, 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, @@ -58,29 +65,36 @@ public EtherScanApi(final String apiKey, public EtherScanApi(final String apiKey, final EthNetwork network, final Supplier executorSupplier) { + this(apiKey, network, executorSupplier, + DEFAULT_KEY.equals(apiKey) + ? new QueueManager(1, 6) + : 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) + if (network == null) throw new ApiException("Ethereum Network is set to NULL value"); - // EtherScan 5request\sec limit support by queue manager - final IQueueManager masterQueue = (apiKey.equals("YourApiKeyToken")) - ? new FakeQueueManager() - : new QueueManager(5, 1); - + // 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.account = new AccountApiProvider(masterQueue, baseUrl, executor); - this.block = new BlockApiProvider(masterQueue, baseUrl, executor); - this.contract = new ContractApiProvider(masterQueue, baseUrl, executor); - this.logs = new LogsApiProvider(masterQueue, baseUrl, executor); - this.proxy = new ProxyApiProvider(masterQueue, baseUrl, executor); - this.stats = new StatisticApiProvider(masterQueue, baseUrl, executor); - this.txs = new TransactionApiProvider(masterQueue, baseUrl, executor); + 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 diff --git a/src/main/java/io/api/etherscan/core/impl/EventsApiProvider.java b/src/main/java/io/api/etherscan/core/impl/EventsApiProvider.java index b99a934..d6bfa25 100644 --- a/src/main/java/io/api/etherscan/core/impl/EventsApiProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/EventsApiProvider.java @@ -1,13 +1,10 @@ package io.api.etherscan.core.impl; import io.api.etherscan.core.IEventsApi; -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.event.IEvent; -import io.api.etherscan.model.event.impl.Event; import io.api.etherscan.model.query.impl.LogQuery; import io.api.etherscan.model.utility.LogResponseTO; import io.api.etherscan.util.BasicUtils; @@ -41,8 +38,8 @@ public List events(final LogQuery query) throws ApiException { BasicUtils.validateTxResponse(response); if (BasicUtils.isEmpty(response.getResult())) { - return Collections.emptyList(); - }; + return Collections.emptyList(); + } ; return response .getResult() .stream() 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 f2376d6..cb0c6a5 100644 --- a/src/main/java/io/api/etherscan/core/impl/ProxyApiProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/ProxyApiProvider.java @@ -62,7 +62,7 @@ public class ProxyApiProvider extends BasicProvider implements IProxyApi { ProxyApiProvider(final IQueueManager queue, final String baseUrl, final IHttpExecutor executor) { - super(queue, "proxy", baseUrl,executor); + super(queue, "proxy", baseUrl, executor); } @Override @@ -111,7 +111,8 @@ public Optional tx(final long blockNo, final long index) throws ApiExce final long compBlockNo = BasicUtils.compensateMinBlock(blockNo); final long compIndex = (index < 1) ? 1 : index; - final String urlParams = ACT_TX_BY_BLOCKNOINDEX_PARAM + TAG_PARAM + compBlockNo + INDEX_PARAM + "0x" + Long.toHexString(compIndex); + final String urlParams = ACT_TX_BY_BLOCKNOINDEX_PARAM + TAG_PARAM + compBlockNo + INDEX_PARAM + "0x" + + Long.toHexString(compIndex); final TxProxyTO response = getRequest(urlParams, TxProxyTO.class); return Optional.ofNullable(response.getResult()); } @@ -136,12 +137,12 @@ public int txSendCount(final String address) throws ApiException { @Override @NotNull public Optional txSendRaw(final String hexEncodedTx) throws ApiException { - if(BasicUtils.isNotHex(hexEncodedTx)) + if (BasicUtils.isNotHex(hexEncodedTx)) throw new InvalidDataHexException("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) + if (response.getError() != null) throw new EtherScanException("Error occurred with code " + response.getError().getCode() + " with message " + response.getError().getMessage() + ", error id " + response.getId() + ", jsonRPC " + response.getJsonrpc()); @@ -163,12 +164,12 @@ public Optional txReceipt(final String txhash) throws ApiException @Override public Optional call(final String address, final String data) throws ApiException { BasicUtils.validateAddress(address); - if(BasicUtils.isNotHex(data)) + if (BasicUtils.isNotHex(data)) throw new InvalidDataHexException("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); - return Optional.ofNullable (response.getResult()); + return Optional.ofNullable(response.getResult()); } @NotNull @@ -212,7 +213,7 @@ public BigInteger gasEstimated() throws ApiException { @NotNull @Override public BigInteger gasEstimated(final String hexData) throws ApiException { - if(!BasicUtils.isEmpty(hexData) && BasicUtils.isNotHex(hexData)) + if (!BasicUtils.isEmpty(hexData) && BasicUtils.isNotHex(hexData)) throw new InvalidDataHexException("Data is not in hex format."); final String urlParams = ACT_ESTIMATEGAS_PARAM + DATA_PARAM + hexData + GAS_PARAM + "2000000000000000"; 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 0125850..d178a81 100644 --- a/src/main/java/io/api/etherscan/core/impl/StatisticApiProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/StatisticApiProvider.java @@ -41,7 +41,7 @@ public class StatisticApiProvider extends BasicProvider implements IStatisticApi public Supply supply() throws ApiException { final StringResponseTO response = getRequest(ACT_SUPPLY_PARAM, StringResponseTO.class); if (response.getStatus() != 1) - throw new EtherScanException(response.getMessage() + ", with status " + response.getStatus()); + throw new EtherScanException(response); return new Supply(new BigInteger(response.getResult())); } @@ -54,7 +54,7 @@ public BigInteger supply(final String contract) throws ApiException { 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.getMessage() + ", with status " + response.getStatus()); + throw new EtherScanException(response); return new BigInteger(response.getResult()); } @@ -64,7 +64,7 @@ public BigInteger supply(final String contract) throws ApiException { public Price lastPrice() throws ApiException { final PriceResponseTO response = getRequest(ACT_LASTPRICE_PARAM, PriceResponseTO.class); if (response.getStatus() != 1) - throw new EtherScanException(response.getMessage() + ", with status " + response.getStatus()); + throw new EtherScanException(response); return response.getResult(); } diff --git a/src/main/java/io/api/etherscan/error/ApiException.java b/src/main/java/io/api/etherscan/error/ApiException.java index 087758e..33e4228 100644 --- a/src/main/java/io/api/etherscan/error/ApiException.java +++ b/src/main/java/io/api/etherscan/error/ApiException.java @@ -1,8 +1,6 @@ package io.api.etherscan.error; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 30.10.2018 */ diff --git a/src/main/java/io/api/etherscan/error/ApiKeyException.java b/src/main/java/io/api/etherscan/error/ApiKeyException.java index 0e2f81a..4e22934 100644 --- a/src/main/java/io/api/etherscan/error/ApiKeyException.java +++ b/src/main/java/io/api/etherscan/error/ApiKeyException.java @@ -1,8 +1,6 @@ package io.api.etherscan.error; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 05.11.2018 */ diff --git a/src/main/java/io/api/etherscan/error/ApiTimeoutException.java b/src/main/java/io/api/etherscan/error/ApiTimeoutException.java index 803731b..39b6e93 100644 --- a/src/main/java/io/api/etherscan/error/ApiTimeoutException.java +++ b/src/main/java/io/api/etherscan/error/ApiTimeoutException.java @@ -1,8 +1,6 @@ package io.api.etherscan.error; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 12.11.2018 */ diff --git a/src/main/java/io/api/etherscan/error/ConnectionException.java b/src/main/java/io/api/etherscan/error/ConnectionException.java index 410c0ac..96a881c 100644 --- a/src/main/java/io/api/etherscan/error/ConnectionException.java +++ b/src/main/java/io/api/etherscan/error/ConnectionException.java @@ -1,8 +1,6 @@ package io.api.etherscan.error; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 29.10.2018 */ diff --git a/src/main/java/io/api/etherscan/error/EtherScanException.java b/src/main/java/io/api/etherscan/error/EtherScanException.java index 3865572..cb7dd7f 100644 --- a/src/main/java/io/api/etherscan/error/EtherScanException.java +++ b/src/main/java/io/api/etherscan/error/EtherScanException.java @@ -1,13 +1,22 @@ package io.api.etherscan.error; +import io.api.etherscan.model.utility.BaseResponseTO; +import io.api.etherscan.model.utility.StringResponseTO; + /** - * ! NO DESCRIPTION ! - * * @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 index 5c3e17e..feb60be 100644 --- a/src/main/java/io/api/etherscan/error/EventModelException.java +++ b/src/main/java/io/api/etherscan/error/EventModelException.java @@ -1,6 +1,7 @@ package io.api.etherscan.error; -public class EventModelException extends ApiException { +public class EventModelException extends ApiException { + public EventModelException(String message) { super(message); } diff --git a/src/main/java/io/api/etherscan/error/InvalidAddressException.java b/src/main/java/io/api/etherscan/error/InvalidAddressException.java index b6a448c..9a0c143 100644 --- a/src/main/java/io/api/etherscan/error/InvalidAddressException.java +++ b/src/main/java/io/api/etherscan/error/InvalidAddressException.java @@ -1,8 +1,6 @@ package io.api.etherscan.error; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 29.10.2018 */ diff --git a/src/main/java/io/api/etherscan/error/InvalidDataHexException.java b/src/main/java/io/api/etherscan/error/InvalidDataHexException.java index 5b5952d..dd12cb9 100644 --- a/src/main/java/io/api/etherscan/error/InvalidDataHexException.java +++ b/src/main/java/io/api/etherscan/error/InvalidDataHexException.java @@ -1,8 +1,6 @@ package io.api.etherscan.error; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 02.11.2018 */ diff --git a/src/main/java/io/api/etherscan/error/InvalidTxHashException.java b/src/main/java/io/api/etherscan/error/InvalidTxHashException.java index fb18578..aba32c1 100644 --- a/src/main/java/io/api/etherscan/error/InvalidTxHashException.java +++ b/src/main/java/io/api/etherscan/error/InvalidTxHashException.java @@ -1,8 +1,6 @@ package io.api.etherscan.error; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 02.11.2018 */ diff --git a/src/main/java/io/api/etherscan/error/LogQueryException.java b/src/main/java/io/api/etherscan/error/LogQueryException.java index c72cd3e..504219f 100644 --- a/src/main/java/io/api/etherscan/error/LogQueryException.java +++ b/src/main/java/io/api/etherscan/error/LogQueryException.java @@ -1,8 +1,6 @@ package io.api.etherscan.error; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 31.10.2018 */ diff --git a/src/main/java/io/api/etherscan/error/ParseException.java b/src/main/java/io/api/etherscan/error/ParseException.java index f279fda..5dc6199 100644 --- a/src/main/java/io/api/etherscan/error/ParseException.java +++ b/src/main/java/io/api/etherscan/error/ParseException.java @@ -1,16 +1,19 @@ package io.api.etherscan.error; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 29.10.2018 */ public class ParseException extends ApiException { - String json; + + private final String json; public ParseException(String message, Throwable cause, String json) { super(message, cause); this.json = json; } + + public String getJson() { + return json; + } } diff --git a/src/main/java/io/api/etherscan/error/RateLimitException.java b/src/main/java/io/api/etherscan/error/RateLimitException.java index 2562342..c29f54d 100644 --- a/src/main/java/io/api/etherscan/error/RateLimitException.java +++ b/src/main/java/io/api/etherscan/error/RateLimitException.java @@ -1,8 +1,6 @@ package io.api.etherscan.error; /** - * ! NO DESCRIPTION ! - * * @author iSnow * @since 2020-10-06 */ @@ -11,5 +9,4 @@ public class RateLimitException extends ApiException { public RateLimitException(String message) { super(message); } - } diff --git a/src/main/java/io/api/etherscan/executor/IHttpExecutor.java b/src/main/java/io/api/etherscan/executor/IHttpExecutor.java index 0a47714..0c80282 100644 --- a/src/main/java/io/api/etherscan/executor/IHttpExecutor.java +++ b/src/main/java/io/api/etherscan/executor/IHttpExecutor.java @@ -19,7 +19,7 @@ public interface IHttpExecutor { /** * Performs a Http POST request * - * @param url as string + * @param url as string * @param data to post * @return result as string */ 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 c059d27..5ba39f2 100644 --- a/src/main/java/io/api/etherscan/executor/impl/HttpExecutor.java +++ b/src/main/java/io/api/etherscan/executor/impl/HttpExecutor.java @@ -65,8 +65,8 @@ public HttpExecutor(final int connectTimeout, final int readTimeout) { public HttpExecutor(final int connectTimeout, final int readTimeout, final Map headers) { - this.connectTimeout = (connectTimeout < 0) ? 0 : connectTimeout; - this.readTimeout = (readTimeout < 0) ? 0 : readTimeout; + this.connectTimeout = Math.max(connectTimeout, 0); + this.readTimeout = Math.max(readTimeout, 0); this.headers = headers; } @@ -88,9 +88,9 @@ 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()); - } else if (status >= HTTP_INTERNAL_ERROR) { - throw new ConnectionException("Server error: "+connection.getResponseMessage()); + throw new ConnectionException("Protocol error: " + connection.getResponseMessage()); + } else if (status >= HTTP_INTERNAL_ERROR) { + throw new ConnectionException("Server error: " + connection.getResponseMessage()); } final String data = readData(connection); @@ -99,7 +99,7 @@ public String get(final String urlAsString) { } catch (SocketTimeoutException e) { throw new ApiTimeoutException("Timeout: Could not establish connection for " + connectTimeout + " millis", e); } catch (Exception e) { - throw new ConnectionException(e.getLocalizedMessage(), e); + throw new ConnectionException(e.getMessage(), e); } } @@ -121,6 +121,10 @@ public String post(final String urlAsString, final String dataToPost) { final int status = connection.getResponseCode(); if (status == HTTP_MOVED_TEMP || status == HTTP_MOVED_PERM) { return post(connection.getHeaderField("Location"), dataToPost); + } else if ((status >= HTTP_BAD_REQUEST) && (status < HTTP_INTERNAL_ERROR)) { + throw new ConnectionException("Protocol error: " + connection.getResponseMessage()); + } else if (status >= HTTP_INTERNAL_ERROR) { + throw new ConnectionException("Server error: " + connection.getResponseMessage()); } final String data = readData(connection); @@ -129,7 +133,7 @@ public String post(final String urlAsString, final String dataToPost) { } catch (SocketTimeoutException e) { throw new ApiTimeoutException("Timeout: Could not establish connection for " + connectTimeout + " millis", e); } catch (Exception e) { - throw new ConnectionException(e.getLocalizedMessage(), e); + throw new ConnectionException(e.getMessage(), e); } } @@ -139,8 +143,6 @@ private String readData(final HttpURLConnection connection) throws IOException { String inputLine; while ((inputLine = in.readLine()) != null) content.append(inputLine); - - in.close(); } return content.toString(); @@ -149,11 +151,11 @@ private String readData(final HttpURLConnection connection) throws IOException { private InputStreamReader getStreamReader(final HttpURLConnection connection) throws IOException { switch (String.valueOf(connection.getContentEncoding())) { case "gzip": - return new InputStreamReader(new GZIPInputStream(connection.getInputStream()), "utf-8"); + return new InputStreamReader(new GZIPInputStream(connection.getInputStream()), StandardCharsets.UTF_8); case "deflate": - return new InputStreamReader(new InflaterInputStream(connection.getInputStream()), "utf-8"); + return new InputStreamReader(new InflaterInputStream(connection.getInputStream()), StandardCharsets.UTF_8); default: - return new InputStreamReader(connection.getInputStream(), "utf-8"); + return new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8); } } } diff --git a/src/main/java/io/api/etherscan/manager/IQueueManager.java b/src/main/java/io/api/etherscan/manager/IQueueManager.java index 3fddf19..3a65240 100644 --- a/src/main/java/io/api/etherscan/manager/IQueueManager.java +++ b/src/main/java/io/api/etherscan/manager/IQueueManager.java @@ -1,9 +1,8 @@ package io.api.etherscan.manager; /** - * Queue manager to support API limits (EtherScan 5request\sec limit) - * Managers grants turn if the limit is not exhausted - * And resets queue each set period + * Queue manager to support API limits (EtherScan 5request\sec limit) Managers + * grants turn if the limit is not exhausted And resets queue each set period * * @author GoodforGod * @since 30.10.2018 @@ -12,7 +11,6 @@ public interface IQueueManager { /** * Waits in queue for chance to take turn - * @return can or can not rake turn */ - boolean takeTurn(); + void takeTurn(); } diff --git a/src/main/java/io/api/etherscan/manager/impl/FakeQueueManager.java b/src/main/java/io/api/etherscan/manager/impl/FakeQueueManager.java index 45241af..d8bc048 100644 --- a/src/main/java/io/api/etherscan/manager/impl/FakeQueueManager.java +++ b/src/main/java/io/api/etherscan/manager/impl/FakeQueueManager.java @@ -11,7 +11,5 @@ public class FakeQueueManager implements IQueueManager { @Override - public boolean takeTurn() { - return true; - } + public 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 index f1d9f1a..cd957ee 100644 --- a/src/main/java/io/api/etherscan/manager/impl/QueueManager.java +++ b/src/main/java/io/api/etherscan/manager/impl/QueueManager.java @@ -2,15 +2,11 @@ import io.api.etherscan.manager.IQueueManager; -import java.util.List; import java.util.concurrent.*; -import java.util.logging.Logger; -import java.util.stream.Collectors; -import java.util.stream.IntStream; /** - * Queue implementation - * With size and reset time as params + * Queue implementation With size and reset time as params + * * @see IQueueManager * * @author GoodforGod @@ -18,55 +14,24 @@ */ public class QueueManager implements IQueueManager { - private static final Logger logger = Logger.getLogger(QueueManager.class.getName()); - - private final int queueSize; - private final BlockingQueue queue; - private final List queueValues; - - private final ScheduledExecutorService queueExecutor; + private final Semaphore semaphore; public QueueManager(int queueSize, int queueResetTimeInSec) { - this(queueSize, queueResetTimeInSec, 0); + this(queueSize, queueResetTimeInSec, queueResetTimeInSec); } public QueueManager(int queueSize, int queueResetTimeInSec, int delayInSec) { - this.queueSize = queueSize; - this.queueValues = generateList(queueSize); - this.queue = new ArrayBlockingQueue<>(queueSize); - - this.queueExecutor = Executors.newSingleThreadScheduledExecutor(); - this.queueExecutor.scheduleAtFixedRate(createTask(), delayInSec, queueResetTimeInSec, TimeUnit.SECONDS); + this.semaphore = new Semaphore(queueSize); + Executors.newSingleThreadScheduledExecutor() + .scheduleAtFixedRate(releaseLocks(queueSize), delayInSec, queueResetTimeInSec, TimeUnit.SECONDS); } @Override - public boolean takeTurn() { - try { - queue.take(); - return true; - } catch (InterruptedException e) { - logger.warning(e.getLocalizedMessage()); - return false; - } - } - - private Runnable createTask() { - return () -> { - try { - if(queue.size() == queueSize) - return; - - queue.clear(); - queue.addAll(queueValues); - } catch (Exception e) { - logger.warning(e.getLocalizedMessage()); - } - }; + public void takeTurn() { + semaphore.acquireUninterruptibly(); } - private List generateList(int size) { - return IntStream.range(0, size) - .boxed() - .collect(Collectors.toList()); + private Runnable releaseLocks(int toRelease) { + return () -> semaphore.release(toRelease); } } diff --git a/src/main/java/io/api/etherscan/model/Abi.java b/src/main/java/io/api/etherscan/model/Abi.java index b5203bc..a48a11d 100644 --- a/src/main/java/io/api/etherscan/model/Abi.java +++ b/src/main/java/io/api/etherscan/model/Abi.java @@ -9,6 +9,7 @@ * @since 31.10.2018 */ public class Abi { + private String contractAbi; private boolean isVerified; @@ -39,12 +40,15 @@ public boolean isVerified() { @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; Abi abi = (Abi) o; - if (isVerified != abi.isVerified) return false; + if (isVerified != abi.isVerified) + return false; return contractAbi != null ? contractAbi.equals(abi.contractAbi) : abi.contractAbi == null; } diff --git a/src/main/java/io/api/etherscan/model/Balance.java b/src/main/java/io/api/etherscan/model/Balance.java index 30cc676..5529a90 100644 --- a/src/main/java/io/api/etherscan/model/Balance.java +++ b/src/main/java/io/api/etherscan/model/Balance.java @@ -26,7 +26,7 @@ public static Balance of(BalanceTO balance) { return new Balance(balance.getAccount(), new BigInteger(balance.getBalance())); } - // + // public String getAddress() { return address; } @@ -50,16 +50,19 @@ public BigInteger getGwei() { public BigInteger getEther() { return balance.asEther(); } - // + // @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; Balance balance1 = (Balance) o; - if (!balance.equals(balance1.balance)) return false; + if (!balance.equals(balance1.balance)) + return false; return address != null ? address.equals(balance1.address) : balance1.address == null; } diff --git a/src/main/java/io/api/etherscan/model/BaseTx.java b/src/main/java/io/api/etherscan/model/BaseTx.java index af2286f..5aea827 100644 --- a/src/main/java/io/api/etherscan/model/BaseTx.java +++ b/src/main/java/io/api/etherscan/model/BaseTx.java @@ -26,13 +26,13 @@ abstract class BaseTx { private BigInteger gas; private BigInteger gasUsed; - // + // public long getBlockNumber() { return blockNumber; } public LocalDateTime getTimeStamp() { - if(_timeStamp == null && !BasicUtils.isEmpty(timeStamp)) + if (_timeStamp == null && !BasicUtils.isEmpty(timeStamp)) _timeStamp = LocalDateTime.ofEpochSecond(Long.valueOf(timeStamp), 0, ZoneOffset.UTC); return _timeStamp; } @@ -68,20 +68,27 @@ public BigInteger getGas() { public BigInteger getGasUsed() { return gasUsed; } - // + // @Override public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof BaseTx)) return false; + if (this == o) + return true; + if (!(o instanceof BaseTx)) + return false; BaseTx baseTx = (BaseTx) o; - if (blockNumber != baseTx.blockNumber) return false; - if (timeStamp != null ? !timeStamp.equals(baseTx.timeStamp) : baseTx.timeStamp != null) return false; - if (hash != null ? !hash.equals(baseTx.hash) : baseTx.hash != null) return false; - if (from != null ? !from.equals(baseTx.from) : baseTx.from != null) return false; - if (to != null ? !to.equals(baseTx.to) : baseTx.to != null) return false; + if (blockNumber != baseTx.blockNumber) + return false; + if (timeStamp != null ? !timeStamp.equals(baseTx.timeStamp) : baseTx.timeStamp != null) + return false; + if (hash != null ? !hash.equals(baseTx.hash) : baseTx.hash != null) + return false; + if (from != null ? !from.equals(baseTx.from) : baseTx.from != null) + return false; + if (to != null ? !to.equals(baseTx.to) : baseTx.to != null) + return false; return value != null ? value.equals(baseTx.value) : baseTx.value == null; } diff --git a/src/main/java/io/api/etherscan/model/Block.java b/src/main/java/io/api/etherscan/model/Block.java index 0406e4e..2e9b96b 100644 --- a/src/main/java/io/api/etherscan/model/Block.java +++ b/src/main/java/io/api/etherscan/model/Block.java @@ -19,13 +19,13 @@ public class Block { private String timeStamp; private LocalDateTime _timeStamp; - // + // public long getBlockNumber() { return blockNumber; } public LocalDateTime getTimeStamp() { - if(_timeStamp == null && !BasicUtils.isEmpty(timeStamp)) + if (_timeStamp == null && !BasicUtils.isEmpty(timeStamp)) _timeStamp = LocalDateTime.ofEpochSecond(Long.valueOf(timeStamp), 0, ZoneOffset.UTC); return _timeStamp; } @@ -33,12 +33,14 @@ public LocalDateTime getTimeStamp() { public BigInteger getBlockReward() { return blockReward; } - // + // @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; Block block = (Block) o; diff --git a/src/main/java/io/api/etherscan/model/EthNetwork.java b/src/main/java/io/api/etherscan/model/EthNetwork.java index d10aead..f7b91de 100644 --- a/src/main/java/io/api/etherscan/model/EthNetwork.java +++ b/src/main/java/io/api/etherscan/model/EthNetwork.java @@ -7,6 +7,7 @@ * @since 28.10.2018 */ public enum EthNetwork { + MAINNET("api"), ROPSTEN("api-ropsten"), KOVAN("api-kovan"), diff --git a/src/main/java/io/api/etherscan/model/Log.java b/src/main/java/io/api/etherscan/model/Log.java index 2fa58ee..2a7cc09 100644 --- a/src/main/java/io/api/etherscan/model/Log.java +++ b/src/main/java/io/api/etherscan/model/Log.java @@ -32,9 +32,9 @@ public class Log { private String logIndex; private Long _logIndex; - // + // public Long getBlockNumber() { - if(_blockNumber == null && !BasicUtils.isEmpty(blockNumber)){ + if (_blockNumber == null && !BasicUtils.isEmpty(blockNumber)) { _blockNumber = BasicUtils.parseHex(blockNumber).longValue(); } return _blockNumber; @@ -49,7 +49,7 @@ public String getTransactionHash() { } public Long getTransactionIndex() { - if(_transactionIndex == null && !BasicUtils.isEmpty(transactionIndex)){ + if (_transactionIndex == null && !BasicUtils.isEmpty(transactionIndex)) { _transactionIndex = BasicUtils.parseHex(transactionIndex).longValue(); } @@ -57,7 +57,7 @@ public Long getTransactionIndex() { } public LocalDateTime getTimeStamp() { - if(_timeStamp == null && !BasicUtils.isEmpty(timeStamp)) { + if (_timeStamp == null && !BasicUtils.isEmpty(timeStamp)) { long formatted = (timeStamp.charAt(0) == '0' && timeStamp.charAt(1) == 'x') ? BasicUtils.parseHex(timeStamp).longValue() : Long.parseLong(timeStamp); @@ -67,9 +67,11 @@ 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 + * Return the "timeStamp" field of the event record as a long-int representing + * the milliseconds since the Unix epoch (1970-01-01 00:00:00). + * + * @return milliseconds between Unix epoch and `timeStamp`. If field is empty or + * null, returns null */ public Long getTimeStampAsMillis() { if (BasicUtils.isEmpty(timeStamp)) { @@ -86,7 +88,7 @@ public String getData() { } public BigInteger getGasPrice() { - if(!BasicUtils.isEmpty(gasPrice)){ + if (!BasicUtils.isEmpty(gasPrice)) { _gasPrice = BasicUtils.parseHex(gasPrice); } @@ -94,7 +96,7 @@ public BigInteger getGasPrice() { } public BigInteger getGasUsed() { - if(!BasicUtils.isEmpty(gasUsed)){ + if (!BasicUtils.isEmpty(gasUsed)) { _gasUsed = BasicUtils.parseHex(gasUsed); } @@ -106,25 +108,30 @@ public List getTopics() { } public Long getLogIndex() { - if(_logIndex == null && !BasicUtils.isEmpty(logIndex)){ + if (_logIndex == null && !BasicUtils.isEmpty(logIndex)) { _logIndex = BasicUtils.parseHex(logIndex).longValue(); } return _logIndex; } - // + // @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; Log log = (Log) o; - if (blockNumber != null ? !blockNumber.equals(log.blockNumber) : log.blockNumber != null) return false; - if (address != null ? !address.equals(log.address) : log.address != null) return false; + if (blockNumber != null ? !blockNumber.equals(log.blockNumber) : log.blockNumber != null) + return false; + if (address != null ? !address.equals(log.address) : log.address != null) + return false; if (transactionHash != null ? !transactionHash.equals(log.transactionHash) : log.transactionHash != null) return false; - if (timeStamp != null ? !timeStamp.equals(log.timeStamp) : log.timeStamp != null) return false; + if (timeStamp != null ? !timeStamp.equals(log.timeStamp) : log.timeStamp != null) + return false; return logIndex != null ? logIndex.equals(log.logIndex) : log.logIndex == null; } diff --git a/src/main/java/io/api/etherscan/model/Price.java b/src/main/java/io/api/etherscan/model/Price.java index f9839e2..9a37592 100644 --- a/src/main/java/io/api/etherscan/model/Price.java +++ b/src/main/java/io/api/etherscan/model/Price.java @@ -27,26 +27,30 @@ public double inBtc() { } public LocalDateTime usdTimestamp() { - if(_ethusd_timestamp == null) + if (_ethusd_timestamp == null) _ethusd_timestamp = LocalDateTime.ofEpochSecond(Long.valueOf(ethusd_timestamp), 0, ZoneOffset.UTC); return _ethusd_timestamp; } public LocalDateTime btcTimestamp() { - if(_ethbtc_timestamp == null) + if (_ethbtc_timestamp == null) _ethbtc_timestamp = LocalDateTime.ofEpochSecond(Long.valueOf(ethbtc_timestamp), 0, ZoneOffset.UTC); return _ethbtc_timestamp; } @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; Price price = (Price) o; - if (Double.compare(price.ethusd, ethusd) != 0) return false; - if (Double.compare(price.ethbtc, ethbtc) != 0) return false; + if (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); diff --git a/src/main/java/io/api/etherscan/model/Status.java b/src/main/java/io/api/etherscan/model/Status.java index 71a4ebb..4a1fe18 100644 --- a/src/main/java/io/api/etherscan/model/Status.java +++ b/src/main/java/io/api/etherscan/model/Status.java @@ -24,12 +24,15 @@ public String getErrDescription() { @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; Status status = (Status) o; - if (isError != status.isError) return false; + if (isError != status.isError) + return false; return errDescription != null ? errDescription.equals(status.errDescription) : status.errDescription == null; } diff --git a/src/main/java/io/api/etherscan/model/TokenBalance.java b/src/main/java/io/api/etherscan/model/TokenBalance.java index b40ae0c..e198079 100644 --- a/src/main/java/io/api/etherscan/model/TokenBalance.java +++ b/src/main/java/io/api/etherscan/model/TokenBalance.java @@ -23,9 +23,12 @@ public String getContract() { @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; + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + if (!super.equals(o)) + return false; TokenBalance that = (TokenBalance) o; diff --git a/src/main/java/io/api/etherscan/model/Tx.java b/src/main/java/io/api/etherscan/model/Tx.java index 68f00f1..6fce75b 100644 --- a/src/main/java/io/api/etherscan/model/Tx.java +++ b/src/main/java/io/api/etherscan/model/Tx.java @@ -21,7 +21,7 @@ public class Tx extends BaseTx { private String isError; private String txreceipt_status; - // + // public long getNonce() { return nonce; } @@ -53,19 +53,25 @@ public BigInteger getCumulativeGasUsed() { public long getConfirmations() { return confirmations; } - // + // @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; + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + if (!super.equals(o)) + return false; Tx tx = (Tx) o; - if (nonce != tx.nonce) return false; - if (transactionIndex != tx.transactionIndex) return false; - if (blockHash != null ? !blockHash.equals(tx.blockHash) : tx.blockHash != null) return false; + if (nonce != tx.nonce) + return false; + if (transactionIndex != tx.transactionIndex) + return false; + if (blockHash != null ? !blockHash.equals(tx.blockHash) : tx.blockHash != null) + return false; return isError != null ? isError.equals(tx.isError) : tx.isError == null; } diff --git a/src/main/java/io/api/etherscan/model/TxInternal.java b/src/main/java/io/api/etherscan/model/TxInternal.java index 1d9d8a8..e7b75d9 100644 --- a/src/main/java/io/api/etherscan/model/TxInternal.java +++ b/src/main/java/io/api/etherscan/model/TxInternal.java @@ -13,7 +13,7 @@ public class TxInternal extends BaseTx { private int isError; private String errCode; - // + // public String getType() { return type; } @@ -29,17 +29,21 @@ public boolean haveError() { public String getErrCode() { return errCode; } - // + // @Override public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof TxInternal)) return false; - if (!super.equals(o)) return false; + if (this == o) + return true; + if (!(o instanceof TxInternal)) + return false; + if (!super.equals(o)) + return false; TxInternal that = (TxInternal) o; - if (traceId != that.traceId) return false; + if (traceId != that.traceId) + return false; return errCode != null ? errCode.equals(that.errCode) : that.errCode == null; } diff --git a/src/main/java/io/api/etherscan/model/TxToken.java b/src/main/java/io/api/etherscan/model/TxToken.java index 985066b..8f5e36f 100644 --- a/src/main/java/io/api/etherscan/model/TxToken.java +++ b/src/main/java/io/api/etherscan/model/TxToken.java @@ -18,7 +18,7 @@ public class TxToken extends BaseTx { private long cumulativeGasUsed; private long confirmations; - // + // public long getNonce() { return nonce; } @@ -54,7 +54,7 @@ public long getCumulativeGasUsed() { public long getConfirmations() { return confirmations; } - // + // @Override public String toString() { diff --git a/src/main/java/io/api/etherscan/model/Uncle.java b/src/main/java/io/api/etherscan/model/Uncle.java index bf5145d..2ee206b 100644 --- a/src/main/java/io/api/etherscan/model/Uncle.java +++ b/src/main/java/io/api/etherscan/model/Uncle.java @@ -14,7 +14,7 @@ public class Uncle { private BigInteger blockreward; private int unclePosition; - // + // public String getMiner() { return miner; } @@ -26,17 +26,21 @@ public BigInteger getBlockreward() { public int getUnclePosition() { return unclePosition; } - // + // @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; Uncle uncle = (Uncle) o; - if (unclePosition != uncle.unclePosition) return false; - if (miner != null ? !miner.equals(uncle.miner) : uncle.miner != null) return false; + 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; } diff --git a/src/main/java/io/api/etherscan/model/UncleBlock.java b/src/main/java/io/api/etherscan/model/UncleBlock.java index 6b7eb4f..88c975d 100644 --- a/src/main/java/io/api/etherscan/model/UncleBlock.java +++ b/src/main/java/io/api/etherscan/model/UncleBlock.java @@ -16,7 +16,7 @@ public class UncleBlock extends Block { private List uncles; private String uncleInclusionReward; - // + // public boolean isEmpty() { return getBlockNumber() == 0 && getBlockReward() == null && getTimeStamp() == null @@ -34,13 +34,16 @@ public List getUncles() { 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; + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + if (!super.equals(o)) + return false; UncleBlock that = (UncleBlock) o; diff --git a/src/main/java/io/api/etherscan/model/Wei.java b/src/main/java/io/api/etherscan/model/Wei.java index cf5f465..35f90d0 100644 --- a/src/main/java/io/api/etherscan/model/Wei.java +++ b/src/main/java/io/api/etherscan/model/Wei.java @@ -16,7 +16,7 @@ public Wei(BigInteger value) { this.result = value; } - // + // public BigInteger getValue() { return result; } @@ -36,12 +36,14 @@ public BigInteger asGwei() { public BigInteger asEther() { return result.divide(BigInteger.valueOf(1000000000000000L)); } - // + // @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; Wei wei = (Wei) o; diff --git a/src/main/java/io/api/etherscan/model/event/IEvent.java b/src/main/java/io/api/etherscan/model/event/IEvent.java index 0fb0e65..47e2e2c 100644 --- a/src/main/java/io/api/etherscan/model/event/IEvent.java +++ b/src/main/java/io/api/etherscan/model/event/IEvent.java @@ -8,6 +8,7 @@ import java.util.Map; public interface IEvent { + static final Map> subTypes = new HashMap<>(); void setLog(Log log); diff --git a/src/main/java/io/api/etherscan/model/event/impl/ApprovalEvent.java b/src/main/java/io/api/etherscan/model/event/impl/ApprovalEvent.java index 915b99c..ba52faf 100644 --- a/src/main/java/io/api/etherscan/model/event/impl/ApprovalEvent.java +++ b/src/main/java/io/api/etherscan/model/event/impl/ApprovalEvent.java @@ -3,6 +3,7 @@ import io.api.etherscan.model.event.IEvent; public class ApprovalEvent extends Event { + static final String eventTypeHash = "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"; static { IEvent.registerEventType(ApprovalEvent.eventTypeHash, ApprovalEvent.class); diff --git a/src/main/java/io/api/etherscan/model/event/impl/DepositEvent.java b/src/main/java/io/api/etherscan/model/event/impl/DepositEvent.java index 31343a5..fe3ad06 100644 --- a/src/main/java/io/api/etherscan/model/event/impl/DepositEvent.java +++ b/src/main/java/io/api/etherscan/model/event/impl/DepositEvent.java @@ -2,7 +2,8 @@ import io.api.etherscan.model.event.IEvent; -public class DepositEvent extends Event { +public class DepositEvent extends Event { + static final String eventTypeHash = "0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c"; static { IEvent.registerEventType(DepositEvent.eventTypeHash, DepositEvent.class); diff --git a/src/main/java/io/api/etherscan/model/event/impl/Event.java b/src/main/java/io/api/etherscan/model/event/impl/Event.java index dbcffcd..6f30e25 100644 --- a/src/main/java/io/api/etherscan/model/event/impl/Event.java +++ b/src/main/java/io/api/etherscan/model/event/impl/Event.java @@ -1,15 +1,11 @@ package io.api.etherscan.model.event.impl; -import io.api.etherscan.error.ApiException; -import io.api.etherscan.error.EventModelException; import io.api.etherscan.model.Log; import io.api.etherscan.model.event.IEvent; -import java.util.HashMap; -import java.util.Map; - /** - * Base class for a higher-level API on top of {@link Log}. Each Event class has an identifying hash + * Base class for a higher-level API on top of {@link Log}. Each Event class has + * an identifying hash */ public class Event implements IEvent { diff --git a/src/main/java/io/api/etherscan/model/event/impl/MintEvent.java b/src/main/java/io/api/etherscan/model/event/impl/MintEvent.java index 597dd7e..cd3b6d5 100644 --- a/src/main/java/io/api/etherscan/model/event/impl/MintEvent.java +++ b/src/main/java/io/api/etherscan/model/event/impl/MintEvent.java @@ -2,7 +2,8 @@ import io.api.etherscan.model.event.IEvent; -public class MintEvent extends Event { +public class MintEvent extends Event { + static final String eventTypeHash = "0x4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f"; static { IEvent.registerEventType(MintEvent.eventTypeHash, MintEvent.class); diff --git a/src/main/java/io/api/etherscan/model/event/impl/SyncEvent.java b/src/main/java/io/api/etherscan/model/event/impl/SyncEvent.java index ff566f4..76b4931 100644 --- a/src/main/java/io/api/etherscan/model/event/impl/SyncEvent.java +++ b/src/main/java/io/api/etherscan/model/event/impl/SyncEvent.java @@ -3,6 +3,7 @@ import io.api.etherscan.model.event.IEvent; public class SyncEvent extends Event { + static final String eventTypeHash = "0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1"; static { IEvent.registerEventType(SyncEvent.eventTypeHash, SyncEvent.class); diff --git a/src/main/java/io/api/etherscan/model/event/impl/TransferErc20Event.java b/src/main/java/io/api/etherscan/model/event/impl/TransferErc20Event.java index a1a8be0..ce16958 100644 --- a/src/main/java/io/api/etherscan/model/event/impl/TransferErc20Event.java +++ b/src/main/java/io/api/etherscan/model/event/impl/TransferErc20Event.java @@ -3,6 +3,7 @@ import io.api.etherscan.model.event.IEvent; public class TransferErc20Event extends Event { + static final String eventTypeHash = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"; static { IEvent.registerEventType(TransferErc20Event.eventTypeHash, TransferErc20Event.class); diff --git a/src/main/java/io/api/etherscan/model/event/impl/WithdrawEvent.java b/src/main/java/io/api/etherscan/model/event/impl/WithdrawEvent.java index 3fa442b..23036bf 100644 --- a/src/main/java/io/api/etherscan/model/event/impl/WithdrawEvent.java +++ b/src/main/java/io/api/etherscan/model/event/impl/WithdrawEvent.java @@ -2,7 +2,8 @@ import io.api.etherscan.model.event.IEvent; -public class WithdrawEvent extends Event { +public class WithdrawEvent extends Event { + static final String eventTypeHash = "0x7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65"; static { IEvent.registerEventType(WithdrawEvent.eventTypeHash, WithdrawEvent.class); 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 414a797..3d7ddd3 100644 --- a/src/main/java/io/api/etherscan/model/proxy/BlockProxy.java +++ b/src/main/java/io/api/etherscan/model/proxy/BlockProxy.java @@ -44,9 +44,9 @@ public class BlockProxy { private String transactionsRoot; private List transactions; - // + // public Long getNumber() { - if(_number == null && !BasicUtils.isEmpty(number)) + if (_number == null && !BasicUtils.isEmpty(number)) _number = BasicUtils.parseHex(number).longValue(); return _number; } @@ -64,7 +64,7 @@ public String getStateRoot() { } public Long getSize() { - if(_size == null && !BasicUtils.isEmpty(size)) + if (_size == null && !BasicUtils.isEmpty(size)) _size = BasicUtils.parseHex(size).longValue(); return _size; } @@ -78,7 +78,7 @@ public String getTotalDifficulty() { } public LocalDateTime getTimeStamp() { - if(_timestamp == null && !BasicUtils.isEmpty(timestamp)) + if (_timestamp == null && !BasicUtils.isEmpty(timestamp)) _timestamp = LocalDateTime.ofEpochSecond(BasicUtils.parseHex(timestamp).longValue(), 0, ZoneOffset.UTC); return _timestamp; } @@ -104,13 +104,13 @@ public String getMixHash() { } public BigInteger getGasUsed() { - if(_gasUsed == null && !BasicUtils.isEmpty(gasUsed)) + if (_gasUsed == null && !BasicUtils.isEmpty(gasUsed)) _gasUsed = BasicUtils.parseHex(gasUsed); return _gasUsed; } public BigInteger getGasLimit() { - if(_gasLimit == null && !BasicUtils.isEmpty(gasLimit)) + if (_gasLimit == null && !BasicUtils.isEmpty(gasLimit)) _gasLimit = BasicUtils.parseHex(gasLimit); return _gasLimit; } @@ -134,17 +134,21 @@ public String getTransactionsRoot() { public List getTransactions() { return transactions; } - // + // @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; 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; + 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; } 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 b6e2bcc..d69a627 100644 --- a/src/main/java/io/api/etherscan/model/proxy/ReceiptProxy.java +++ b/src/main/java/io/api/etherscan/model/proxy/ReceiptProxy.java @@ -32,7 +32,7 @@ public class ReceiptProxy { private List logs; private String logsBloom; - // + // public String getRoot() { return root; } @@ -46,7 +46,7 @@ public String getTo() { } public Long getBlockNumber() { - if(_blockNumber == null && !BasicUtils.isEmpty(blockNumber)) + if (_blockNumber == null && !BasicUtils.isEmpty(blockNumber)) _blockNumber = BasicUtils.parseHex(blockNumber).longValue(); return _blockNumber; } @@ -60,19 +60,19 @@ public String getTransactionHash() { } public Long getTransactionIndex() { - if(_transactionIndex == null && !BasicUtils.isEmpty(transactionIndex)) + if (_transactionIndex == null && !BasicUtils.isEmpty(transactionIndex)) _transactionIndex = BasicUtils.parseHex(transactionIndex).longValue(); return _transactionIndex; } public BigInteger getGasUsed() { - if(_gasUsed == null && !BasicUtils.isEmpty(gasUsed)) + if (_gasUsed == null && !BasicUtils.isEmpty(gasUsed)) _gasUsed = BasicUtils.parseHex(gasUsed); return _gasUsed; } public BigInteger getCumulativeGasUsed() { - if(_cumulativeGasUsed == null && !BasicUtils.isEmpty(cumulativeGasUsed)) + if (_cumulativeGasUsed == null && !BasicUtils.isEmpty(cumulativeGasUsed)) _cumulativeGasUsed = BasicUtils.parseHex(cumulativeGasUsed); return _cumulativeGasUsed; } @@ -88,16 +88,19 @@ public List getLogs() { public String getLogsBloom() { return logsBloom; } - // + // @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; ReceiptProxy that = (ReceiptProxy) o; - if (blockNumber != null ? !blockNumber.equals(that.blockNumber) : that.blockNumber != null) return false; + 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; 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 c45e679..25b50c8 100644 --- a/src/main/java/io/api/etherscan/model/proxy/TxProxy.java +++ b/src/main/java/io/api/etherscan/model/proxy/TxProxy.java @@ -32,7 +32,7 @@ public class TxProxy { private String blockNumber; private Long _blockNumber; - // + // public String getTo() { return to; } @@ -42,7 +42,7 @@ public String getHash() { } public Long getTransactionIndex() { - if(_transactionIndex == null && !BasicUtils.isEmpty(transactionIndex)) + if (_transactionIndex == null && !BasicUtils.isEmpty(transactionIndex)) _transactionIndex = BasicUtils.parseHex(transactionIndex).longValue(); return _transactionIndex; } @@ -52,7 +52,7 @@ public String getFrom() { } public BigInteger getGas() { - if(_gas == null && !BasicUtils.isEmpty(gas)) + if (_gas == null && !BasicUtils.isEmpty(gas)) _gas = BasicUtils.parseHex(gas); return _gas; } @@ -74,7 +74,7 @@ public String getR() { } public Long getNonce() { - if(_nonce == null && !BasicUtils.isEmpty(nonce)) + if (_nonce == null && !BasicUtils.isEmpty(nonce)) _nonce = BasicUtils.parseHex(nonce).longValue(); return _nonce; } @@ -84,7 +84,7 @@ public String getValue() { } public BigInteger getGasPrice() { - if(_gasPrice == null && !BasicUtils.isEmpty(gasPrice)) + if (_gasPrice == null && !BasicUtils.isEmpty(gasPrice)) _gasPrice = BasicUtils.parseHex(gasPrice); return _gasPrice; } @@ -94,21 +94,25 @@ public String getBlockHash() { } public Long getBlockNumber() { - if(_blockNumber == null && !BasicUtils.isEmpty(blockNumber)) + if (_blockNumber == null && !BasicUtils.isEmpty(blockNumber)) _blockNumber = BasicUtils.parseHex(blockNumber).longValue(); return _blockNumber; } - // + // @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; 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; + 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; } diff --git a/src/main/java/io/api/etherscan/model/query/IQueryBuilder.java b/src/main/java/io/api/etherscan/model/query/IQueryBuilder.java index 17bc2f9..6a76c62 100644 --- a/src/main/java/io/api/etherscan/model/query/IQueryBuilder.java +++ b/src/main/java/io/api/etherscan/model/query/IQueryBuilder.java @@ -10,5 +10,6 @@ * @since 31.10.2018 */ public interface IQueryBuilder { + LogQuery build() throws LogQueryException; } diff --git a/src/main/java/io/api/etherscan/model/query/LogOp.java b/src/main/java/io/api/etherscan/model/query/LogOp.java index 5c6138c..0c0ebee 100644 --- a/src/main/java/io/api/etherscan/model/query/LogOp.java +++ b/src/main/java/io/api/etherscan/model/query/LogOp.java @@ -7,6 +7,7 @@ * @since 31.10.2018 */ public enum LogOp { + AND("and"), OR("or"); 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 ae429b0..3ba6c4f 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 @@ -5,8 +5,7 @@ /** * Final builded container for The Event Log API * - * EtherScan - API Descriptions - * https://etherscan.io/apis#logs + * EtherScan - API Descriptions https://etherscan.io/apis#logs * * @see LogQueryBuilder * @see ILogsApi 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 1397f15..bd8a9fc 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 @@ -41,37 +41,37 @@ public static LogQueryBuilder with(String address, long startBlock, long endBloc } public LogTopicSingle topic(String topic0) { - if(BasicUtils.isNotHex(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)) + if (BasicUtils.isNotHex(topic0)) throw new LogQueryException("topic0 can not be empty or non hex."); - if(BasicUtils.isNotHex(topic1)) + 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)) + if (BasicUtils.isNotHex(topic0)) throw new LogQueryException("topic0 can not be empty or non hex."); - if(BasicUtils.isNotHex(topic1)) + if (BasicUtils.isNotHex(topic1)) throw new LogQueryException("topic1 can not be empty or non hex."); - if(BasicUtils.isNotHex(topic2)) + 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)) + if (BasicUtils.isNotHex(topic0)) throw new LogQueryException("topic0 can not be empty or non hex."); - if(BasicUtils.isNotHex(topic1)) + if (BasicUtils.isNotHex(topic1)) throw new LogQueryException("topic1 can not be empty or non hex."); - if(BasicUtils.isNotHex(topic2)) + if (BasicUtils.isNotHex(topic2)) throw new LogQueryException("topic2 can not be empty or non hex."); - if(BasicUtils.isNotHex(topic3)) + 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); 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 22774f2..cbc5a3e 100644 --- a/src/main/java/io/api/etherscan/model/utility/BlockParam.java +++ b/src/main/java/io/api/etherscan/model/utility/BlockParam.java @@ -7,6 +7,7 @@ * @since 31.10.2018 */ public class BlockParam { + private long startBlock; private long endBlock; diff --git a/src/main/java/io/api/etherscan/util/BasicUtils.java b/src/main/java/io/api/etherscan/util/BasicUtils.java index cec41e5..0d2a654 100644 --- a/src/main/java/io/api/etherscan/util/BasicUtils.java +++ b/src/main/java/io/api/etherscan/util/BasicUtils.java @@ -108,9 +108,10 @@ public static void validateTxResponse(T response) { if (response.getStatus() != 1) { if (response.getMessage() == null) { - throw new EtherScanException("Unexpected Etherscan exception, no information from server about error, code " + response.getStatus()); + throw new EtherScanException( + "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.getMessage() + ", with status " + response.getStatus()); + throw new EtherScanException(response); } } } From 268e5e957c098a166b1131053d925e2e04afb313 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 18 Oct 2020 15:52:18 +0300 Subject: [PATCH 029/112] [1.1.0-SNAPSHOT] ApiRunner added All tests refactored to use ApiRunner ApiRunner correct key retrival and queue set added All tests fixed and improved --- .../api/etherscan/core/impl/EtherScanApi.java | 2 +- .../etherscan/manager/impl/QueueManager.java | 4 ++ src/test/java/io/api/ApiRunner.java | 46 ++++++++++++++++++ .../io/api/etherscan/EtherScanApiTest.java | 20 +++----- .../account/AccountBalanceListTest.java | 33 +++++-------- .../etherscan/account/AccountBalanceTest.java | 40 ++++------------ .../account/AccountMinedBlocksTest.java | 31 +++++------- .../account/AccountTokenBalanceTest.java | 48 +++++-------------- .../account/AccountTxInternalByHashTest.java | 44 +++++------------ .../account/AccountTxInternalTest.java | 17 +++---- .../etherscan/account/AccountTxTokenTest.java | 21 ++++---- .../api/etherscan/account/AccountTxsTest.java | 17 +++---- .../io/api/etherscan/block/BlockApiTest.java | 17 +++---- .../etherscan/contract/ContractApiTest.java | 13 ++--- .../etherscan/logs/LogQueryBuilderTest.java | 4 +- .../io/api/etherscan/logs/LogsApiTest.java | 25 +++++----- .../etherscan/proxy/ProxyBlockApiTest.java | 13 ++--- .../proxy/ProxyBlockLastNoApiTest.java | 9 ++-- .../proxy/ProxyBlockUncleApiTest.java | 13 ++--- .../api/etherscan/proxy/ProxyCallApiTest.java | 15 +++--- .../api/etherscan/proxy/ProxyCodeApiTest.java | 13 ++--- .../api/etherscan/proxy/ProxyGasApiTest.java | 17 +++---- .../etherscan/proxy/ProxyStorageApiTest.java | 13 ++--- .../api/etherscan/proxy/ProxyTxApiTest.java | 17 +++---- .../etherscan/proxy/ProxyTxCountApiTest.java | 17 +++---- .../proxy/ProxyTxReceiptApiTest.java | 16 +++---- .../proxy/ProxyTxSendRawApiTest.java | 18 ++++--- .../statistic/StatisticPriceApiTest.java | 9 ++-- .../statistic/StatisticSupplyApiTest.java | 9 ++-- .../StatisticTokenSupplyApiTest.java | 15 +++--- .../transaction/TransactionExecApiTest.java | 13 ++--- .../TransactionReceiptApiTest.java | 15 +++--- .../java/io/api/manager/QueueManagerTest.java | 24 +++++----- .../java/io/api/util/BasicUtilsTests.java | 4 +- 34 files changed, 266 insertions(+), 366 deletions(-) create mode 100644 src/test/java/io/api/ApiRunner.java 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 789794f..0dd0f9e 100644 --- a/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java +++ b/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java @@ -67,7 +67,7 @@ public EtherScanApi(final String apiKey, final Supplier executorSupplier) { this(apiKey, network, executorSupplier, DEFAULT_KEY.equals(apiKey) - ? new QueueManager(1, 6) + ? QueueManager.DEFAULT_KEY_QUEUE : new FakeQueueManager() ); } 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 cd957ee..a9a32dd 100644 --- a/src/main/java/io/api/etherscan/manager/impl/QueueManager.java +++ b/src/main/java/io/api/etherscan/manager/impl/QueueManager.java @@ -1,5 +1,6 @@ package io.api.etherscan.manager.impl; +import io.api.etherscan.core.impl.EtherScanApi; import io.api.etherscan.manager.IQueueManager; import java.util.concurrent.*; @@ -14,6 +15,9 @@ */ public class QueueManager implements IQueueManager { + public static final QueueManager DEFAULT_KEY_QUEUE = new QueueManager(1, 6); + public static final QueueManager PERSONAL_KEY_QUEUE = new QueueManager(5, 1); + private final Semaphore semaphore; public QueueManager(int queueSize, int queueResetTimeInSec) { diff --git a/src/test/java/io/api/ApiRunner.java b/src/test/java/io/api/ApiRunner.java new file mode 100644 index 0000000..75b3e4e --- /dev/null +++ b/src/test/java/io/api/ApiRunner.java @@ -0,0 +1,46 @@ +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.Assert; + +public class ApiRunner extends Assert { + + private static final EtherScanApi api; + private static final EtherScanApi apiRopsten; + private static final EtherScanApi apiRinkeby; + private static final EtherScanApi apiKovan; + + static { + final String apiKey = System.getenv("API_KEY"); + final String keyOrDefault = (apiKey == null || apiKey.isEmpty()) + ? EtherScanApi.DEFAULT_KEY + : apiKey; + + final QueueManager queue = keyOrDefault.equals(EtherScanApi.DEFAULT_KEY) + ? QueueManager.DEFAULT_KEY_QUEUE + : QueueManager.PERSONAL_KEY_QUEUE; + + api = new EtherScanApi(keyOrDefault, EthNetwork.MAINNET, queue); + apiRopsten = new EtherScanApi(keyOrDefault, EthNetwork.ROPSTEN, queue); + apiRinkeby = new EtherScanApi(keyOrDefault, EthNetwork.RINKEBY, queue); + apiKovan = new EtherScanApi(keyOrDefault, EthNetwork.KOVAN, queue); + } + + public static EtherScanApi getApi() { + return api; + } + + public static EtherScanApi getApiRopsten() { + return apiRopsten; + } + + public static EtherScanApi getApiRinkeby() { + return apiRinkeby; + } + + public static EtherScanApi getApiKovan() { + return apiKovan; + } +} diff --git a/src/test/java/io/api/etherscan/EtherScanApiTest.java b/src/test/java/io/api/etherscan/EtherScanApiTest.java index 2d0d978..c478b59 100644 --- a/src/test/java/io/api/etherscan/EtherScanApiTest.java +++ b/src/test/java/io/api/etherscan/EtherScanApiTest.java @@ -1,5 +1,6 @@ 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; @@ -9,22 +10,19 @@ import io.api.etherscan.model.Balance; import io.api.etherscan.model.Block; import io.api.etherscan.model.EthNetwork; -import org.junit.Assert; import org.junit.Test; import java.util.List; import java.util.function.Supplier; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 05.11.2018 */ -public class EtherScanApiTest extends Assert { +public class EtherScanApiTest extends ApiRunner { - private EthNetwork network = EthNetwork.KOVAN; - private String validKey = "YourKey"; + private final EthNetwork network = EthNetwork.KOVAN; + private final String validKey = "YourKey"; @Test public void validKey() { @@ -59,24 +57,20 @@ public void noTimeoutOnRead() { @Test public void noTimeoutOnReadGroli() { Supplier supplier = () -> new HttpExecutor(300); - EtherScanApi api = new EtherScanApi(EthNetwork.GORLI, supplier); - Balance balance = api.account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); + Balance balance = getApi().account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); assertNotNull(balance); } @Test public void noTimeoutOnReadTobalala() { Supplier supplier = () -> new HttpExecutor(30000); - EtherScanApi api = new EtherScanApi(EthNetwork.TOBALABA, supplier); - Balance balance = api.account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); + Balance balance = getApi().account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); assertNotNull(balance); } @Test public void noTimeoutUnlimitedAwait() { - Supplier supplier = () -> new HttpExecutor(-30, -300); - EtherScanApi api = new EtherScanApi(EthNetwork.MAINNET, supplier); - Balance balance = api.account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); + Balance balance = getApi().account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); assertNotNull(balance); } diff --git a/src/test/java/io/api/etherscan/account/AccountBalanceListTest.java b/src/test/java/io/api/etherscan/account/AccountBalanceListTest.java index f5a1ebd..fdeb1e9 100644 --- a/src/test/java/io/api/etherscan/account/AccountBalanceListTest.java +++ b/src/test/java/io/api/etherscan/account/AccountBalanceListTest.java @@ -1,12 +1,12 @@ package io.api.etherscan.account; -import io.api.etherscan.core.impl.EtherScanApi; +import io.api.ApiRunner; import io.api.etherscan.error.InvalidAddressException; import io.api.etherscan.model.Balance; import io.api.support.AddressUtil; -import org.junit.Assert; import org.junit.Test; +import java.math.BigInteger; import java.util.ArrayList; import java.util.List; @@ -16,9 +16,7 @@ * @author GoodforGod * @since 03.11.2018 */ -public class AccountBalanceListTest extends Assert { - - private EtherScanApi api = new EtherScanApi(); +public class AccountBalanceListTest extends ApiRunner { @Test public void correct() { @@ -26,13 +24,13 @@ public void correct() { addresses.add("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9"); addresses.add("0xC9F32CE1127e44C51cbD182D6364F3D707Fd0d47"); - List balances = api.account().balances(addresses); + List balances = getApi().account().balances(addresses); assertNotNull(balances); assertFalse(balances.isEmpty()); assertEquals(2, balances.size()); assertNotEquals(balances.get(0), balances.get(1)); assertNotEquals(balances.get(0).hashCode(), balances.get(1).hashCode()); - for(Balance balance : balances) { + for (Balance balance : balances) { assertNotNull(balance.getAddress()); assertNotNull(balance.getGwei()); assertNotNull(balance.getKwei()); @@ -40,7 +38,7 @@ public void correct() { assertNotNull(balance.getEther()); assertNotNull(balance.getGwei()); assertNotNull(balance.getAddress()); - assertNotEquals(0, balance.getWei()); + assertNotEquals(BigInteger.ZERO, balance.getWei()); assertNotNull(balance.toString()); } } @@ -49,20 +47,15 @@ public void correct() { public void correctMoreThat20Addresses() { List addresses = AddressUtil.genRealAddresses(); - List balances = api.account().balances(addresses); + List balances = getApi().account().balances(addresses); assertNotNull(balances); assertFalse(balances.isEmpty()); assertEquals(25, balances.size()); - for(Balance balance : balances) { + for (Balance balance : balances) { assertNotNull(balance.getAddress()); - assertNotEquals(0, balance.getWei()); - assertNotEquals(0, balance.getKwei()); - assertNotEquals(0, balance.getMwei()); - assertNotEquals(0, balance.getEther()); - assertNotEquals(0, balance.getGwei()); } - assertFalse(balances.get(0).equals(balances.get(1))); + assertNotEquals(balances.get(0), balances.get(1)); } @Test(expected = InvalidAddressException.class) @@ -71,13 +64,13 @@ public void invalidParamWithError() { addresses.add("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9"); addresses.add("C9F32CE1127e44C51cbD182D6364F3D707Fd0d47"); - api.account().balances(addresses); + getApi().account().balances(addresses); } @Test public void emptyParamList() { List addresses = new ArrayList<>(); - List balances = api.account().balances(addresses); + List balances = getApi().account().balances(addresses); assertNotNull(balances); assertTrue(balances.isEmpty()); } @@ -88,11 +81,11 @@ public void correctParamWithEmptyExpectedResult() { addresses.add("0x1327cb34984c3992ec1EA0eAE98Ccf80A74f95B9"); addresses.add("0xC1F32CE1127e44C51cbD182D6364F3D707Fd0d47"); - List balances = api.account().balances(addresses); + List balances = getApi().account().balances(addresses); assertNotNull(balances); assertFalse(balances.isEmpty()); assertEquals(2, balances.size()); - for(Balance balance : balances) { + for (Balance balance : balances) { assertNotNull(balance.getAddress()); assertEquals(0, balance.getWei().intValue()); } diff --git a/src/test/java/io/api/etherscan/account/AccountBalanceTest.java b/src/test/java/io/api/etherscan/account/AccountBalanceTest.java index 21eca22..76aca68 100644 --- a/src/test/java/io/api/etherscan/account/AccountBalanceTest.java +++ b/src/test/java/io/api/etherscan/account/AccountBalanceTest.java @@ -1,10 +1,9 @@ package io.api.etherscan.account; +import io.api.ApiRunner; import io.api.etherscan.core.impl.EtherScanApi; import io.api.etherscan.error.InvalidAddressException; import io.api.etherscan.model.Balance; -import io.api.etherscan.model.EthNetwork; -import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -20,12 +19,12 @@ * @since 03.11.2018 */ @RunWith(Parameterized.class) -public class AccountBalanceTest extends Assert { +public class AccountBalanceTest extends ApiRunner { - private EtherScanApi api; - private String addressCorrect; - private String addressInvalid; - private String addressNoResponse; + 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; @@ -36,31 +35,13 @@ public AccountBalanceTest(EtherScanApi api, String addressCorrect, String addres @Parameters public static Collection data() { - return Arrays.asList(new Object[][]{ + return Arrays.asList(new Object[][] { { - new EtherScanApi(), + getApi(), "0x8d4426f94e42f721C7116E81d6688cd935cB3b4F", "8d4426f94e42f721C7116E81d6688cd935cB3b4F", "0x1d4426f94e42f721C7116E81d6688cd935cB3b4F" - }, - { - new EtherScanApi(EthNetwork.ROPSTEN), - "0xddbd2b932c763ba5b1b7ae3b362eac3e8d40121a", - "xddbd2b932c763ba5b1b7ae3b362eac3e8d40121a", - "0x1dbd2b932c763ba5b1b7ae3b362eac3e8d40121a" - }, - { - new EtherScanApi(EthNetwork.RINKEBY), - "0xddbd2b932c763ba5b1b7ae3b362eac3e8d40121a", - "xddbd2b932c763ba5b1b7ae3b362eac3e8d40121a", - "0x1dbd2b932c763ba5b1b7ae3b362eac3e8d40121a" - }, - { - new EtherScanApi(EthNetwork.KOVAN), - "0xB9F36EE9df7E2A24B61b1738F4127BFDe8bA1A87", - "xB9F36EE9df7E2A24B61b1738F4127BFDe8bA1A87", - "0xB1F36EE9df7E2A24B61b1738F4127BFDe8bA1A87" - }, + } }); } @@ -74,13 +55,12 @@ public void correct() { assertNotNull(balance.getGwei()); assertNotNull(balance.getEther()); assertNotNull(balance.getAddress()); - assertNotEquals(0, balance.getWei()); assertNotNull(balance.toString()); } @Test(expected = InvalidAddressException.class) public void invalidParamWithError() { - Balance balance = api.account().balance(addressInvalid); + Balance balance = getApi().account().balance(addressInvalid); } @Test diff --git a/src/test/java/io/api/etherscan/account/AccountMinedBlocksTest.java b/src/test/java/io/api/etherscan/account/AccountMinedBlocksTest.java index 5f44b68..3a46858 100644 --- a/src/test/java/io/api/etherscan/account/AccountMinedBlocksTest.java +++ b/src/test/java/io/api/etherscan/account/AccountMinedBlocksTest.java @@ -1,10 +1,9 @@ package io.api.etherscan.account; +import io.api.ApiRunner; import io.api.etherscan.core.impl.EtherScanApi; import io.api.etherscan.error.InvalidAddressException; import io.api.etherscan.model.Block; -import io.api.etherscan.model.EthNetwork; -import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -21,13 +20,13 @@ * @since 03.11.2018 */ @RunWith(Parameterized.class) -public class AccountMinedBlocksTest extends Assert { +public class AccountMinedBlocksTest extends ApiRunner { - private EtherScanApi api; - private int blocksMined; - private String addressCorrect; - private String addressInvalid; - private String addressNoResponse; + 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, @@ -43,22 +42,14 @@ public AccountMinedBlocksTest(EtherScanApi api, @Parameters public static Collection data() { - return Arrays.asList(new Object[][]{ + return Arrays.asList(new Object[][] { { - new EtherScanApi(), + getApi(), 223, "0xE4C6175183029A0f039bf2DFffa5C6e8F3cA9B23", "xE4C6175183029A0f039bf2DFffa5C6e8F3cA9B23", "0xE1C6175183029A0f039bf2DFffa5C6e8F3cA9B23", - }, - { - new EtherScanApi(EthNetwork.ROPSTEN), - 1, - "0x0923DafEB5A5d11a83E188d5dbCdEd14f9b161a7", - "00923DafEB5A5d11a83E188d5dbCdEd14f9b161a7", - "0x1923DafEB5A5d11a83E188d5dbCdEd14f9b161a7", } - // Other netWorks not presented due to 30k+ mined blocks, tests runs forever }); } @@ -71,7 +62,7 @@ public void correct() { assertBlocks(blocks); assertNotNull(blocks.get(0).toString()); - if(blocks.size() > 1) { + if (blocks.size() > 1) { assertNotEquals(blocks.get(0), blocks.get(1)); assertNotEquals(blocks.get(0).hashCode(), blocks.get(1).hashCode()); } @@ -79,7 +70,7 @@ public void correct() { @Test(expected = InvalidAddressException.class) public void invalidParamWithError() { - List txs = api.account().minedBlocks(addressInvalid); + List txs = getApi().account().minedBlocks(addressInvalid); } @Test diff --git a/src/test/java/io/api/etherscan/account/AccountTokenBalanceTest.java b/src/test/java/io/api/etherscan/account/AccountTokenBalanceTest.java index 2c747a0..2794e95 100644 --- a/src/test/java/io/api/etherscan/account/AccountTokenBalanceTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTokenBalanceTest.java @@ -1,11 +1,10 @@ package io.api.etherscan.account; +import io.api.ApiRunner; import io.api.etherscan.core.impl.EtherScanApi; import io.api.etherscan.error.InvalidAddressException; import io.api.etherscan.model.Balance; -import io.api.etherscan.model.EthNetwork; import io.api.etherscan.model.TokenBalance; -import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -21,14 +20,14 @@ * @since 03.11.2018 */ @RunWith(Parameterized.class) -public class AccountTokenBalanceTest extends Assert { +public class AccountTokenBalanceTest extends ApiRunner { - private EtherScanApi api; - private String contractValid; - private String addressValid; - private String contractInvalid; - private String addressInvalid; - private String addressEmpty; + private final EtherScanApi api; + private final String contractValid; + private final String addressValid; + private final String contractInvalid; + private final String addressInvalid; + private final String addressEmpty; public AccountTokenBalanceTest(EtherScanApi api, String contractValid, @@ -46,39 +45,15 @@ public AccountTokenBalanceTest(EtherScanApi api, @Parameters public static Collection data() { - return Arrays.asList(new Object[][]{ + return Arrays.asList(new Object[][] { { - new EtherScanApi(), + getApi(), "0x5EaC95ad5b287cF44E058dCf694419333b796123", "0x5d807e7F124EC2103a59c5249187f772c0b8D6b2", "0xEaC95ad5b287cF44E058dCf694419333b796123", "0x5807e7F124EC2103a59c5249187f772c0b8D6b2", "0x1d807e7F124EC2103a59c5249187f772c0b8D6b2", - }, - { - new EtherScanApi(EthNetwork.ROPSTEN), - "0x60a5aa08619bd5f71c6d20bfaefb5ac2c2806745", - "0x0923dafeb5a5d11a83e188d5dbcded14f9b161a7", - "0x0a5aa08619bd5f71c6d20bfaefb5ac2c2806745", - "0x923dafeb5a5d11a83e188d5dbcded14f9b161a7", - "0x1923dafeb5a5d11a83e188d5dbcded14f9b161a7", - }, - { - new EtherScanApi(EthNetwork.RINKEBY), - "0xb8b6f3fb67403c90652dc5f085ba4a62ab1ef5ce", - "0x7ffc57839b00206d1ad20c69a1981b489f772031", - "0x8b6f3fb67403c90652dc5f085ba4a62ab1ef5ce", - "0x7fc57839b00206d1ad20c69a1981b489f772031", - "0x1ffc57839b00206d1ad20c69a1981b489f772031", - }, - { - new EtherScanApi(EthNetwork.KOVAN), - "0xde0eaa632f071069214f1c1ad7eb609ff8152dfe", - "0x00e6d2b931f55a3f1701c7389d592a7778897879", - "0xd0eaa632f071069214f1c1ad7eb609ff8152dfe", - "0x0e6d2b931f55a3f1701c7389d592a7778897879", - "0x10e6d2b931f55a3f1701c7389d592a7778897879", - }, + } }); } @@ -89,7 +64,6 @@ public void correct() { assertNotNull(balance.getWei()); assertNotNull(balance.getAddress()); assertNotNull(balance.getContract()); - assertNotEquals(0, balance.getWei()); assertNotNull(balance.toString()); TokenBalance balance2 = new TokenBalance("125161", balance.getWei(), balance.getContract()); diff --git a/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java b/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java index 4727317..d1ed2bc 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java @@ -1,11 +1,10 @@ package io.api.etherscan.account; +import io.api.ApiRunner; import io.api.etherscan.core.impl.EtherScanApi; import io.api.etherscan.error.InvalidTxHashException; -import io.api.etherscan.model.EthNetwork; import io.api.etherscan.model.TxInternal; import io.api.etherscan.util.BasicUtils; -import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -22,13 +21,13 @@ * @since 03.11.2018 */ @RunWith(Parameterized.class) -public class AccountTxInternalByHashTest extends Assert { +public class AccountTxInternalByHashTest extends ApiRunner { - private EtherScanApi api; - private int txAmount; - private String validTx; - private String invalidTx; - private String emptyTx; + 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; @@ -40,35 +39,14 @@ public AccountTxInternalByHashTest(EtherScanApi api, int txAmount, String validT @Parameters public static Collection data() { - return Arrays.asList(new Object[][]{ + return Arrays.asList(new Object[][] { { - new EtherScanApi(), + getApi(), 1, "0x1b513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b", "0xb513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b", "0x2b513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b", - }, - { - new EtherScanApi(EthNetwork.ROPSTEN), - 1, - "0x8bc5504517d40ad7b4f32d008d82436cc118e856593f61d23c70eec73c10322a", - "0x8c5504517d40ad7b4f32d008d82436cc118e856593f61d23c70eec73c10322a", - "0x7bc5504517d40ad7b4f32d008d82436cc118e856593f61d23c70eec73c10322a", - }, - { - new EtherScanApi(EthNetwork.RINKEBY), - 10, - "0x4be697e735f594038e2e70e051948896bdfd6193fc399e38d6113e96f96e8567", - "0x4e697e735f594038e2e70e051948896bdfd6193fc399e38d6113e96f96e8567", - "0x3be697e735f594038e2e70e051948896bdfd6193fc399e38d6113e96f96e8567", - }, - { - new EtherScanApi(EthNetwork.KOVAN), - 1, - "0x3b85c47f2a8c5efd639a85f5233097af834c9ab6ab7965cf7cfc3ab3c8bae285", - "0x385c47f2a8c5efd639a85f5233097af834c9ab6ab7965cf7cfc3ab3c8bae285", - "0x2b85c47f2a8c5efd639a85f5233097af834c9ab6ab7965cf7cfc3ab3c8bae285", - }, + } }); } @@ -89,7 +67,7 @@ public void correct() { assertTrue(BasicUtils.isEmpty(txs.get(0).getErrCode())); assertNotNull(txs.get(0).toString()); - if(txs.size() > 9) { + if (txs.size() > 9) { assertNotEquals(txs.get(0), txs.get(9)); assertNotEquals(txs.get(0).hashCode(), txs.get(9).hashCode()); } diff --git a/src/test/java/io/api/etherscan/account/AccountTxInternalTest.java b/src/test/java/io/api/etherscan/account/AccountTxInternalTest.java index a80be28..f993c39 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxInternalTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTxInternalTest.java @@ -1,9 +1,8 @@ package io.api.etherscan.account; -import io.api.etherscan.core.impl.EtherScanApi; +import io.api.ApiRunner; import io.api.etherscan.error.InvalidAddressException; import io.api.etherscan.model.TxInternal; -import org.junit.Assert; import org.junit.Test; import java.util.List; @@ -14,13 +13,11 @@ * @author GoodforGod * @since 03.11.2018 */ -public class AccountTxInternalTest extends Assert { - - private EtherScanApi api = new EtherScanApi(); +public class AccountTxInternalTest extends ApiRunner { @Test public void correct() { - List txs = api.account().txsInternal("0x2C1ba59D6F58433FB1EaEe7d20b26Ed83bDA51A3"); + List txs = getApi().account().txsInternal("0x2C1ba59D6F58433FB1EaEe7d20b26Ed83bDA51A3"); assertNotNull(txs); assertEquals(66, txs.size()); assertTxs(txs); @@ -29,7 +26,7 @@ public void correct() { @Test public void correctStartBlock() { - List txs = api.account().txsInternal("0x2C1ba59D6F58433FB1EaEe7d20b26Ed83bDA51A3", 2558775); + List txs = getApi().account().txsInternal("0x2C1ba59D6F58433FB1EaEe7d20b26Ed83bDA51A3", 2558775); assertNotNull(txs); assertEquals(24, txs.size()); assertTxs(txs); @@ -37,7 +34,7 @@ public void correctStartBlock() { @Test public void correctStartBlockEndBlock() { - List txs = api.account().txsInternal("0x2C1ba59D6F58433FB1EaEe7d20b26Ed83bDA51A3", 2558775, 2685504); + List txs = getApi().account().txsInternal("0x2C1ba59D6F58433FB1EaEe7d20b26Ed83bDA51A3", 2558775, 2685504); assertNotNull(txs); assertEquals(21, txs.size()); assertTxs(txs); @@ -45,12 +42,12 @@ public void correctStartBlockEndBlock() { @Test(expected = InvalidAddressException.class) public void invalidParamWithError() { - List txs = api.account().txsInternal("0x2C1ba59D6F58433FB1EaEe7d20b26Ed83bDA51"); + List txs = getApi().account().txsInternal("0x2C1ba59D6F58433FB1EaEe7d20b26Ed83bDA51"); } @Test public void correctParamWithEmptyExpectedResult() { - List txs = api.account().txsInternal("0x2C1ba59D6F58433FB2EaEe7d20b26Ed83bDA51A3"); + List txs = getApi().account().txsInternal("0x2C1ba59D6F58433FB2EaEe7d20b26Ed83bDA51A3"); 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 7bdf2d6..b82d4d1 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java @@ -1,9 +1,8 @@ package io.api.etherscan.account; -import io.api.etherscan.core.impl.EtherScanApi; +import io.api.ApiRunner; import io.api.etherscan.error.InvalidAddressException; import io.api.etherscan.model.TxToken; -import org.junit.Assert; import org.junit.Test; import java.util.List; @@ -14,13 +13,11 @@ * @author GoodforGod * @since 03.11.2018 */ -public class AccountTxTokenTest extends Assert { - - private final EtherScanApi api = new EtherScanApi(); +public class AccountTxTokenTest extends ApiRunner { @Test public void correct() { - List txs = api.account().txsToken("0xE376F69ED2218076682e2b3B7b9099eC50aD68c4"); + List txs = getApi().account().txsToken("0xE376F69ED2218076682e2b3B7b9099eC50aD68c4"); assertNotNull(txs); assertEquals(3, txs.size()); assertTxs(txs); @@ -39,7 +36,7 @@ public void correct() { @Test public void correctStartBlock() { - List txs = api.account().txsToken("0x36ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7", 5578167); + List txs = getApi().account().txsToken("0x36ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7", 5578167); assertNotNull(txs); assertEquals(11, txs.size()); assertTxs(txs); @@ -47,7 +44,7 @@ public void correctStartBlock() { @Test public void correctStartBlockEndBlock() { - List txs = api.account().txsToken("0x36ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7", 5578167, 5813576); + List txs = getApi().account().txsToken("0x36ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7", 5578167, 5813576); assertNotNull(txs); assertEquals(5, txs.size()); assertTxs(txs); @@ -55,12 +52,12 @@ public void correctStartBlockEndBlock() { @Test(expected = InvalidAddressException.class) public void invalidParamWithError() { - api.account().txsToken("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); + getApi().account().txsToken("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); } @Test public void correctParamWithEmptyExpectedResult() { - List txs = api.account().txsToken("0x31ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); + List txs = getApi().account().txsToken("0x31ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); assertNotNull(txs); assertTrue(txs.isEmpty()); } @@ -74,9 +71,9 @@ private void assertTxs(List txs) { assertNotNull(tx.getTo()); assertNotNull(tx.getTimeStamp()); assertNotNull(tx.getTokenDecimal()); - assertNotEquals(-1,(tx.getConfirmations())); + assertNotEquals(-1, (tx.getConfirmations())); assertNotNull(tx.getGasUsed()); - assertNotEquals(-1 ,tx.getCumulativeGasUsed()); + assertNotEquals(-1, tx.getCumulativeGasUsed()); assertNotEquals(-1, tx.getTransactionIndex()); } } diff --git a/src/test/java/io/api/etherscan/account/AccountTxsTest.java b/src/test/java/io/api/etherscan/account/AccountTxsTest.java index 5c0ad48..66a95e4 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxsTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTxsTest.java @@ -1,9 +1,8 @@ package io.api.etherscan.account; -import io.api.etherscan.core.impl.EtherScanApi; +import io.api.ApiRunner; import io.api.etherscan.error.InvalidAddressException; import io.api.etherscan.model.Tx; -import org.junit.Assert; import org.junit.Test; import java.util.List; @@ -14,13 +13,11 @@ * @author GoodforGod * @since 03.11.2018 */ -public class AccountTxsTest extends Assert { - - private final EtherScanApi api = new EtherScanApi(); +public class AccountTxsTest extends ApiRunner { @Test public void correct() { - List txs = api.account().txs("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9"); + List txs = getApi().account().txs("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9"); assertNotNull(txs); assertEquals(5, txs.size()); assertTxs(txs); @@ -43,7 +40,7 @@ public void correct() { @Test public void correctStartBlock() { - List txs = api.account().txs("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9", 3892842); + List txs = getApi().account().txs("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9", 3892842); assertNotNull(txs); assertEquals(4, txs.size()); assertTxs(txs); @@ -51,7 +48,7 @@ public void correctStartBlock() { @Test public void correctStartBlockEndBlock() { - List txs = api.account().txs("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9", 3892842, 3945741); + List txs = getApi().account().txs("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9", 3892842, 3945741); assertNotNull(txs); assertEquals(3, txs.size()); assertTxs(txs); @@ -60,12 +57,12 @@ public void correctStartBlockEndBlock() { @Test(expected = InvalidAddressException.class) public void invalidParamWithError() { - List txs = api.account().txs("9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9"); + List txs = getApi().account().txs("9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9"); } @Test public void correctParamWithEmptyExpectedResult() { - List txs = api.account().txs("0x9321cb34984c3992ec1EA0eAE98Ccf80A74f95B9"); + 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 c459b1a..34b9de5 100644 --- a/src/test/java/io/api/etherscan/block/BlockApiTest.java +++ b/src/test/java/io/api/etherscan/block/BlockApiTest.java @@ -1,8 +1,7 @@ package io.api.etherscan.block; -import io.api.etherscan.core.impl.EtherScanApi; +import io.api.ApiRunner; import io.api.etherscan.model.UncleBlock; -import org.junit.Assert; import org.junit.Test; import java.util.Optional; @@ -13,13 +12,11 @@ * @author GoodforGod * @since 03.11.2018 */ -public class BlockApiTest extends Assert { - - private final EtherScanApi api = new EtherScanApi(); +public class BlockApiTest extends ApiRunner { @Test public void correct() { - Optional uncle = api.block().uncles(2165403); + Optional uncle = getApi().block().uncles(2165403); assertTrue(uncle.isPresent()); assertFalse(uncle.get().isEmpty()); assertNotNull(uncle.get().getBlockMiner()); @@ -36,13 +33,13 @@ public void correct() { assertNotEquals(uncle.get(), empty); assertTrue(empty.isEmpty()); - if(uncle.get().getUncles().size() > 0) { + if (uncle.get().getUncles().size() > 0) { assertNotEquals(-1, uncle.get().getUncles().get(0).getUnclePosition()); assertEquals(uncle.get().getUncles().get(0), uncle.get().getUncles().get(0)); assertEquals(uncle.get().getUncles().get(0).hashCode(), uncle.get().getUncles().get(0).hashCode()); } - if(uncle.get().getUncles().size() > 1) { + if (uncle.get().getUncles().size() > 1) { assertNotEquals(uncle.get().getUncles().get(1), uncle.get().getUncles().get(0)); assertNotEquals(uncle.get().getUncles().get(1).hashCode(), uncle.get().getUncles().get(0).hashCode()); } @@ -50,14 +47,14 @@ public void correct() { @Test public void correctNoUncles() { - Optional uncles = api.block().uncles(34); + Optional uncles = getApi().block().uncles(34); assertTrue(uncles.isPresent()); assertTrue(uncles.get().getUncles().isEmpty()); } @Test public void correctParamWithEmptyExpectedResult() { - Optional uncles = api.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/api/etherscan/contract/ContractApiTest.java index 7ecf26f..9d43c5f 100644 --- a/src/test/java/io/api/etherscan/contract/ContractApiTest.java +++ b/src/test/java/io/api/etherscan/contract/ContractApiTest.java @@ -1,9 +1,8 @@ package io.api.etherscan.contract; -import io.api.etherscan.core.impl.EtherScanApi; +import io.api.ApiRunner; import io.api.etherscan.error.InvalidAddressException; import io.api.etherscan.model.Abi; -import org.junit.Assert; import org.junit.Test; /** @@ -12,13 +11,11 @@ * @author GoodforGod * @since 03.11.2018 */ -public class ContractApiTest extends Assert { - - private final EtherScanApi api = new EtherScanApi(); +public class ContractApiTest extends ApiRunner { @Test public void correct() { - Abi abi = api.contract().contractAbi("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413"); + Abi abi = getApi().contract().contractAbi("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413"); assertNotNull(abi); assertTrue(abi.isVerified()); assertTrue(abi.haveAbi()); @@ -32,12 +29,12 @@ public void correct() { @Test(expected = InvalidAddressException.class) public void invalidParamWithError() { - api.contract().contractAbi("0xBBbc244D798123fDe783fCc1C72d3Bb8C189413"); + getApi().contract().contractAbi("0xBBbc244D798123fDe783fCc1C72d3Bb8C189413"); } @Test public void correctParamWithEmptyExpectedResult() { - Abi abi = api.contract().contractAbi("0xBB1bc244D798123fDe783fCc1C72d3Bb8C189413"); + Abi abi = getApi().contract().contractAbi("0xBB1bc244D798123fDe783fCc1C72d3Bb8C189413"); assertNotNull(abi); assertFalse(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 058f592..85b35e8 100644 --- a/src/test/java/io/api/etherscan/logs/LogQueryBuilderTest.java +++ b/src/test/java/io/api/etherscan/logs/LogQueryBuilderTest.java @@ -1,12 +1,12 @@ package io.api.etherscan.logs; +import io.api.ApiRunner; import io.api.etherscan.error.InvalidAddressException; import io.api.etherscan.error.LogQueryException; import io.api.etherscan.model.query.LogOp; import io.api.etherscan.model.query.impl.LogQuery; import io.api.etherscan.model.query.impl.LogQueryBuilder; import io.api.etherscan.model.query.impl.LogTopicQuadro; -import org.junit.Assert; import org.junit.Test; /** @@ -15,7 +15,7 @@ * @author GoodforGod * @since 03.11.2018 */ -public class LogQueryBuilderTest extends Assert { +public class LogQueryBuilderTest extends ApiRunner { @Test public void singleCorrect() { diff --git a/src/test/java/io/api/etherscan/logs/LogsApiTest.java b/src/test/java/io/api/etherscan/logs/LogsApiTest.java index 4a3f9e1..7143a83 100644 --- a/src/test/java/io/api/etherscan/logs/LogsApiTest.java +++ b/src/test/java/io/api/etherscan/logs/LogsApiTest.java @@ -1,11 +1,10 @@ package io.api.etherscan.logs; -import io.api.etherscan.core.impl.EtherScanApi; +import io.api.ApiRunner; import io.api.etherscan.model.Log; import io.api.etherscan.model.query.LogOp; import io.api.etherscan.model.query.impl.LogQuery; import io.api.etherscan.model.query.impl.LogQueryBuilder; -import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -22,12 +21,10 @@ * @since 03.11.2018 */ @RunWith(Parameterized.class) -public class LogsApiTest extends Assert { +public class LogsApiTest extends ApiRunner { - private final EtherScanApi api = new EtherScanApi(); - - private LogQuery query; - private int logsSize; + private final LogQuery query; + private final int logsSize; public LogsApiTest(LogQuery query, int logsSize) { this.query = query; @@ -56,21 +53,21 @@ public static Collection data() { .setOpTopic0_1(LogOp.OR) .build(); - return Arrays.asList(new Object[][]{ - {single, 423}, - {singleInvalidAddr, 0}, - {tupleAnd, 1}, - {tupleOr, 425} + return Arrays.asList(new Object[][] { + { single, 423 }, + { singleInvalidAddr, 0 }, + { tupleAnd, 1 }, + { tupleOr, 425 } }); } @Test public void validateQuery() { - List logs = api.logs().logs(query); + List logs = getApi().logs().logs(query); assertEquals(logsSize, logs.size()); if (logsSize > 0) { - if(logsSize > 1) { + if (logsSize > 1) { assertNotEquals(logs.get(0), logs.get(1)); assertNotEquals(logs.get(0).hashCode(), logs.get(1).hashCode()); } diff --git a/src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java index 43b4851..181a68d 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java @@ -1,8 +1,7 @@ package io.api.etherscan.proxy; -import io.api.etherscan.core.impl.EtherScanApi; +import io.api.ApiRunner; import io.api.etherscan.model.proxy.BlockProxy; -import org.junit.Assert; import org.junit.Test; import java.util.Optional; @@ -13,13 +12,11 @@ * @author GoodforGod * @since 03.11.2018 */ -public class ProxyBlockApiTest extends Assert { - - private final EtherScanApi api = new EtherScanApi(); +public class ProxyBlockApiTest extends ApiRunner { @Test public void correct() { - Optional block = api.proxy().block(5120); + Optional block = getApi().proxy().block(5120); assertTrue(block.isPresent()); BlockProxy proxy = block.get(); assertNotNull(proxy.getHash()); @@ -52,13 +49,13 @@ public void correct() { @Test public void correctParamWithEmptyExpectedResult() { - Optional block = api.proxy().block(99999999999L); + Optional block = getApi().proxy().block(99999999999L); assertFalse(block.isPresent()); } @Test public void correctParamNegativeNo() { - Optional block = api.proxy().block(-1); + Optional block = getApi().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 b981a42..5485391 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyBlockLastNoApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyBlockLastNoApiTest.java @@ -1,7 +1,6 @@ package io.api.etherscan.proxy; -import io.api.etherscan.core.impl.EtherScanApi; -import org.junit.Assert; +import io.api.ApiRunner; import org.junit.Test; /** @@ -10,13 +9,11 @@ * @author GoodforGod * @since 13.11.2018 */ -public class ProxyBlockLastNoApiTest extends Assert { - - private final EtherScanApi api = new EtherScanApi(); +public class ProxyBlockLastNoApiTest extends ApiRunner { @Test public void correct() { - long noLast = api.proxy().blockNoLast(); + 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 3ccac29..474c5bb 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyBlockUncleApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyBlockUncleApiTest.java @@ -1,8 +1,7 @@ package io.api.etherscan.proxy; -import io.api.etherscan.core.impl.EtherScanApi; +import io.api.ApiRunner; import io.api.etherscan.model.proxy.BlockProxy; -import org.junit.Assert; import org.junit.Test; import java.util.Optional; @@ -13,13 +12,11 @@ * @author GoodforGod * @since 13.11.2018 */ -public class ProxyBlockUncleApiTest extends Assert { - - private final EtherScanApi api = new EtherScanApi(); +public class ProxyBlockUncleApiTest extends ApiRunner { @Test public void correct() { - Optional block = api.proxy().blockUncle(603183, 0); + Optional block = getApi().proxy().blockUncle(603183, 0); assertTrue(block.isPresent()); assertNotNull(block.get().getHash()); assertNotNull(block.get().toString()); @@ -27,13 +24,13 @@ public void correct() { @Test public void correctParamWithEmptyExpectedResult() { - Optional block = api.proxy().blockUncle(5120, 1); + Optional block = getApi().proxy().blockUncle(5120, 1); assertFalse(block.isPresent()); } @Test public void correctParamNegativeNo() { - Optional block = api.proxy().blockUncle(-603183, 0); + 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 159ed9c..c90850c 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyCallApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyCallApiTest.java @@ -1,10 +1,9 @@ package io.api.etherscan.proxy; -import io.api.etherscan.core.impl.EtherScanApi; +import io.api.ApiRunner; import io.api.etherscan.error.InvalidAddressException; import io.api.etherscan.error.InvalidDataHexException; import io.api.etherscan.util.BasicUtils; -import org.junit.Assert; import org.junit.Test; import java.util.Optional; @@ -15,13 +14,11 @@ * @author GoodforGod * @since 03.11.2018 */ -public class ProxyCallApiTest extends Assert { - - private final EtherScanApi api = new EtherScanApi(); +public class ProxyCallApiTest extends ApiRunner { @Test public void correct() { - Optional call = api.proxy().call("0xAEEF46DB4855E25702F8237E8f403FddcaF931C0", + Optional call = getApi().proxy().call("0xAEEF46DB4855E25702F8237E8f403FddcaF931C0", "0x70a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724"); assertTrue(call.isPresent()); assertFalse(BasicUtils.isNotHex(call.get())); @@ -29,19 +26,19 @@ public void correct() { @Test(expected = InvalidAddressException.class) public void invalidParamWithError() { - Optional call = api.proxy().call("0xEEF46DB4855E25702F8237E8f403FddcaF931C0", + Optional call = getApi().proxy().call("0xEEF46DB4855E25702F8237E8f403FddcaF931C0", "0x70a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724"); } @Test(expected = InvalidDataHexException.class) public void invalidParamNotHex() { - Optional call = api.proxy().call("0xAEEF46DB4855E25702F8237E8f403FddcaF931C0", + Optional call = getApi().proxy().call("0xAEEF46DB4855E25702F8237E8f403FddcaF931C0", "7-0a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724"); } @Test public void correctParamWithEmptyExpectedResult() { - Optional call = api.proxy().call("0xAEEF16DB4855E25702F8237E8f403FddcaF931C0", + Optional call = getApi().proxy().call("0xAEEF16DB4855E25702F8237E8f403FddcaF931C0", "0x70a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724"); assertTrue(call.isPresent()); assertFalse(BasicUtils.isNotHex(call.get())); diff --git a/src/test/java/io/api/etherscan/proxy/ProxyCodeApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyCodeApiTest.java index 3c47171..3120503 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyCodeApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyCodeApiTest.java @@ -1,9 +1,8 @@ package io.api.etherscan.proxy; -import io.api.etherscan.core.impl.EtherScanApi; +import io.api.ApiRunner; import io.api.etherscan.error.InvalidAddressException; import io.api.etherscan.util.BasicUtils; -import org.junit.Assert; import org.junit.Test; import java.util.Optional; @@ -14,25 +13,23 @@ * @author GoodforGod * @since 03.11.2018 */ -public class ProxyCodeApiTest extends Assert { - - private final EtherScanApi api = new EtherScanApi(); +public class ProxyCodeApiTest extends ApiRunner { @Test public void correct() { - Optional call = api.proxy().code("0xf75e354c5edc8efed9b59ee9f67a80845ade7d0c"); + Optional call = getApi().proxy().code("0xf75e354c5edc8efed9b59ee9f67a80845ade7d0c"); assertTrue(call.isPresent()); assertFalse(BasicUtils.isNotHex(call.get())); } @Test(expected = InvalidAddressException.class) public void invalidParamWithError() { - Optional call = api.proxy().code("0f75e354c5edc8efed9b59ee9f67a80845ade7d0c"); + Optional call = getApi().proxy().code("0f75e354c5edc8efed9b59ee9f67a80845ade7d0c"); } @Test public void correctParamWithEmptyExpectedResult() { - Optional call = api.proxy().code("0xf15e354c5edc8efed9b59ee9f67a80845ade7d0c"); + Optional call = getApi().proxy().code("0xf15e354c5edc8efed9b59ee9f67a80845ade7d0c"); assertTrue(call.isPresent()); assertFalse(BasicUtils.isNotHex(call.get())); } diff --git a/src/test/java/io/api/etherscan/proxy/ProxyGasApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyGasApiTest.java index 2665175..63e476c 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyGasApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyGasApiTest.java @@ -1,8 +1,7 @@ package io.api.etherscan.proxy; -import io.api.etherscan.core.impl.EtherScanApi; +import io.api.ApiRunner; import io.api.etherscan.error.InvalidDataHexException; -import org.junit.Assert; import org.junit.Test; import java.math.BigInteger; @@ -13,20 +12,18 @@ * @author GoodforGod * @since 03.11.2018 */ -public class ProxyGasApiTest extends Assert { - - private final EtherScanApi api = new EtherScanApi(); +public class ProxyGasApiTest extends ApiRunner { @Test public void correctPrice() { - BigInteger price = api.proxy().gasPrice(); + BigInteger price = getApi().proxy().gasPrice(); assertNotNull(price); assertNotEquals(0, price.intValue()); } @Test public void correctEstimated() { - BigInteger price = api.proxy().gasEstimated(); + BigInteger price = getApi().proxy().gasEstimated(); assertNotNull(price); assertNotEquals(0, price.intValue()); } @@ -34,8 +31,8 @@ public void correctEstimated() { @Test public void correctEstimatedWithData() { String dataCustom = "606060405260728060106000396000f360606040526000606060405260728060106000396000f360606040526000"; - BigInteger price = api.proxy().gasEstimated(); - BigInteger priceCustom = api.proxy().gasEstimated(dataCustom); + BigInteger price = getApi().proxy().gasEstimated(); + BigInteger priceCustom = getApi().proxy().gasEstimated(dataCustom); assertNotNull(price); assertNotNull(priceCustom); assertNotEquals(price, priceCustom); @@ -44,6 +41,6 @@ public void correctEstimatedWithData() { @Test(expected = InvalidDataHexException.class) public void invalidParamWithError() { String dataCustom = "280&60106000396000f360606040526000"; - BigInteger priceCustom = api.proxy().gasEstimated(dataCustom); + BigInteger priceCustom = getApi().proxy().gasEstimated(dataCustom); } } diff --git a/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java index a96ddb3..fbfcf80 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java @@ -1,9 +1,8 @@ package io.api.etherscan.proxy; -import io.api.etherscan.core.impl.EtherScanApi; +import io.api.ApiRunner; import io.api.etherscan.error.InvalidAddressException; import io.api.etherscan.util.BasicUtils; -import org.junit.Assert; import org.junit.Test; import java.util.Optional; @@ -14,25 +13,23 @@ * @author GoodforGod * @since 03.11.2018 */ -public class ProxyStorageApiTest extends Assert { - - private final EtherScanApi api = new EtherScanApi(); +public class ProxyStorageApiTest extends ApiRunner { @Test public void correct() { - Optional call = api.proxy().storageAt("0x6e03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 0); + Optional call = getApi().proxy().storageAt("0x6e03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 0); assertTrue(call.isPresent()); assertFalse(BasicUtils.isNotHex(call.get())); } @Test(expected = InvalidAddressException.class) public void invalidParamWithError() { - Optional call = api.proxy().storageAt("0xe03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 0); + Optional call = getApi().proxy().storageAt("0xe03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 0); } @Test public void correctParamWithEmptyExpectedResult() { - Optional call = api.proxy().storageAt("0x6e03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 100); + Optional call = getApi().proxy().storageAt("0x6e03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 100); 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 1a36372..2779120 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyTxApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyTxApiTest.java @@ -1,9 +1,8 @@ package io.api.etherscan.proxy; -import io.api.etherscan.core.impl.EtherScanApi; +import io.api.ApiRunner; import io.api.etherscan.error.InvalidTxHashException; import io.api.etherscan.model.proxy.TxProxy; -import org.junit.Assert; import org.junit.Test; import java.util.Optional; @@ -14,13 +13,11 @@ * @author GoodforGod * @since 03.11.2018 */ -public class ProxyTxApiTest extends Assert { - - private final EtherScanApi api = new EtherScanApi(); +public class ProxyTxApiTest extends ApiRunner { @Test public void correctByHash() { - Optional tx = api.proxy().tx("0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); + Optional tx = getApi().proxy().tx("0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); assertTrue(tx.isPresent()); assertNotNull(tx.get().getBlockHash()); assertNotNull(tx.get().getFrom()); @@ -37,7 +34,7 @@ public void correctByHash() { @Test public void correctByBlockNo() { - Optional tx = api.proxy().tx(637368, 0); + Optional tx = getApi().proxy().tx(637368, 0); assertTrue(tx.isPresent()); assertNotNull(tx.get().getBlockHash()); assertNotNull(tx.get().getFrom()); @@ -57,18 +54,18 @@ public void correctByBlockNo() { @Test(expected = InvalidTxHashException.class) public void invalidParamWithError() { - Optional tx = api.proxy().tx("0xe2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); + Optional tx = getApi().proxy().tx("0xe2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); } @Test public void correctParamWithEmptyExpectedResultBlockNoExist() { - Optional tx = api.proxy().tx(99999999L, 0); + Optional tx = getApi().proxy().tx(99999999L, 0); assertFalse(tx.isPresent()); } @Test public void correctParamWithEmptyExpectedResult() { - Optional tx = api.proxy().tx("0x2e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); + 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 7ab1100..6a0778c 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyTxCountApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyTxCountApiTest.java @@ -1,8 +1,7 @@ package io.api.etherscan.proxy; -import io.api.etherscan.core.impl.EtherScanApi; +import io.api.ApiRunner; import io.api.etherscan.error.InvalidAddressException; -import org.junit.Assert; import org.junit.Test; /** @@ -11,36 +10,34 @@ * @author GoodforGod * @since 03.11.2018 */ -public class ProxyTxCountApiTest extends Assert { - - private final EtherScanApi api = new EtherScanApi(); +public class ProxyTxCountApiTest extends ApiRunner { @Test public void correctSended() { - int count = api.proxy().txSendCount("0x2910543af39aba0cd09dbb2d50200b3e800a63d2"); + int count = getApi().proxy().txSendCount("0x2910543af39aba0cd09dbb2d50200b3e800a63d2"); assertNotEquals(0, count); } @Test public void correctByBlockNo() { - int count = api.proxy().txCount(6137420); + int count = getApi().proxy().txCount(6137420); assertNotEquals(0, count); } @Test(expected = InvalidAddressException.class) public void invalidParamWithError() { - int count = api.proxy().txSendCount("0xe03d9cce9d60f3e9f2597e13cd4c54c55330cfd"); + int count = getApi().proxy().txSendCount("0xe03d9cce9d60f3e9f2597e13cd4c54c55330cfd"); } @Test public void correctParamWithEmptyExpectedResultBlockNoExist() { - int count = api.proxy().txCount(99999999999L); + int count = getApi().proxy().txCount(99999999999L); assertEquals(0, count); } @Test public void correctParamWithEmptyExpectedResult() { - int count = api.proxy().txSendCount("0x1e03d9cce9d60f3e9f2597e13cd4c54c55330cfd"); + int count = getApi().proxy().txSendCount("0x1e03d9cce9d60f3e9f2597e13cd4c54c55330cfd"); assertEquals(0, count); } } diff --git a/src/test/java/io/api/etherscan/proxy/ProxyTxReceiptApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyTxReceiptApiTest.java index a530bdb..c4a3383 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyTxReceiptApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyTxReceiptApiTest.java @@ -1,9 +1,8 @@ package io.api.etherscan.proxy; -import io.api.etherscan.core.impl.EtherScanApi; +import io.api.ApiRunner; import io.api.etherscan.error.InvalidTxHashException; import io.api.etherscan.model.proxy.ReceiptProxy; -import org.junit.Assert; import org.junit.Test; import java.util.Optional; @@ -14,13 +13,12 @@ * @author GoodforGod * @since 03.11.2018 */ -public class ProxyTxReceiptApiTest extends Assert { - - private final EtherScanApi api = new EtherScanApi(); +public class ProxyTxReceiptApiTest extends ApiRunner { @Test public void correct() { - Optional infoProxy = api.proxy().txReceipt("0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); + Optional infoProxy = getApi().proxy() + .txReceipt("0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); assertTrue(infoProxy.isPresent()); assertNotNull(infoProxy.get().getBlockHash()); assertNotNull(infoProxy.get().getRoot()); @@ -44,12 +42,14 @@ public void correct() { @Test(expected = InvalidTxHashException.class) public void invalidParamWithError() { - Optional infoProxy = api.proxy().txReceipt("0xe2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); + Optional infoProxy = getApi().proxy() + .txReceipt("0xe2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); } @Test public void correctParamWithEmptyExpectedResult() { - Optional infoProxy = api.proxy().txReceipt("0x2e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); + 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 a5eb5eb..40e79a6 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyTxSendRawApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyTxSendRawApiTest.java @@ -1,9 +1,8 @@ package io.api.etherscan.proxy; -import io.api.etherscan.core.impl.EtherScanApi; +import io.api.ApiRunner; import io.api.etherscan.error.EtherScanException; import io.api.etherscan.error.InvalidDataHexException; -import org.junit.Assert; import org.junit.Test; import java.util.Optional; @@ -14,28 +13,27 @@ * @author GoodforGod * @since 03.11.2018 */ -//TODO contact etherscan and ask about method behavior -public class ProxyTxSendRawApiTest extends Assert { - - private final EtherScanApi api = new EtherScanApi(); +// TODO contact etherscan and ask about method behavior +public class ProxyTxSendRawApiTest extends ApiRunner { public void correct() { - Optional sendRaw = api.proxy().txSendRaw("0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); + Optional sendRaw = getApi().proxy() + .txSendRaw("0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); assertTrue(sendRaw.isPresent()); } @Test(expected = InvalidDataHexException.class) public void invalidParamWithError() { - Optional sendRaw = api.proxy().txSendRaw("5151=0561"); + Optional sendRaw = getApi().proxy().txSendRaw("5151=0561"); } @Test(expected = EtherScanException.class) public void invalidParamEtherScanDataException() { - Optional sendRaw = api.proxy().txSendRaw("0x1"); + Optional sendRaw = getApi().proxy().txSendRaw("0x1"); } public void correctParamWithEmptyExpectedResult() { - Optional sendRaw = api.proxy().txSendRaw("0x000000"); + 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 f92755e..3245b17 100644 --- a/src/test/java/io/api/etherscan/statistic/StatisticPriceApiTest.java +++ b/src/test/java/io/api/etherscan/statistic/StatisticPriceApiTest.java @@ -1,8 +1,7 @@ package io.api.etherscan.statistic; -import io.api.etherscan.core.impl.EtherScanApi; +import io.api.ApiRunner; import io.api.etherscan.model.Price; -import org.junit.Assert; import org.junit.Test; /** @@ -11,13 +10,11 @@ * @author GoodforGod * @since 03.11.2018 */ -public class StatisticPriceApiTest extends Assert { - - private final EtherScanApi api = new EtherScanApi(); +public class StatisticPriceApiTest extends ApiRunner { @Test public void correct() { - Price price = api.stats().lastPrice(); + Price price = getApi().stats().lastPrice(); assertNotNull(price); assertNotNull(price.btcTimestamp()); assertNotNull(price.usdTimestamp()); diff --git a/src/test/java/io/api/etherscan/statistic/StatisticSupplyApiTest.java b/src/test/java/io/api/etherscan/statistic/StatisticSupplyApiTest.java index 4d1eecb..a705a31 100644 --- a/src/test/java/io/api/etherscan/statistic/StatisticSupplyApiTest.java +++ b/src/test/java/io/api/etherscan/statistic/StatisticSupplyApiTest.java @@ -1,8 +1,7 @@ package io.api.etherscan.statistic; -import io.api.etherscan.core.impl.EtherScanApi; +import io.api.ApiRunner; import io.api.etherscan.model.Supply; -import org.junit.Assert; import org.junit.Test; import java.math.BigInteger; @@ -13,13 +12,11 @@ * @author GoodforGod * @since 03.11.2018 */ -public class StatisticSupplyApiTest extends Assert { - - private final EtherScanApi api = new EtherScanApi(); +public class StatisticSupplyApiTest extends ApiRunner { @Test public void correct() { - Supply supply = api.stats().supply(); + Supply supply = getApi().stats().supply(); assertNotNull(supply); assertNotNull(supply.getValue()); assertNotNull(supply.asGwei()); diff --git a/src/test/java/io/api/etherscan/statistic/StatisticTokenSupplyApiTest.java b/src/test/java/io/api/etherscan/statistic/StatisticTokenSupplyApiTest.java index 53aede7..0a84d01 100644 --- a/src/test/java/io/api/etherscan/statistic/StatisticTokenSupplyApiTest.java +++ b/src/test/java/io/api/etherscan/statistic/StatisticTokenSupplyApiTest.java @@ -1,8 +1,7 @@ package io.api.etherscan.statistic; -import io.api.etherscan.core.impl.EtherScanApi; +import io.api.ApiRunner; import io.api.etherscan.error.InvalidAddressException; -import org.junit.Assert; import org.junit.Test; import java.math.BigInteger; @@ -13,25 +12,23 @@ * @author GoodforGod * @since 03.11.2018 */ -public class StatisticTokenSupplyApiTest extends Assert { - - private final EtherScanApi api = new EtherScanApi(); +public class StatisticTokenSupplyApiTest extends ApiRunner { @Test public void correct() { - BigInteger supply = api.stats().supply("0x57d90b64a1a57749b0f932f1a3395792e12e7055"); + BigInteger supply = getApi().stats().supply("0x57d90b64a1a57749b0f932f1a3395792e12e7055"); assertNotNull(supply); - assertNotEquals(0, supply); + assertNotEquals(BigInteger.ZERO, supply); } @Test(expected = InvalidAddressException.class) public void invalidParamWithError() { - BigInteger supply = api.stats().supply("0x7d90b64a1a57749b0f932f1a3395792e12e7055"); + BigInteger supply = getApi().stats().supply("0x7d90b64a1a57749b0f932f1a3395792e12e7055"); } @Test public void correctParamWithEmptyExpectedResult() { - BigInteger supply = api.stats().supply("0x51d90b64a1a57749b0f932f1a3395792e12e7055"); + 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 2f36d79..25320cc 100644 --- a/src/test/java/io/api/etherscan/transaction/TransactionExecApiTest.java +++ b/src/test/java/io/api/etherscan/transaction/TransactionExecApiTest.java @@ -1,9 +1,8 @@ package io.api.etherscan.transaction; -import io.api.etherscan.core.impl.EtherScanApi; +import io.api.ApiRunner; import io.api.etherscan.error.InvalidTxHashException; import io.api.etherscan.model.Status; -import org.junit.Assert; import org.junit.Test; import java.util.Optional; @@ -14,13 +13,11 @@ * @author GoodforGod * @since 03.11.2018 */ -public class TransactionExecApiTest extends Assert { - - private final EtherScanApi api = new EtherScanApi(); +public class TransactionExecApiTest extends ApiRunner { @Test public void correct() { - Optional status = api.txs().execStatus("0x15f8e5ea1079d9a0bb04a4c58ae5fe7654b5b2b4463375ff7ffb490aa0032f3a"); + Optional status = getApi().txs().execStatus("0x15f8e5ea1079d9a0bb04a4c58ae5fe7654b5b2b4463375ff7ffb490aa0032f3a"); assertTrue(status.isPresent()); assertTrue(status.get().haveError()); assertNotNull(status.get().getErrDescription()); @@ -33,12 +30,12 @@ public void correct() { @Test(expected = InvalidTxHashException.class) public void invalidParamWithError() { - api.txs().execStatus("0xb513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b"); + getApi().txs().execStatus("0xb513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b"); } @Test public void correctParamWithEmptyExpectedResult() { - Optional status = api.txs().execStatus("0x55f8e5ea1079d9a0bb04a4c58ae5fe7654b5b2b4463375ff7ffb490aa0032f3a"); + 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 c716c8a..a459355 100644 --- a/src/test/java/io/api/etherscan/transaction/TransactionReceiptApiTest.java +++ b/src/test/java/io/api/etherscan/transaction/TransactionReceiptApiTest.java @@ -1,8 +1,7 @@ package io.api.etherscan.transaction; -import io.api.etherscan.core.impl.EtherScanApi; +import io.api.ApiRunner; import io.api.etherscan.error.InvalidTxHashException; -import org.junit.Assert; import org.junit.Test; import java.util.Optional; @@ -13,25 +12,25 @@ * @author GoodforGod * @since 03.11.2018 */ -public class TransactionReceiptApiTest extends Assert { - - private final EtherScanApi api = new EtherScanApi(); +public class TransactionReceiptApiTest extends ApiRunner { @Test public void correct() { - Optional status = api.txs().receiptStatus("0x513c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76"); + Optional status = getApi().txs() + .receiptStatus("0x513c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76"); assertTrue(status.isPresent()); assertTrue(status.get()); } @Test(expected = InvalidTxHashException.class) public void invalidParamWithError() { - api.txs().receiptStatus("0x13c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76"); + getApi().txs().receiptStatus("0x13c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76"); } @Test public void correctParamWithEmptyExpectedResult() { - Optional status = api.txs().receiptStatus("0x113c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76"); + 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 19b2ea9..acc7b43 100644 --- a/src/test/java/io/api/manager/QueueManagerTest.java +++ b/src/test/java/io/api/manager/QueueManagerTest.java @@ -1,9 +1,9 @@ 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.Assert; import org.junit.Test; /** @@ -12,40 +12,40 @@ * @author GoodforGod * @since 03.11.2018 */ -public class QueueManagerTest extends Assert { +public class QueueManagerTest extends ApiRunner { @Test public void fakeManager() { IQueueManager fakeManager = new FakeQueueManager(); - assertTrue(fakeManager.takeTurn()); - assertTrue(fakeManager.takeTurn()); - assertTrue(fakeManager.takeTurn()); - assertTrue(fakeManager.takeTurn()); - assertTrue(fakeManager.takeTurn()); - assertTrue(fakeManager.takeTurn()); + fakeManager.takeTurn(); + fakeManager.takeTurn(); + fakeManager.takeTurn(); + fakeManager.takeTurn(); + fakeManager.takeTurn(); + fakeManager.takeTurn(); } @Test(timeout = 3500) public void queueManager() { IQueueManager queueManager = new QueueManager(1, 3); - assertTrue(queueManager.takeTurn()); + queueManager.takeTurn(); queueManager.takeTurn(); } @Test(timeout = 4500) public void queueManagerWithDelay() { IQueueManager queueManager = new QueueManager(1, 2, 2); - assertTrue(queueManager.takeTurn()); + queueManager.takeTurn(); queueManager.takeTurn(); } @Test public void queueManagerTimeout() { IQueueManager queueManager = new QueueManager(1, 3); - assertTrue(queueManager.takeTurn()); + queueManager.takeTurn(); long start = System.currentTimeMillis(); queueManager.takeTurn(); long end = System.currentTimeMillis(); - assertEquals(3, Math.round((double)(end - start)/1000)); + assertEquals(3, Math.round((double) (end - start) / 1000)); } } diff --git a/src/test/java/io/api/util/BasicUtilsTests.java b/src/test/java/io/api/util/BasicUtilsTests.java index 2c5ad71..c35bada 100644 --- a/src/test/java/io/api/util/BasicUtilsTests.java +++ b/src/test/java/io/api/util/BasicUtilsTests.java @@ -1,10 +1,10 @@ package io.api.util; import com.google.gson.Gson; +import io.api.ApiRunner; import io.api.etherscan.error.EtherScanException; import io.api.etherscan.error.ParseException; import io.api.etherscan.model.utility.StringResponseTO; -import org.junit.Assert; import org.junit.Test; import java.util.ArrayList; @@ -18,7 +18,7 @@ * @author GoodforGod * @since 13.11.2018 */ -public class BasicUtilsTests extends Assert { +public class BasicUtilsTests extends ApiRunner { @Test(expected = EtherScanException.class) public void responseValidateEmpty() { From bb83e216e06214ef84a3ca6a2fdf30de4f4d666c Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 18 Oct 2020 15:54:19 +0300 Subject: [PATCH 030/112] [1.1.0-SNAPSHOT] Code style applied Experimental features marked --- src/main/java/io/api/etherscan/core/IEventsApi.java | 3 +++ src/main/java/io/api/etherscan/core/impl/EtherScanApi.java | 3 +-- .../java/io/api/etherscan/core/impl/EventsApiProvider.java | 3 +++ src/main/java/io/api/etherscan/manager/impl/QueueManager.java | 1 - 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/api/etherscan/core/IEventsApi.java b/src/main/java/io/api/etherscan/core/IEventsApi.java index 12c19db..8894b87 100644 --- a/src/main/java/io/api/etherscan/core/IEventsApi.java +++ b/src/main/java/io/api/etherscan/core/IEventsApi.java @@ -3,6 +3,7 @@ import io.api.etherscan.error.ApiException; import io.api.etherscan.model.event.IEvent; import io.api.etherscan.model.query.impl.LogQuery; +import org.jetbrains.annotations.ApiStatus.Experimental; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -10,6 +11,7 @@ /** * EtherScan - API Descriptions https://etherscan.io/apis#logs */ +@Experimental public interface IEventsApi { /** @@ -22,6 +24,7 @@ public interface IEventsApi { * * @see io.api.etherscan.model.query.impl.LogQueryBuilder */ + @Experimental @NotNull List events(LogQuery query) 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 0dd0f9e..8cd5d46 100644 --- a/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java +++ b/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java @@ -68,8 +68,7 @@ public EtherScanApi(final String apiKey, this(apiKey, network, executorSupplier, DEFAULT_KEY.equals(apiKey) ? QueueManager.DEFAULT_KEY_QUEUE - : new FakeQueueManager() - ); + : new FakeQueueManager()); } public EtherScanApi(final String apiKey, diff --git a/src/main/java/io/api/etherscan/core/impl/EventsApiProvider.java b/src/main/java/io/api/etherscan/core/impl/EventsApiProvider.java index d6bfa25..4b42259 100644 --- a/src/main/java/io/api/etherscan/core/impl/EventsApiProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/EventsApiProvider.java @@ -8,6 +8,7 @@ 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.ApiStatus.Experimental; import org.jetbrains.annotations.NotNull; import java.util.Collections; @@ -20,6 +21,7 @@ * @see IEventsApi * */ +@Experimental public class EventsApiProvider extends BasicProvider implements IEventsApi { private static final String ACT_LOGS_PARAM = ACT_PREFIX + "getLogs"; @@ -30,6 +32,7 @@ public class EventsApiProvider extends BasicProvider implements IEventsApi { super(queue, "logs", baseUrl, executor); } + @Experimental @NotNull @Override public List events(final LogQuery query) throws ApiException { 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 a9a32dd..8041734 100644 --- a/src/main/java/io/api/etherscan/manager/impl/QueueManager.java +++ b/src/main/java/io/api/etherscan/manager/impl/QueueManager.java @@ -1,6 +1,5 @@ package io.api.etherscan.manager.impl; -import io.api.etherscan.core.impl.EtherScanApi; import io.api.etherscan.manager.IQueueManager; import java.util.concurrent.*; From e74930b66c0a35227f9ae220c571da29c18c4b03 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 18 Oct 2020 15:55:15 +0300 Subject: [PATCH 031/112] [1.1.0-SNAPSHOT] Experimental features removed as untested and badly designed --- .../io/api/etherscan/core/IEventsApi.java | 30 ---------- .../core/impl/EventsApiProvider.java | 55 ------------------- .../io/api/etherscan/model/event/IEvent.java | 33 ----------- .../model/event/impl/ApprovalEvent.java | 11 ---- .../model/event/impl/DepositEvent.java | 11 ---- .../api/etherscan/model/event/impl/Event.java | 38 ------------- .../etherscan/model/event/impl/MintEvent.java | 12 ---- .../etherscan/model/event/impl/SyncEvent.java | 11 ---- .../model/event/impl/TransferErc20Event.java | 32 ----------- .../model/event/impl/WithdrawEvent.java | 11 ---- 10 files changed, 244 deletions(-) delete mode 100644 src/main/java/io/api/etherscan/core/IEventsApi.java delete mode 100644 src/main/java/io/api/etherscan/core/impl/EventsApiProvider.java delete mode 100644 src/main/java/io/api/etherscan/model/event/IEvent.java delete mode 100644 src/main/java/io/api/etherscan/model/event/impl/ApprovalEvent.java delete mode 100644 src/main/java/io/api/etherscan/model/event/impl/DepositEvent.java delete mode 100644 src/main/java/io/api/etherscan/model/event/impl/Event.java delete mode 100644 src/main/java/io/api/etherscan/model/event/impl/MintEvent.java delete mode 100644 src/main/java/io/api/etherscan/model/event/impl/SyncEvent.java delete mode 100644 src/main/java/io/api/etherscan/model/event/impl/TransferErc20Event.java delete mode 100644 src/main/java/io/api/etherscan/model/event/impl/WithdrawEvent.java diff --git a/src/main/java/io/api/etherscan/core/IEventsApi.java b/src/main/java/io/api/etherscan/core/IEventsApi.java deleted file mode 100644 index 8894b87..0000000 --- a/src/main/java/io/api/etherscan/core/IEventsApi.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.api.etherscan.core; - -import io.api.etherscan.error.ApiException; -import io.api.etherscan.model.event.IEvent; -import io.api.etherscan.model.query.impl.LogQuery; -import org.jetbrains.annotations.ApiStatus.Experimental; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - -/** - * EtherScan - API Descriptions https://etherscan.io/apis#logs - */ -@Experimental -public interface IEventsApi { - - /** - * This is a high-level alternative to the ILogsApi and an 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 - */ - @Experimental - @NotNull - List events(LogQuery query) throws ApiException; -} diff --git a/src/main/java/io/api/etherscan/core/impl/EventsApiProvider.java b/src/main/java/io/api/etherscan/core/impl/EventsApiProvider.java deleted file mode 100644 index 4b42259..0000000 --- a/src/main/java/io/api/etherscan/core/impl/EventsApiProvider.java +++ /dev/null @@ -1,55 +0,0 @@ -package io.api.etherscan.core.impl; - -import io.api.etherscan.core.IEventsApi; -import io.api.etherscan.error.ApiException; -import io.api.etherscan.executor.IHttpExecutor; -import io.api.etherscan.manager.IQueueManager; -import io.api.etherscan.model.event.IEvent; -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.ApiStatus.Experimental; -import org.jetbrains.annotations.NotNull; - -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -/** - * Logs API Implementation - * - * @see IEventsApi - * - */ -@Experimental -public class EventsApiProvider extends BasicProvider implements IEventsApi { - - private static final String ACT_LOGS_PARAM = ACT_PREFIX + "getLogs"; - - EventsApiProvider(final IQueueManager queue, - final String baseUrl, - final IHttpExecutor executor) { - super(queue, "logs", baseUrl, executor); - } - - @Experimental - @NotNull - @Override - public List events(final LogQuery query) throws ApiException { - final String urlParams = ACT_LOGS_PARAM + query.getParams(); - final LogResponseTO response = getRequest(urlParams, LogResponseTO.class); - BasicUtils.validateTxResponse(response); - - if (BasicUtils.isEmpty(response.getResult())) { - return Collections.emptyList(); - } ; - return response - .getResult() - .stream() - .map((log) -> { - String eventTypeHash = log.getTopics().get(0); - return IEvent.createEvent(eventTypeHash, log); - }) - .collect(Collectors.toList()); - } -} diff --git a/src/main/java/io/api/etherscan/model/event/IEvent.java b/src/main/java/io/api/etherscan/model/event/IEvent.java deleted file mode 100644 index 47e2e2c..0000000 --- a/src/main/java/io/api/etherscan/model/event/IEvent.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.api.etherscan.model.event; - -import io.api.etherscan.error.ApiException; -import io.api.etherscan.error.EventModelException; -import io.api.etherscan.model.Log; - -import java.util.HashMap; -import java.util.Map; - -public interface IEvent { - - static final Map> subTypes = new HashMap<>(); - - void setLog(Log log); - - static void registerEventType(String typeHash, Class clazz) { - subTypes.put(typeHash, clazz); - } - - static IEvent createEvent(String typeHash, Log log) { - if (null == typeHash) { - throw new EventModelException("Event type hash cannot be null"); - } - Class clazz = subTypes.get(typeHash); - try { - IEvent evt = (IEvent) clazz.newInstance(); - evt.setLog(log); - return evt; - } catch (InstantiationException | IllegalAccessException ex) { - throw new ApiException("Client-side error instantiating Event object", ex); - } - } -} diff --git a/src/main/java/io/api/etherscan/model/event/impl/ApprovalEvent.java b/src/main/java/io/api/etherscan/model/event/impl/ApprovalEvent.java deleted file mode 100644 index ba52faf..0000000 --- a/src/main/java/io/api/etherscan/model/event/impl/ApprovalEvent.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.api.etherscan.model.event.impl; - -import io.api.etherscan.model.event.IEvent; - -public class ApprovalEvent extends Event { - - static final String eventTypeHash = "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"; - static { - IEvent.registerEventType(ApprovalEvent.eventTypeHash, ApprovalEvent.class); - } -} diff --git a/src/main/java/io/api/etherscan/model/event/impl/DepositEvent.java b/src/main/java/io/api/etherscan/model/event/impl/DepositEvent.java deleted file mode 100644 index fe3ad06..0000000 --- a/src/main/java/io/api/etherscan/model/event/impl/DepositEvent.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.api.etherscan.model.event.impl; - -import io.api.etherscan.model.event.IEvent; - -public class DepositEvent extends Event { - - static final String eventTypeHash = "0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c"; - static { - IEvent.registerEventType(DepositEvent.eventTypeHash, DepositEvent.class); - } -} diff --git a/src/main/java/io/api/etherscan/model/event/impl/Event.java b/src/main/java/io/api/etherscan/model/event/impl/Event.java deleted file mode 100644 index 6f30e25..0000000 --- a/src/main/java/io/api/etherscan/model/event/impl/Event.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.api.etherscan.model.event.impl; - -import io.api.etherscan.model.Log; -import io.api.etherscan.model.event.IEvent; - -/** - * Base class for a higher-level API on top of {@link Log}. Each Event class has - * an identifying hash - */ -public class Event implements IEvent { - - static String eventTypeHash; - - private Log log; - - String address; - - public static String getEventTypeHash() { - return eventTypeHash; - } - - public Log getLog() { - return log; - } - - public String getAddress() { - return address; - } - - public void setLog(Log log) { - this.log = log; - } - - public void setAddress(String address) { - this.address = address; - } - -} diff --git a/src/main/java/io/api/etherscan/model/event/impl/MintEvent.java b/src/main/java/io/api/etherscan/model/event/impl/MintEvent.java deleted file mode 100644 index cd3b6d5..0000000 --- a/src/main/java/io/api/etherscan/model/event/impl/MintEvent.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.api.etherscan.model.event.impl; - -import io.api.etherscan.model.event.IEvent; - -public class MintEvent extends Event { - - static final String eventTypeHash = "0x4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f"; - static { - IEvent.registerEventType(MintEvent.eventTypeHash, MintEvent.class); - } - -} diff --git a/src/main/java/io/api/etherscan/model/event/impl/SyncEvent.java b/src/main/java/io/api/etherscan/model/event/impl/SyncEvent.java deleted file mode 100644 index 76b4931..0000000 --- a/src/main/java/io/api/etherscan/model/event/impl/SyncEvent.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.api.etherscan.model.event.impl; - -import io.api.etherscan.model.event.IEvent; - -public class SyncEvent extends Event { - - static final String eventTypeHash = "0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1"; - static { - IEvent.registerEventType(SyncEvent.eventTypeHash, SyncEvent.class); - } -} diff --git a/src/main/java/io/api/etherscan/model/event/impl/TransferErc20Event.java b/src/main/java/io/api/etherscan/model/event/impl/TransferErc20Event.java deleted file mode 100644 index ce16958..0000000 --- a/src/main/java/io/api/etherscan/model/event/impl/TransferErc20Event.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.api.etherscan.model.event.impl; - -import io.api.etherscan.model.event.IEvent; - -public class TransferErc20Event extends Event { - - static final String eventTypeHash = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"; - static { - IEvent.registerEventType(TransferErc20Event.eventTypeHash, TransferErc20Event.class); - } - - String fromAddress; - - String toAddress; - - public String getFromAddress() { - return fromAddress; - } - - public void setFromAddress(String fromAddress) { - this.fromAddress = fromAddress; - } - - public String getToAddress() { - return toAddress; - } - - public void setToAddress(String toAddress) { - this.toAddress = toAddress; - } - -} diff --git a/src/main/java/io/api/etherscan/model/event/impl/WithdrawEvent.java b/src/main/java/io/api/etherscan/model/event/impl/WithdrawEvent.java deleted file mode 100644 index 23036bf..0000000 --- a/src/main/java/io/api/etherscan/model/event/impl/WithdrawEvent.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.api.etherscan.model.event.impl; - -import io.api.etherscan.model.event.IEvent; - -public class WithdrawEvent extends Event { - - static final String eventTypeHash = "0x7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65"; - static { - IEvent.registerEventType(WithdrawEvent.eventTypeHash, WithdrawEvent.class); - } -} From 93e6b02e6adbcdf528650e055566d5cf8b2fe377 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 18 Oct 2020 16:02:55 +0300 Subject: [PATCH 032/112] [1.1.0-SNAPSHOT] gradlew permission added --- gradlew | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 gradlew diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 From ea512d1612ae1f5c9454c80e6082bb3a04b38e71 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 18 Oct 2020 16:10:17 +0300 Subject: [PATCH 033/112] [1.1.0-SNAPSHOT] FailFast true added [1.1.0-SNAPSHOT] Only java 11 for testing is left [1.1.0-SNAPSHOT] Tests on push disabled for RateLimit not exceed [1.1.0-SNAPSHOT] CI api key env setted [1.1.0-SNAPSHOT] QueueManager 7 sec instead of 6 set [1.1.0-SNAPSHOT] Test fixed with correct address for ABI Debug logging removed PersonalQueue optimized [1.1.0-SNAPSHOT] Key removed [1.1.0-SNAPSHOT] Weak queue set for tests [1.1.0-SNAPSHOT] Rest before timeout check for api not exceed rate --- .github/workflows/gradle.yml | 9 ++++----- build.gradle | 2 ++ .../etherscan/core/impl/BasicProvider.java | 10 ---------- .../etherscan/manager/impl/QueueManager.java | 16 ++++++++-------- src/test/java/io/api/ApiRunner.java | 19 ++++++++++++------- .../io/api/etherscan/EtherScanApiTest.java | 6 ++++-- .../etherscan/contract/ContractApiTest.java | 4 ++-- 7 files changed, 32 insertions(+), 34 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 655e4ad..6e68558 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -1,10 +1,6 @@ name: Java CI on: - push: - branches: - - master - - dev schedule: - cron: "0 12 1 * *" pull_request: @@ -17,7 +13,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - java: [ '1.8', '11' ] + java: [ '11' ] name: Java ${{ matrix.java }} setup steps: @@ -30,9 +26,12 @@ jobs: - name: Build with Gradle run: ./gradlew build jacocoTestReport + env: + API_KEY: ${{ secrets.API_KEY }} - name: Analyze with SonarQube run: ./gradlew sonarqube env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + API_KEY: ${{ secrets.API_KEY }} diff --git a/build.gradle b/build.gradle index a68159c..1d07a61 100644 --- a/build.gradle +++ b/build.gradle @@ -43,6 +43,8 @@ dependencies { } test { + failFast = true + useJUnit() testLogging { events "passed", "skipped", "failed" 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 f2be0d2..cf16337 100644 --- a/src/main/java/io/api/etherscan/core/impl/BasicProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/BasicProvider.java @@ -7,14 +7,10 @@ import io.api.etherscan.error.RateLimitException; import io.api.etherscan.executor.IHttpExecutor; import io.api.etherscan.manager.IQueueManager; -import io.api.etherscan.manager.impl.QueueManager; import io.api.etherscan.model.utility.StringResponseTO; import io.api.etherscan.util.BasicUtils; -import java.time.LocalTime; import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; /** * Base provider for API Implementations @@ -25,8 +21,6 @@ */ abstract class BasicProvider { - private static final Logger logger = Logger.getLogger(QueueManager.class.getName()); - static final int MAX_END_BLOCK = Integer.MAX_VALUE; static final int MIN_START_BLOCK = 0; @@ -76,9 +70,7 @@ T convert(final String json, final Class tClass) { } String getRequest(final String urlParameters) { - logger.log(Level.SEVERE, "ASKED - " + LocalTime.now()); queue.takeTurn(); - logger.log(Level.SEVERE, "GRANTED - " + LocalTime.now()); final String url = baseUrl + module + urlParameters; final String result = executor.get(url); if (BasicUtils.isEmpty(result)) @@ -88,9 +80,7 @@ String getRequest(final String urlParameters) { } String postRequest(final String urlParameters, final String dataToPost) { - logger.log(Level.SEVERE, "ASKED - " + LocalTime.now()); queue.takeTurn(); - logger.log(Level.SEVERE, "GRANTED - " + LocalTime.now()); final String url = baseUrl + module + urlParameters; return executor.post(url, dataToPost); } 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 8041734..517883c 100644 --- a/src/main/java/io/api/etherscan/manager/impl/QueueManager.java +++ b/src/main/java/io/api/etherscan/manager/impl/QueueManager.java @@ -5,7 +5,7 @@ import java.util.concurrent.*; /** - * Queue implementation With size and reset time as params + * Queue Semaphore implementation with size and reset time as params * * @see IQueueManager * @@ -14,19 +14,19 @@ */ public class QueueManager implements IQueueManager { - public static final QueueManager DEFAULT_KEY_QUEUE = new QueueManager(1, 6); - public static final QueueManager PERSONAL_KEY_QUEUE = new QueueManager(5, 1); + public static final QueueManager DEFAULT_KEY_QUEUE = new QueueManager(1, 7); + public static final QueueManager PERSONAL_KEY_QUEUE = new QueueManager(2, 1); private final Semaphore semaphore; - public QueueManager(int queueSize, int queueResetTimeInSec) { - this(queueSize, queueResetTimeInSec, queueResetTimeInSec); + public QueueManager(int size, int resetInSec) { + this(size, resetInSec, resetInSec); } - public QueueManager(int queueSize, int queueResetTimeInSec, int delayInSec) { - this.semaphore = new Semaphore(queueSize); + public QueueManager(int size, int queueResetTimeInSec, int delayInSec) { + this.semaphore = new Semaphore(size); Executors.newSingleThreadScheduledExecutor() - .scheduleAtFixedRate(releaseLocks(queueSize), delayInSec, queueResetTimeInSec, TimeUnit.SECONDS); + .scheduleAtFixedRate(releaseLocks(size), delayInSec, queueResetTimeInSec, TimeUnit.SECONDS); } @Override diff --git a/src/test/java/io/api/ApiRunner.java b/src/test/java/io/api/ApiRunner.java index 75b3e4e..6f608d8 100644 --- a/src/test/java/io/api/ApiRunner.java +++ b/src/test/java/io/api/ApiRunner.java @@ -11,21 +11,26 @@ public class ApiRunner extends Assert { private static final EtherScanApi apiRopsten; private static final EtherScanApi apiRinkeby; private static final EtherScanApi apiKovan; + private static final String key; static { final String apiKey = System.getenv("API_KEY"); - final String keyOrDefault = (apiKey == null || apiKey.isEmpty()) + key = (apiKey == null || apiKey.isEmpty()) ? EtherScanApi.DEFAULT_KEY : apiKey; - final QueueManager queue = keyOrDefault.equals(EtherScanApi.DEFAULT_KEY) + final QueueManager queue = key.equals(EtherScanApi.DEFAULT_KEY) ? QueueManager.DEFAULT_KEY_QUEUE - : QueueManager.PERSONAL_KEY_QUEUE; + : new QueueManager(1, 2); - api = new EtherScanApi(keyOrDefault, EthNetwork.MAINNET, queue); - apiRopsten = new EtherScanApi(keyOrDefault, EthNetwork.ROPSTEN, queue); - apiRinkeby = new EtherScanApi(keyOrDefault, EthNetwork.RINKEBY, queue); - apiKovan = new EtherScanApi(keyOrDefault, EthNetwork.KOVAN, queue); + api = new EtherScanApi(key, EthNetwork.MAINNET, queue); + apiRopsten = new EtherScanApi(key, EthNetwork.ROPSTEN, queue); + apiRinkeby = new EtherScanApi(key, EthNetwork.RINKEBY, queue); + apiKovan = new EtherScanApi(key, EthNetwork.KOVAN, queue); + } + + public static String getKey() { + return key; } public static EtherScanApi getApi() { diff --git a/src/test/java/io/api/etherscan/EtherScanApiTest.java b/src/test/java/io/api/etherscan/EtherScanApiTest.java index c478b59..5071a68 100644 --- a/src/test/java/io/api/etherscan/EtherScanApiTest.java +++ b/src/test/java/io/api/etherscan/EtherScanApiTest.java @@ -13,6 +13,7 @@ import org.junit.Test; import java.util.List; +import java.util.concurrent.TimeUnit; import java.util.function.Supplier; /** @@ -75,9 +76,10 @@ public void noTimeoutUnlimitedAwait() { } @Test(expected = ApiTimeoutException.class) - public void timeout() { + public void timeout() throws InterruptedException { + TimeUnit.SECONDS.sleep(5); Supplier supplier = () -> new HttpExecutor(300, 300); - EtherScanApi api = new EtherScanApi(EthNetwork.KOVAN, supplier); + EtherScanApi api = new EtherScanApi(getKey(), EthNetwork.KOVAN, supplier); List blocks = api.account().minedBlocks("0x0010f94b296A852aAac52EA6c5Ac72e03afD032D"); assertNotNull(blocks); } diff --git a/src/test/java/io/api/etherscan/contract/ContractApiTest.java b/src/test/java/io/api/etherscan/contract/ContractApiTest.java index 9d43c5f..6b4d7d8 100644 --- a/src/test/java/io/api/etherscan/contract/ContractApiTest.java +++ b/src/test/java/io/api/etherscan/contract/ContractApiTest.java @@ -34,8 +34,8 @@ public void invalidParamWithError() { @Test public void correctParamWithEmptyExpectedResult() { - Abi abi = getApi().contract().contractAbi("0xBB1bc244D798123fDe783fCc1C72d3Bb8C189413"); + Abi abi = getApi().contract().contractAbi("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413"); assertNotNull(abi); - assertFalse(abi.isVerified()); + assertTrue(abi.isVerified()); } } From 8c0754d42993adf84422bc46c4c8d59af0740a3d Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 18 Oct 2020 16:54:04 +0300 Subject: [PATCH 034/112] [1.1.0-SNAPSHOT] Master push CI added --- .github/workflows/gradle.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 6e68558..03c2e11 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -1,6 +1,9 @@ name: Java CI -on: +on: + push: + branches: + - master schedule: - cron: "0 12 1 * *" pull_request: From 8ba73e8027e5a3590f21f0d00045b4bbd2e0cac1 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 18 Oct 2020 17:15:59 +0300 Subject: [PATCH 035/112] [1.1.0] SonarQube issues fixed README.md updated --- README.md | 8 +++++++- gradle.properties | 2 +- .../io/api/etherscan/core/impl/BasicProvider.java | 6 ++---- .../etherscan/manager/impl/FakeQueueManager.java | 5 ++++- src/main/java/io/api/etherscan/model/Balance.java | 3 ++- src/main/java/io/api/etherscan/model/BaseTx.java | 13 +++++++------ src/main/java/io/api/etherscan/model/Block.java | 2 +- src/main/java/io/api/etherscan/model/Log.java | 11 ++++++----- src/main/java/io/api/etherscan/model/Price.java | 4 ++-- src/main/java/io/api/etherscan/model/Status.java | 4 +++- .../java/io/api/etherscan/model/TokenBalance.java | 4 ++-- src/main/java/io/api/etherscan/model/Tx.java | 5 +++-- .../java/io/api/etherscan/model/TxInternal.java | 4 +++- src/main/java/io/api/etherscan/model/Wei.java | 4 ++-- .../api/etherscan/model/utility/BaseResponseTO.java | 2 +- .../io/api/etherscan/model/utility/BlockParam.java | 4 ++-- src/main/java/io/api/etherscan/util/BasicUtils.java | 12 ++++-------- .../etherscan/statistic/StatisticPriceApiTest.java | 4 ++-- src/test/java/io/api/manager/QueueManagerTest.java | 3 +++ 19 files changed, 57 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 733a2ed..545c0e7 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # java-etherscan-api +[![GitHub Action](https://github.com/goodforgod/java-etherscan-api/workflows/Java%20CI/badge.svg)](https://github.com/GoodforGod/dummymaker/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_dummymaker) +[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=GoodforGod_java-etherscan-api&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=GoodforGod_dummymaker) +[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=GoodforGod_java-etherscan-api&metric=ncloc)](https://sonarcloud.io/dashboard?id=GoodforGod_dummymaker) [![Jitpack](https://jitpack.io/v/iSnow/java-etherscan-api.svg)](https://jitpack.io/#GoodforGod/java-etherscan-api) [Etherscan](https://etherscan.io/apis) Java API implementation. @@ -163,6 +167,8 @@ Token API methods migrated to [Account](#account-api) & [Stats](#stats-api) resp ## Version History +**1.1.0** - Improved error handling, QueueManager improved, Gradle 6.7 instead of Maven, GitHub CI, Sonarcloud analyzer, dependencies updated. + **1.0.2** - Minor http client improvements. **1.0.1** - Gorli & TOBALABA networks support. @@ -171,4 +177,4 @@ Token API methods migrated to [Account](#account-api) & [Stats](#stats-api) resp ## License -This project is licensed under the MIT - see the [LICENSE](LICENSE) file for details. +This project licensed under the MIT - see the [LICENSE](LICENSE) file for details. diff --git a/gradle.properties b/gradle.properties index 0c89028..1fa1ad4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ groupId=com.github.goodforgod artifactId=java-etherscan-api -artifactVersion=1.1.0-SNAPSHOT +artifactVersion=1.1.0 buildNumber=1 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 cf16337..b89447a 100644 --- a/src/main/java/io/api/etherscan/core/impl/BasicProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/BasicProvider.java @@ -46,10 +46,8 @@ abstract class BasicProvider { T convert(final String json, final Class tClass) { try { final T t = gson.fromJson(json, tClass); - if (t instanceof StringResponseTO) { - if (((StringResponseTO) t).getResult().startsWith("Max rate limit reached")) { - throw new RateLimitException(((StringResponseTO) t).getResult()); - } + if (t instanceof StringResponseTO && ((StringResponseTO) t).getResult().startsWith("Max rate limit reached")) { + throw new RateLimitException(((StringResponseTO) t).getResult()); } return t; diff --git a/src/main/java/io/api/etherscan/manager/impl/FakeQueueManager.java b/src/main/java/io/api/etherscan/manager/impl/FakeQueueManager.java index d8bc048..e797f8b 100644 --- a/src/main/java/io/api/etherscan/manager/impl/FakeQueueManager.java +++ b/src/main/java/io/api/etherscan/manager/impl/FakeQueueManager.java @@ -11,5 +11,8 @@ public class FakeQueueManager implements IQueueManager { @Override - public void takeTurn() {} + public void takeTurn() { + // no limit or await provided for fake impl so rate limit exception will be + // thrown if too many calls + } } diff --git a/src/main/java/io/api/etherscan/model/Balance.java b/src/main/java/io/api/etherscan/model/Balance.java index 5529a90..cbd8502 100644 --- a/src/main/java/io/api/etherscan/model/Balance.java +++ b/src/main/java/io/api/etherscan/model/Balance.java @@ -3,6 +3,7 @@ import io.api.etherscan.model.utility.BalanceTO; import java.math.BigInteger; +import java.util.Objects; /** * ! NO DESCRIPTION ! @@ -63,7 +64,7 @@ public boolean equals(Object o) { if (!balance.equals(balance1.balance)) return false; - return address != null ? address.equals(balance1.address) : balance1.address == null; + return Objects.equals(address, balance1.address); } @Override diff --git a/src/main/java/io/api/etherscan/model/BaseTx.java b/src/main/java/io/api/etherscan/model/BaseTx.java index 5aea827..a219e57 100644 --- a/src/main/java/io/api/etherscan/model/BaseTx.java +++ b/src/main/java/io/api/etherscan/model/BaseTx.java @@ -5,6 +5,7 @@ import java.math.BigInteger; import java.time.LocalDateTime; import java.time.ZoneOffset; +import java.util.Objects; /** * ! NO DESCRIPTION ! @@ -33,7 +34,7 @@ public long getBlockNumber() { public LocalDateTime getTimeStamp() { if (_timeStamp == null && !BasicUtils.isEmpty(timeStamp)) - _timeStamp = LocalDateTime.ofEpochSecond(Long.valueOf(timeStamp), 0, ZoneOffset.UTC); + _timeStamp = LocalDateTime.ofEpochSecond(Long.parseLong(timeStamp), 0, ZoneOffset.UTC); return _timeStamp; } @@ -81,15 +82,15 @@ public boolean equals(Object o) { if (blockNumber != baseTx.blockNumber) return false; - if (timeStamp != null ? !timeStamp.equals(baseTx.timeStamp) : baseTx.timeStamp != null) + if (!Objects.equals(timeStamp, baseTx.timeStamp)) return false; - if (hash != null ? !hash.equals(baseTx.hash) : baseTx.hash != null) + if (!Objects.equals(hash, baseTx.hash)) return false; - if (from != null ? !from.equals(baseTx.from) : baseTx.from != null) + if (!Objects.equals(from, baseTx.from)) return false; - if (to != null ? !to.equals(baseTx.to) : baseTx.to != null) + if (!Objects.equals(to, baseTx.to)) return false; - return value != null ? value.equals(baseTx.value) : baseTx.value == null; + return Objects.equals(value, baseTx.value); } @Override diff --git a/src/main/java/io/api/etherscan/model/Block.java b/src/main/java/io/api/etherscan/model/Block.java index 2e9b96b..d328841 100644 --- a/src/main/java/io/api/etherscan/model/Block.java +++ b/src/main/java/io/api/etherscan/model/Block.java @@ -26,7 +26,7 @@ public long getBlockNumber() { public LocalDateTime getTimeStamp() { if (_timeStamp == null && !BasicUtils.isEmpty(timeStamp)) - _timeStamp = LocalDateTime.ofEpochSecond(Long.valueOf(timeStamp), 0, ZoneOffset.UTC); + _timeStamp = LocalDateTime.ofEpochSecond(Long.parseLong(timeStamp), 0, ZoneOffset.UTC); return _timeStamp; } diff --git a/src/main/java/io/api/etherscan/model/Log.java b/src/main/java/io/api/etherscan/model/Log.java index 2a7cc09..4649d6b 100644 --- a/src/main/java/io/api/etherscan/model/Log.java +++ b/src/main/java/io/api/etherscan/model/Log.java @@ -6,6 +6,7 @@ import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.List; +import java.util.Objects; /** * ! NO DESCRIPTION ! @@ -124,15 +125,15 @@ public boolean equals(Object o) { Log log = (Log) o; - if (blockNumber != null ? !blockNumber.equals(log.blockNumber) : log.blockNumber != null) + if (!Objects.equals(blockNumber, log.blockNumber)) return false; - if (address != null ? !address.equals(log.address) : log.address != null) + if (!Objects.equals(address, log.address)) return false; - if (transactionHash != null ? !transactionHash.equals(log.transactionHash) : log.transactionHash != null) + if (!Objects.equals(transactionHash, log.transactionHash)) return false; - if (timeStamp != null ? !timeStamp.equals(log.timeStamp) : log.timeStamp != null) + if (!Objects.equals(timeStamp, log.timeStamp)) return false; - return logIndex != null ? logIndex.equals(log.logIndex) : log.logIndex == null; + return Objects.equals(logIndex, log.logIndex); } @Override diff --git a/src/main/java/io/api/etherscan/model/Price.java b/src/main/java/io/api/etherscan/model/Price.java index 9a37592..d2c6d1c 100644 --- a/src/main/java/io/api/etherscan/model/Price.java +++ b/src/main/java/io/api/etherscan/model/Price.java @@ -28,13 +28,13 @@ public double inBtc() { public LocalDateTime usdTimestamp() { if (_ethusd_timestamp == null) - _ethusd_timestamp = LocalDateTime.ofEpochSecond(Long.valueOf(ethusd_timestamp), 0, ZoneOffset.UTC); + _ethusd_timestamp = LocalDateTime.ofEpochSecond(Long.parseLong(ethusd_timestamp), 0, ZoneOffset.UTC); return _ethusd_timestamp; } public LocalDateTime btcTimestamp() { if (_ethbtc_timestamp == null) - _ethbtc_timestamp = LocalDateTime.ofEpochSecond(Long.valueOf(ethbtc_timestamp), 0, ZoneOffset.UTC); + _ethbtc_timestamp = LocalDateTime.ofEpochSecond(Long.parseLong(ethbtc_timestamp), 0, ZoneOffset.UTC); return _ethbtc_timestamp; } diff --git a/src/main/java/io/api/etherscan/model/Status.java b/src/main/java/io/api/etherscan/model/Status.java index 4a1fe18..9683bde 100644 --- a/src/main/java/io/api/etherscan/model/Status.java +++ b/src/main/java/io/api/etherscan/model/Status.java @@ -1,5 +1,7 @@ package io.api.etherscan.model; +import java.util.Objects; + /** * Contract Execution Status * @@ -33,7 +35,7 @@ public boolean equals(Object o) { if (isError != status.isError) return false; - return errDescription != null ? errDescription.equals(status.errDescription) : status.errDescription == null; + return Objects.equals(errDescription, status.errDescription); } @Override diff --git a/src/main/java/io/api/etherscan/model/TokenBalance.java b/src/main/java/io/api/etherscan/model/TokenBalance.java index e198079..d057992 100644 --- a/src/main/java/io/api/etherscan/model/TokenBalance.java +++ b/src/main/java/io/api/etherscan/model/TokenBalance.java @@ -1,6 +1,7 @@ package io.api.etherscan.model; import java.math.BigInteger; +import java.util.Objects; /** * ! NO DESCRIPTION ! @@ -31,8 +32,7 @@ public boolean equals(Object o) { return false; TokenBalance that = (TokenBalance) o; - - return tokenContract != null ? tokenContract.equals(that.tokenContract) : that.tokenContract == null; + return Objects.equals(tokenContract, that.tokenContract); } @Override diff --git a/src/main/java/io/api/etherscan/model/Tx.java b/src/main/java/io/api/etherscan/model/Tx.java index 6fce75b..4136d23 100644 --- a/src/main/java/io/api/etherscan/model/Tx.java +++ b/src/main/java/io/api/etherscan/model/Tx.java @@ -3,6 +3,7 @@ import io.api.etherscan.util.BasicUtils; import java.math.BigInteger; +import java.util.Objects; /** * ! NO DESCRIPTION ! @@ -70,9 +71,9 @@ public boolean equals(Object o) { return false; if (transactionIndex != tx.transactionIndex) return false; - if (blockHash != null ? !blockHash.equals(tx.blockHash) : tx.blockHash != null) + if (!Objects.equals(blockHash, tx.blockHash)) return false; - return isError != null ? isError.equals(tx.isError) : tx.isError == null; + return Objects.equals(isError, tx.isError); } @Override diff --git a/src/main/java/io/api/etherscan/model/TxInternal.java b/src/main/java/io/api/etherscan/model/TxInternal.java index e7b75d9..22c5104 100644 --- a/src/main/java/io/api/etherscan/model/TxInternal.java +++ b/src/main/java/io/api/etherscan/model/TxInternal.java @@ -1,5 +1,7 @@ package io.api.etherscan.model; +import java.util.Objects; + /** * ! NO DESCRIPTION ! * @@ -44,7 +46,7 @@ public boolean equals(Object o) { if (traceId != that.traceId) return false; - return errCode != null ? errCode.equals(that.errCode) : that.errCode == null; + return Objects.equals(errCode, that.errCode); } @Override diff --git a/src/main/java/io/api/etherscan/model/Wei.java b/src/main/java/io/api/etherscan/model/Wei.java index 35f90d0..eddf8d2 100644 --- a/src/main/java/io/api/etherscan/model/Wei.java +++ b/src/main/java/io/api/etherscan/model/Wei.java @@ -1,6 +1,7 @@ package io.api.etherscan.model; import java.math.BigInteger; +import java.util.Objects; /** * ! NO DESCRIPTION ! @@ -46,8 +47,7 @@ public boolean equals(Object o) { return false; Wei wei = (Wei) o; - - return result != null ? result.equals(wei.result) : wei.result == null; + return Objects.equals(result, wei.result); } @Override 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 144deef..d3653e2 100644 --- a/src/main/java/io/api/etherscan/model/utility/BaseResponseTO.java +++ b/src/main/java/io/api/etherscan/model/utility/BaseResponseTO.java @@ -14,7 +14,7 @@ public abstract class BaseResponseTO { private String message; public int getStatus() { - return (BasicUtils.isEmpty(status)) ? -1 : Integer.valueOf(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 cbc5a3e..0f027ec 100644 --- a/src/main/java/io/api/etherscan/model/utility/BlockParam.java +++ b/src/main/java/io/api/etherscan/model/utility/BlockParam.java @@ -8,8 +8,8 @@ */ public class BlockParam { - private long startBlock; - private long endBlock; + private final long startBlock; + private final long endBlock; public BlockParam(long startBlock, long endBlock) { this.startBlock = startBlock; diff --git a/src/main/java/io/api/etherscan/util/BasicUtils.java b/src/main/java/io/api/etherscan/util/BasicUtils.java index 0d2a654..96b855d 100644 --- a/src/main/java/io/api/etherscan/util/BasicUtils.java +++ b/src/main/java/io/api/etherscan/util/BasicUtils.java @@ -26,6 +26,8 @@ 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(); } @@ -42,14 +44,8 @@ public static BlockParam compensateBlocks(long startBlock, long endBlock) { long startCompensated = compensateMinBlock(startBlock); long endCompensated = compensateMaxBlock(endBlock); - final long startFinal = (startCompensated > endCompensated) - ? endCompensated - : startCompensated; - - final long endFinal = (startCompensated > endCompensated) - ? startCompensated - : endCompensated; - + final long startFinal = Math.min(startCompensated, endCompensated); + final long endFinal = Math.max(startCompensated, endCompensated); return new BlockParam(startFinal, endFinal); } diff --git a/src/test/java/io/api/etherscan/statistic/StatisticPriceApiTest.java b/src/test/java/io/api/etherscan/statistic/StatisticPriceApiTest.java index 3245b17..e29a6b1 100644 --- a/src/test/java/io/api/etherscan/statistic/StatisticPriceApiTest.java +++ b/src/test/java/io/api/etherscan/statistic/StatisticPriceApiTest.java @@ -18,8 +18,8 @@ public void correct() { assertNotNull(price); assertNotNull(price.btcTimestamp()); assertNotNull(price.usdTimestamp()); - assertNotEquals(0, price.inBtc()); - assertNotEquals(0, price.inUsd()); + assertNotEquals(0.0, price.inBtc()); + assertNotEquals(0.0, price.inUsd()); assertNotNull(price.toString()); Price empty = new Price(); diff --git a/src/test/java/io/api/manager/QueueManagerTest.java b/src/test/java/io/api/manager/QueueManagerTest.java index acc7b43..74e674c 100644 --- a/src/test/java/io/api/manager/QueueManagerTest.java +++ b/src/test/java/io/api/manager/QueueManagerTest.java @@ -23,6 +23,7 @@ public void fakeManager() { fakeManager.takeTurn(); fakeManager.takeTurn(); fakeManager.takeTurn(); + assertNotNull(fakeManager); } @Test(timeout = 3500) @@ -30,6 +31,7 @@ public void queueManager() { IQueueManager queueManager = new QueueManager(1, 3); queueManager.takeTurn(); queueManager.takeTurn(); + assertNotNull(queueManager); } @Test(timeout = 4500) @@ -37,6 +39,7 @@ public void queueManagerWithDelay() { IQueueManager queueManager = new QueueManager(1, 2, 2); queueManager.takeTurn(); queueManager.takeTurn(); + assertNotNull(queueManager); } @Test From 5030e1ca9c391b13a16e5d88a50519228e1792cf Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 4 Apr 2021 11:03:02 +0300 Subject: [PATCH 036/112] README.md updated and improved build.gradle plugins updated --- README.md | 16 ++++++++-------- build.gradle | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 545c0e7..352952b 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ -# java-etherscan-api +# Java EtherScan API -[![GitHub Action](https://github.com/goodforgod/java-etherscan-api/workflows/Java%20CI/badge.svg)](https://github.com/GoodforGod/dummymaker/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_dummymaker) -[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=GoodforGod_java-etherscan-api&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=GoodforGod_dummymaker) -[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=GoodforGod_java-etherscan-api&metric=ncloc)](https://sonarcloud.io/dashboard?id=GoodforGod_dummymaker) -[![Jitpack](https://jitpack.io/v/iSnow/java-etherscan-api.svg)](https://jitpack.io/#GoodforGod/java-etherscan-api) +[![GitHub Action](https://github.com/goodforgod/java-etherscan-api/workflows/Java%20CI/badge.svg)](https://github.com/GoodforGod/java-etherscan-api/actions?query=workflow%3A%22Java+CI%22) +[![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](https://etherscan.io/apis) Java API implementation. +[Etherscan.io](https://etherscan.io/apis) Java API implementation. -Library supports all available EtherScan *API* calls for all available *Ethereum Networks*. +Library supports all available EtherScan *API* calls for all available *Ethereum Networks* for *etherscan.io* ## Dependency :rocket: **Maven** diff --git a/build.gradle b/build.gradle index 1d07a61..638baf3 100644 --- a/build.gradle +++ b/build.gradle @@ -3,8 +3,8 @@ plugins { id 'java-library' id 'maven-publish' - id 'org.sonarqube' version '3.0' - id 'com.diffplug.gradle.spotless' version '4.4.0' + id 'org.sonarqube' version '3.1.1' + id 'com.diffplug.spotless' version '5.11.0' } repositories { From de595d2fb729a1a7addf54c0fb1c19e1748a49a7 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 31 Oct 2021 16:33:19 +0300 Subject: [PATCH 037/112] codestyle.xml 80->100 comment per line Gradle wrapper updated yo 7.2 build.gradle updated and cleanup Code style applied --- README.md | 43 ++++--- build.gradle | 109 +++++++++--------- config/codestyle.xml | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 59203 -> 59536 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 2 +- .../io/api/etherscan/core/IAccountApi.java | 4 +- .../java/io/api/etherscan/core/ILogsApi.java | 3 +- .../java/io/api/etherscan/core/IProxyApi.java | 33 +++--- .../api/etherscan/core/ITransactionApi.java | 6 +- .../api/etherscan/manager/IQueueManager.java | 4 +- src/main/java/io/api/etherscan/model/Log.java | 7 +- 12 files changed, 105 insertions(+), 110 deletions(-) diff --git a/README.md b/README.md index 352952b..cd88aaa 100644 --- a/README.md +++ b/README.md @@ -11,22 +11,23 @@ Library supports all available EtherScan *API* calls for all available *Ethereum Networks* for *etherscan.io* ## Dependency :rocket: + +**Gradle** +```groovy +dependencies { + compile "com.github.goodforgod:java-etherscan-api:1.1.1" +} +``` + **Maven** ```xml com.github.goodforgod java-etherscan-api - 1.1.0 + 1.1.1 ``` -**Gradle** -```groovy -dependencies { - compile 'com.github.goodforgod:java-etherscan-api:1.1.0' -} -``` - ## Content - [Ethereum Networks](#mainnet-and-testnets) - [Custom HttpClient](#custom-httpclient) @@ -42,6 +43,7 @@ dependencies { - [Version History](#version-history) ## Mainnet and Testnets + API support Ethereum: *[MAINNET](https://etherscan.io), [ROPSTEN](https://ropsten.etherscan.io), [KOVAN](https://kovan.etherscan.io), @@ -88,14 +90,18 @@ EtherScanApi api = new EtherScanApi("YourApiKey"); Below are examples for each API category. ### Account Api + **Get Ether Balance for a single Address** + ```java EtherScanApi api = new EtherScanApi(); Balance balance = api.account().balance("0x8d4426f94e42f721C7116E81d6688cd935cB3b4F"); ``` ### Block Api + **Get uncles block for block height** + ```java EtherScanApi api = new EtherScanApi(); Optional uncles = api.block().uncles(200000); @@ -109,7 +115,9 @@ Abi abi = api.contract().contractAbi("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413 ``` ### Logs Api + **Get event logs for single topic** + ```java EtherScanApi api = new EtherScanApi(); LogQuery query = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") @@ -119,6 +127,7 @@ List logs = api.logs().logs(query); ``` **Get event logs for 3 topics with respectful operations** + ```java EtherScanApi api = new EtherScanApi(); LogQuery query = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) @@ -134,47 +143,45 @@ List logs = api.logs().logs(query); ``` ### Proxy Api + **Get tx detailds with proxy endpoint** + ```java EtherScanApi api = new EtherScanApi(EthNetwork.MAINNET); Optional tx = api.proxy().tx("0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); ``` **Get block info with proxy endpoint** + ```java EtherScanApi api = new EtherScanApi(EthNetwork.MAINNET); Optional block = api.proxy().block(15215); ``` ### Stats Api + **Statistic about last price** + ```java EtherScanApi api = new EtherScanApi(); Price price = api.stats().lastPrice(); ``` ### Transaction Api + **Request receipt status for tx** + ```java EtherScanApi api = new EtherScanApi(); Optional status = api.txs().receiptStatus("0x513c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76"); ``` ### Token Api + You can read about token API [here](https://etherscan.io/apis#tokens) Token API methods migrated to [Account](#account-api) & [Stats](#stats-api) respectfully. -## Version History - -**1.1.0** - Improved error handling, QueueManager improved, Gradle 6.7 instead of Maven, GitHub CI, Sonarcloud analyzer, dependencies updated. - -**1.0.2** - Minor http client improvements. - -**1.0.1** - Gorli & TOBALABA networks support. - -**1.0.0** - Initial project with all API functionality, for all available networks, with tests coverage for all cases. - ## License This project licensed under the MIT - see the [LICENSE](LICENSE) file for details. diff --git a/build.gradle b/build.gradle index 638baf3..0e3f321 100644 --- a/build.gradle +++ b/build.gradle @@ -1,27 +1,26 @@ plugins { - id 'jacoco' - id 'java-library' - id 'maven-publish' + id "jacoco" + id "java-library" + id "maven-publish" - id 'org.sonarqube' version '3.1.1' - id 'com.diffplug.spotless' version '5.11.0' + id "org.sonarqube" version "3.3" + id "com.diffplug.spotless" version "5.14.3" } repositories { mavenLocal() mavenCentral() - jcenter() } group = groupId version = artifactVersion -sourceCompatibility = 1.8 -targetCompatibility = 1.8 +sourceCompatibility = JavaVersion.VERSION_1_8 +targetCompatibility = JavaVersion.VERSION_1_8 spotless { java { - encoding 'UTF-8' + encoding "UTF-8" removeUnusedImports() eclipse().configFile "${projectDir}/config/codestyle.xml" } @@ -29,43 +28,30 @@ spotless { sonarqube { properties { - property 'sonar.host.url', 'https://sonarcloud.io' - property 'sonar.organization', 'goodforgod' - property 'sonar.projectKey', 'GoodforGod_java-etherscan-api' + property "sonar.host.url", "https://sonarcloud.io" + property "sonar.organization", "goodforgod" + property "sonar.projectKey", "GoodforGod_java-etherscan-api" } } dependencies { - implementation 'org.jetbrains:annotations:20.1.0' - implementation 'com.google.code.gson:gson:2.8.6' + implementation "org.jetbrains:annotations:22.0.0" + implementation "com.google.code.gson:gson:2.8.8" - testImplementation 'junit:junit:4.13.1' + testImplementation "junit:junit:4.13.1" } test { - failFast = true - useJUnit() testLogging { - events "passed", "skipped", "failed" - exceptionFormat "full" + events("passed", "skipped", "failed") + exceptionFormat("full") } -} -tasks.withType(JavaCompile) { - options.encoding = 'UTF-8' - options.incremental = true - options.fork = true -} - -tasks.withType(Test) { - reports.html.enabled = false - reports.junitXml.enabled = false -} - -java { - withJavadocJar() - withSourcesJar() + reports { + html.enabled(false) + junitXml.enabled(false) + } } publishing { @@ -74,27 +60,27 @@ publishing { from components.java pom { - name = 'Java Etherscan API' - url = 'https://github.com/GoodforGod/java-etherscan-api' - description = 'Library is a wrapper for EtherScan API.' + name = "Java Etherscan API" + url = "https://github.com/GoodforGod/java-etherscan-api" + description = "Library is a wrapper for EtherScan API." license { - name = 'MIT License' - url = 'https://github.com/GoodforGod/java-etherscan-api/blob/master/LICENSE' - distribution = 'repo' + name = "MIT License" + url = "https://github.com/GoodforGod/java-etherscan-api/blob/master/LICENSE" + distribution = "repo" } developer { - id = 'GoodforGod' - name = 'Anton Kurako' - email = 'goodforgod.dev@gmail.com' - url = 'https://github.com/GoodforGod' + id = "GoodforGod" + name = "Anton Kurako" + email = "goodforgod.dev@gmail.com" + url = "https://github.com/GoodforGod" } scm { - connection = 'scm:git:git://github.com/GoodforGod/java-etherscan-api.git' - developerConnection = 'scm:git:ssh://GoodforGod/java-etherscan-api.git' - url = 'https://github.com/GoodforGod/java-etherscan-api/tree/master' + connection = "scm:git:git://github.com/GoodforGod/java-etherscan-api.git" + developerConnection = "scm:git:ssh://GoodforGod/java-etherscan-api.git" + url = "https://github.com/GoodforGod/java-etherscan-api/tree/master" } } } @@ -103,7 +89,7 @@ publishing { maven { def releasesRepoUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2" def snapshotsRepoUrl = "https://oss.sonatype.org/content/repositories/snapshots/" - url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl + url = version.endsWith("SNAPSHOT") ? snapshotsRepoUrl : releasesRepoUrl credentials { username System.getenv("OSS_USERNAME") password System.getenv("OSS_PASSWORD") @@ -112,6 +98,17 @@ publishing { } } +java { + withJavadocJar() + withSourcesJar() +} + +tasks.withType(JavaCompile) { + options.encoding("UTF-8") + options.incremental(true) + options.fork = true +} + check.dependsOn jacocoTestReport jacocoTestReport { reports { @@ -120,16 +117,16 @@ jacocoTestReport { } } +javadoc { + options.encoding = "UTF-8" + if (JavaVersion.current().isJava9Compatible()) { + options.addBooleanOption("html5", true) + } +} + if (project.hasProperty("signing.keyId")) { - apply plugin: 'signing' + apply plugin: "signing" signing { sign publishing.publications.mavenJava } } - -javadoc { - options.encoding = "UTF-8" - if (JavaVersion.current().isJava9Compatible()) { - options.addBooleanOption('html5', true) - } -} \ No newline at end of file diff --git a/config/codestyle.xml b/config/codestyle.xml index 0c19beb..a90c4f5 100644 --- a/config/codestyle.xml +++ b/config/codestyle.xml @@ -74,7 +74,7 @@ - + diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e708b1c023ec8b20f512888fe07c5bd3ff77bb8f..7454180f2ae8848c63b8b4dea2cb829da983f2fa 100644 GIT binary patch delta 18435 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 B8dv}T delta 17845 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 X49Luk)baa85H9$5YCsTPT`SVRWMtMW diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index be52383..ffed3a2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 4f906e0..744e882 100755 --- a/gradlew +++ b/gradlew @@ -72,7 +72,7 @@ case "`uname`" in Darwin* ) darwin=true ;; - MINGW* ) + MSYS* | MINGW* ) msys=true ;; NONSTOP* ) diff --git a/src/main/java/io/api/etherscan/core/IAccountApi.java b/src/main/java/io/api/etherscan/core/IAccountApi.java index 6eda869..642f289 100644 --- a/src/main/java/io/api/etherscan/core/IAccountApi.java +++ b/src/main/java/io/api/etherscan/core/IAccountApi.java @@ -36,8 +36,8 @@ public interface IAccountApi { TokenBalance balance(String address, String contract) throws ApiException; /** - * Maximum 20 address for single batch request If address MORE THAN 20, then - * there will be more than 1 request performed + * Maximum 20 address for single batch request If address MORE THAN 20, then there will be more than + * 1 request performed * * @param addresses addresses to get balances for * @return list of balances diff --git a/src/main/java/io/api/etherscan/core/ILogsApi.java b/src/main/java/io/api/etherscan/core/ILogsApi.java index dc7d8d8..37c5eac 100644 --- a/src/main/java/io/api/etherscan/core/ILogsApi.java +++ b/src/main/java/io/api/etherscan/core/ILogsApi.java @@ -16,8 +16,7 @@ public interface ILogsApi { /** - * alternative to the native eth_getLogs Read at EtherScan API description for - * full info! + * alternative to the native eth_getLogs Read at EtherScan API description for full info! * * @param query build log query * @return logs according to query diff --git a/src/main/java/io/api/etherscan/core/IProxyApi.java b/src/main/java/io/api/etherscan/core/IProxyApi.java index 58a70ed..e57f6ec 100644 --- a/src/main/java/io/api/etherscan/core/IProxyApi.java +++ b/src/main/java/io/api/etherscan/core/IProxyApi.java @@ -36,8 +36,7 @@ public interface IProxyApi { Optional block(long blockNo) throws ApiException; /** - * Returns information about a uncle by block number - * eth_getUncleByBlockNumberAndIndex + * Returns information about a uncle by block number eth_getUncleByBlockNumberAndIndex * * @param blockNo block number from 0 to last * @param index uncle block index @@ -59,8 +58,8 @@ public interface IProxyApi { Optional tx(String txhash) throws ApiException; /** - * Returns information about a transaction by block number and transaction index - * position eth_getTransactionByBlockNumberAndIndex + * Returns information about a transaction by block number and transaction index position + * eth_getTransactionByBlockNumberAndIndex * * @param blockNo block number from 0 to last * @param index tx index in block @@ -71,8 +70,8 @@ public interface IProxyApi { Optional tx(long blockNo, long index) throws ApiException; /** - * Returns the number of transactions in a block from a block matching the given - * block number eth_getBlockTransactionCountByNumber + * Returns the number of transactions in a block from a block matching the given block number + * eth_getBlockTransactionCountByNumber * * @param blockNo block number from 0 to last * @return transaction amount in block @@ -81,8 +80,7 @@ public interface IProxyApi { int txCount(long blockNo) throws ApiException; /** - * Returns the number of transactions sent from an address - * eth_getTransactionCount + * Returns the number of transactions sent from an address eth_getTransactionCount * * @param address eth address * @return transactions send amount from address @@ -91,8 +89,8 @@ public interface IProxyApi { int txSendCount(String address) throws ApiException; /** - * Creates new message call transaction or a contract creation for signed - * transactions eth_sendRawTransaction + * Creates new message call transaction or a contract creation for signed transactions + * eth_sendRawTransaction * * @param hexEncodedTx encoded hex data to send * @return optional string response @@ -102,8 +100,7 @@ public interface IProxyApi { Optional txSendRaw(String hexEncodedTx) throws ApiException; /** - * Returns the receipt of a transaction by transaction hash - * eth_getTransactionReceipt + * Returns the receipt of a transaction by transaction hash eth_getTransactionReceipt * * @param txhash transaction hash * @return optional tx receipt @@ -113,8 +110,8 @@ public interface IProxyApi { Optional txReceipt(String txhash) throws ApiException; /** - * Executes a new message call immediately without creating a transaction on the - * block chain eth_call + * Executes a new message call immediately without creating a transaction on the block chain + * eth_call * * @param address to call * @param data data to call address @@ -135,8 +132,7 @@ public interface IProxyApi { Optional code(String address) throws ApiException; /** - * (**experimental) Returns the value from a storage position at a given address - * eth_getStorageAt + * (**experimental) Returns the value from a storage position at a given address eth_getStorageAt * * @param address to get storage * @param position storage position @@ -156,9 +152,8 @@ public interface IProxyApi { BigInteger gasPrice() throws ApiException; /** - * Makes a call or transaction, which won't be added to the blockchain and - * returns the used gas, which can be used for estimating the used gas - * eth_estimateGas + * Makes a call or transaction, which won't be added to the blockchain and returns the used gas, + * which can be used for estimating the used gas eth_estimateGas * * @param hexData data to calc gas usage for * @return estimated gas usage diff --git a/src/main/java/io/api/etherscan/core/ITransactionApi.java b/src/main/java/io/api/etherscan/core/ITransactionApi.java index 51c108c..f545c2d 100644 --- a/src/main/java/io/api/etherscan/core/ITransactionApi.java +++ b/src/main/java/io/api/etherscan/core/ITransactionApi.java @@ -15,8 +15,7 @@ public interface ITransactionApi { /** - * Check Contract Execution Status (if there was an error during contract - * execution) + * Check Contract Execution Status (if there was an error during contract execution) * * @param txhash transaction hash * @return optional status result @@ -26,8 +25,7 @@ public interface ITransactionApi { Optional execStatus(String txhash) throws ApiException; /** - * Check Transaction Receipt Status (Only applicable for Post Byzantium fork - * transactions) + * Check Transaction Receipt Status (Only applicable for Post Byzantium fork transactions) * * @param txhash transaction hash * @return 0 = Fail, 1 = Pass, empty value for pre-byzantium fork diff --git a/src/main/java/io/api/etherscan/manager/IQueueManager.java b/src/main/java/io/api/etherscan/manager/IQueueManager.java index 3a65240..c66a6fd 100644 --- a/src/main/java/io/api/etherscan/manager/IQueueManager.java +++ b/src/main/java/io/api/etherscan/manager/IQueueManager.java @@ -1,8 +1,8 @@ package io.api.etherscan.manager; /** - * Queue manager to support API limits (EtherScan 5request\sec limit) Managers - * grants turn if the limit is not exhausted And resets queue each set period + * Queue manager to support API limits (EtherScan 5request\sec limit) Managers grants turn if the + * limit is not exhausted And resets queue each set period * * @author GoodforGod * @since 30.10.2018 diff --git a/src/main/java/io/api/etherscan/model/Log.java b/src/main/java/io/api/etherscan/model/Log.java index 4649d6b..36d126b 100644 --- a/src/main/java/io/api/etherscan/model/Log.java +++ b/src/main/java/io/api/etherscan/model/Log.java @@ -68,11 +68,10 @@ public LocalDateTime getTimeStamp() { } /** - * Return the "timeStamp" field of the event record as a long-int representing - * the milliseconds since the Unix epoch (1970-01-01 00:00:00). + * Return the "timeStamp" field of the event record as a long-int representing the milliseconds + * since the Unix epoch (1970-01-01 00:00:00). * - * @return milliseconds between Unix epoch and `timeStamp`. If field is empty or - * null, returns null + * @return milliseconds between Unix epoch and `timeStamp`. If field is empty or null, returns null */ public Long getTimeStampAsMillis() { if (BasicUtils.isEmpty(timeStamp)) { From 8adc32c765c610fd8678effc54cd27e8666a9c97 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 31 Oct 2021 16:36:11 +0300 Subject: [PATCH 038/112] [1.1.0] CI pipeline updated --- .github/workflows/gradle.yml | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 03c2e11..033d9af 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -4,12 +4,10 @@ on: push: branches: - master - schedule: - - cron: "0 12 1 * *" pull_request: branches: - - master - - dev + - master + - dev jobs: build: @@ -20,21 +18,18 @@ jobs: name: Java ${{ matrix.java }} setup steps: - - uses: actions/checkout@v1 - - name: Set up JDK - uses: actions/setup-java@v1 + - uses: actions/checkout@v1 + - name: Set up JDK + uses: actions/setup-java@v1 + + with: + java-version: ${{ matrix.java }} - with: - java-version: ${{ matrix.java }} + - name: Build + run: ./gradlew classes - - name: Build with Gradle - run: ./gradlew build jacocoTestReport - env: - API_KEY: ${{ secrets.API_KEY }} + - name: Codestyle + run: ./gradlew spotlessCheck - - name: Analyze with SonarQube - run: ./gradlew sonarqube - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - API_KEY: ${{ secrets.API_KEY }} + - name: Test + run: ./gradlew test jacocoTestReport From 774201f356650981d60427a3ff060b947df9073e Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 31 Oct 2021 17:20:21 +0300 Subject: [PATCH 039/112] [1.1.0] ApiRunner queue removed --- src/test/java/io/api/ApiRunner.java | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/test/java/io/api/ApiRunner.java b/src/test/java/io/api/ApiRunner.java index 6f608d8..e52c35b 100644 --- a/src/test/java/io/api/ApiRunner.java +++ b/src/test/java/io/api/ApiRunner.java @@ -1,7 +1,6 @@ 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.Assert; @@ -19,14 +18,10 @@ public class ApiRunner extends Assert { ? EtherScanApi.DEFAULT_KEY : apiKey; - final QueueManager queue = key.equals(EtherScanApi.DEFAULT_KEY) - ? QueueManager.DEFAULT_KEY_QUEUE - : new QueueManager(1, 2); - - api = new EtherScanApi(key, EthNetwork.MAINNET, queue); - apiRopsten = new EtherScanApi(key, EthNetwork.ROPSTEN, queue); - apiRinkeby = new EtherScanApi(key, EthNetwork.RINKEBY, queue); - apiKovan = new EtherScanApi(key, EthNetwork.KOVAN, queue); + api = new EtherScanApi(key, EthNetwork.MAINNET); + apiRopsten = new EtherScanApi(key, EthNetwork.ROPSTEN); + apiRinkeby = new EtherScanApi(key, EthNetwork.RINKEBY); + apiKovan = new EtherScanApi(key, EthNetwork.KOVAN); } public static String getKey() { From 5c71cac65b5a4531f48b029ad9ca6806f2222128 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 31 Oct 2021 18:23:58 +0300 Subject: [PATCH 040/112] [1.1.0] ProxyStorageApiTest fix --- src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java index fbfcf80..ecd7dca 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java @@ -24,12 +24,12 @@ public void correct() { @Test(expected = InvalidAddressException.class) public void invalidParamWithError() { - Optional call = getApi().proxy().storageAt("0xe03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 0); + getApi().proxy().storageAt("0xe03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 0); } @Test public void correctParamWithEmptyExpectedResult() { - Optional call = getApi().proxy().storageAt("0x6e03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 100); + final Optional call = getApi().proxy().storageAt("0x6e03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 10000); assertFalse(call.isPresent()); } } From 50c6fb6a3a2cd8fe4c5a065e3e3371e1277b1c24 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Mon, 1 Nov 2021 00:38:17 +0300 Subject: [PATCH 041/112] [1.1.0] QueueManager new constructor introduced ApiRunner proper queue used for tests QueueManager#close as AutoClosable EtherScanApi as AutoClosable CI updated Tests API key exposed --- .github/workflows/gradle.yml | 8 +++++ .../api/etherscan/core/impl/EtherScanApi.java | 9 ++++- .../api/etherscan/manager/IQueueManager.java | 2 +- .../manager/impl/FakeQueueManager.java | 5 +++ .../etherscan/manager/impl/QueueManager.java | 33 ++++++++++++++++--- src/test/java/io/api/ApiRunner.java | 10 +++--- .../api/etherscan/proxy/ProxyCallApiTest.java | 2 +- .../api/etherscan/proxy/ProxyCodeApiTest.java | 6 ++-- 8 files changed, 60 insertions(+), 15 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 033d9af..613a39e 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -33,3 +33,11 @@ jobs: - name: Test run: ./gradlew test jacocoTestReport + env: + API_KEY: ${{ secrets.API_KEY }} + + - name: SonarQube + run: ./gradlew sonarqube + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java b/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java index 8cd5d46..ba5dd83 100644 --- a/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java +++ b/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java @@ -20,12 +20,13 @@ * @author GoodforGod * @since 28.10.2018 */ -public class EtherScanApi { +public class EtherScanApi implements AutoCloseable { private static final Supplier DEFAULT_SUPPLIER = HttpExecutor::new; public static final String DEFAULT_KEY = "YourApiKeyToken"; + private final IQueueManager queueManager; private final IAccountApi account; private final IBlockApi block; private final IContractApi contract; @@ -87,6 +88,7 @@ public EtherScanApi(final String apiKey, final String ending = EthNetwork.TOBALABA.equals(network) ? "com" : "io"; final String baseUrl = "https://" + network.getDomain() + ".etherscan." + ending + "/api" + "?apikey=" + apiKey; + this.queueManager = queue; this.account = new AccountApiProvider(queue, baseUrl, executor); this.block = new BlockApiProvider(queue, baseUrl, executor); this.contract = new ContractApiProvider(queue, baseUrl, executor); @@ -130,4 +132,9 @@ public IProxyApi proxy() { public IStatisticApi stats() { return stats; } + + @Override + public void close() throws Exception { + queueManager.close(); + } } diff --git a/src/main/java/io/api/etherscan/manager/IQueueManager.java b/src/main/java/io/api/etherscan/manager/IQueueManager.java index c66a6fd..98a3172 100644 --- a/src/main/java/io/api/etherscan/manager/IQueueManager.java +++ b/src/main/java/io/api/etherscan/manager/IQueueManager.java @@ -7,7 +7,7 @@ * @author GoodforGod * @since 30.10.2018 */ -public interface IQueueManager { +public interface IQueueManager extends AutoCloseable { /** * Waits in queue for chance to take turn diff --git a/src/main/java/io/api/etherscan/manager/impl/FakeQueueManager.java b/src/main/java/io/api/etherscan/manager/impl/FakeQueueManager.java index e797f8b..620244c 100644 --- a/src/main/java/io/api/etherscan/manager/impl/FakeQueueManager.java +++ b/src/main/java/io/api/etherscan/manager/impl/FakeQueueManager.java @@ -15,4 +15,9 @@ public void takeTurn() { // no limit or await provided for fake impl so rate limit exception will be // thrown if too many calls } + + @Override + public void close() { + // do nothing + } } diff --git a/src/main/java/io/api/etherscan/manager/impl/QueueManager.java b/src/main/java/io/api/etherscan/manager/impl/QueueManager.java index 517883c..16f97b4 100644 --- a/src/main/java/io/api/etherscan/manager/impl/QueueManager.java +++ b/src/main/java/io/api/etherscan/manager/impl/QueueManager.java @@ -12,11 +12,12 @@ * @author GoodforGod * @since 30.10.2018 */ -public class QueueManager implements IQueueManager { +public class QueueManager implements IQueueManager, AutoCloseable { public static final QueueManager DEFAULT_KEY_QUEUE = new QueueManager(1, 7); - public static final QueueManager PERSONAL_KEY_QUEUE = new QueueManager(2, 1); + public static final QueueManager PERSONAL_KEY_QUEUE = new QueueManager(5, 1100L, 1100L, 5); + private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); private final Semaphore semaphore; public QueueManager(int size, int resetInSec) { @@ -24,9 +25,26 @@ public QueueManager(int size, int resetInSec) { } public QueueManager(int size, int queueResetTimeInSec, int delayInSec) { - this.semaphore = new Semaphore(size); - Executors.newSingleThreadScheduledExecutor() - .scheduleAtFixedRate(releaseLocks(size), delayInSec, queueResetTimeInSec, TimeUnit.SECONDS); + this(size, queueResetTimeInSec, delayInSec, size); + } + + public QueueManager(int size, + int queueResetTimeInSec, + int delayInSec, + int initialSize) { + this(size, + (long) queueResetTimeInSec * 1000, + (long) delayInSec * 1000, + initialSize); + } + + public QueueManager(int size, + long queueResetTimeInMillis, + long delayInMillis, + int initialSize) { + this.semaphore = new Semaphore(initialSize); + this.executorService.scheduleAtFixedRate(releaseLocks(size), delayInMillis, queueResetTimeInMillis, + TimeUnit.MILLISECONDS); } @Override @@ -37,4 +55,9 @@ public void takeTurn() { private Runnable releaseLocks(int toRelease) { return () -> semaphore.release(toRelease); } + + @Override + public void close() { + executorService.shutdown(); + } } diff --git a/src/test/java/io/api/ApiRunner.java b/src/test/java/io/api/ApiRunner.java index e52c35b..f35504b 100644 --- a/src/test/java/io/api/ApiRunner.java +++ b/src/test/java/io/api/ApiRunner.java @@ -1,6 +1,7 @@ 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.Assert; @@ -18,10 +19,11 @@ public class ApiRunner extends Assert { ? EtherScanApi.DEFAULT_KEY : apiKey; - api = new EtherScanApi(key, EthNetwork.MAINNET); - apiRopsten = new EtherScanApi(key, EthNetwork.ROPSTEN); - apiRinkeby = new EtherScanApi(key, EthNetwork.RINKEBY); - apiKovan = new EtherScanApi(key, EthNetwork.KOVAN); + final QueueManager queueManager = new QueueManager(2, 2100L, 2100L, 0); + api = new EtherScanApi(key, EthNetwork.MAINNET, queueManager); + apiKovan = new EtherScanApi(key, EthNetwork.KOVAN, queueManager); + apiRopsten = new EtherScanApi(key, EthNetwork.ROPSTEN, queueManager); + apiRinkeby = new EtherScanApi(key, EthNetwork.RINKEBY, queueManager); } public static String getKey() { diff --git a/src/test/java/io/api/etherscan/proxy/ProxyCallApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyCallApiTest.java index c90850c..07d26bd 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyCallApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyCallApiTest.java @@ -41,6 +41,6 @@ public void correctParamWithEmptyExpectedResult() { Optional call = getApi().proxy().call("0xAEEF16DB4855E25702F8237E8f403FddcaF931C0", "0x70a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724"); assertTrue(call.isPresent()); - assertFalse(BasicUtils.isNotHex(call.get())); + assertFalse(call.get(), BasicUtils.isNotHex(call.get())); } } diff --git a/src/test/java/io/api/etherscan/proxy/ProxyCodeApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyCodeApiTest.java index 3120503..9e4910c 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyCodeApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyCodeApiTest.java @@ -19,18 +19,18 @@ public class ProxyCodeApiTest extends ApiRunner { public void correct() { Optional call = getApi().proxy().code("0xf75e354c5edc8efed9b59ee9f67a80845ade7d0c"); assertTrue(call.isPresent()); - assertFalse(BasicUtils.isNotHex(call.get())); + assertFalse(call.get(), BasicUtils.isNotHex(call.get())); } @Test(expected = InvalidAddressException.class) public void invalidParamWithError() { - Optional call = getApi().proxy().code("0f75e354c5edc8efed9b59ee9f67a80845ade7d0c"); + getApi().proxy().code("0f75e354c5edc8efed9b59ee9f67a80845ade7d0c"); } @Test public void correctParamWithEmptyExpectedResult() { Optional call = getApi().proxy().code("0xf15e354c5edc8efed9b59ee9f67a80845ade7d0c"); assertTrue(call.isPresent()); - assertFalse(BasicUtils.isNotHex(call.get())); + assertFalse(call.get(), BasicUtils.isNotHex(call.get())); } } From 167edcdff55e59e535001d11b048c13a8b683138 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Mon, 1 Nov 2021 00:40:48 +0300 Subject: [PATCH 042/112] [1.1.1] Version updated --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 1fa1ad4..e4aacfe 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ groupId=com.github.goodforgod artifactId=java-etherscan-api -artifactVersion=1.1.0 +artifactVersion=1.1.1 buildNumber=1 From 412f99b2b7197b506670259b1b22b944b8f2f3ba Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Mon, 1 Nov 2021 00:50:24 +0300 Subject: [PATCH 043/112] [1.1.1] ProxyBlockApiTest self queue with longer timeout --- src/test/java/io/api/ApiRunner.java | 20 +++++++++---------- .../io/api/etherscan/EtherScanApiTest.java | 2 +- .../etherscan/proxy/ProxyBlockApiTest.java | 16 ++++++++++++--- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/test/java/io/api/ApiRunner.java b/src/test/java/io/api/ApiRunner.java index f35504b..554b10f 100644 --- a/src/test/java/io/api/ApiRunner.java +++ b/src/test/java/io/api/ApiRunner.java @@ -11,23 +11,23 @@ public class ApiRunner extends Assert { private static final EtherScanApi apiRopsten; private static final EtherScanApi apiRinkeby; private static final EtherScanApi apiKovan; - private static final String key; + private static final String apiKey; static { - final String apiKey = System.getenv("API_KEY"); - key = (apiKey == null || apiKey.isEmpty()) + final String key = System.getenv("API_KEY"); + apiKey = (key == null || key.isEmpty()) ? EtherScanApi.DEFAULT_KEY - : apiKey; + : key; final QueueManager queueManager = new QueueManager(2, 2100L, 2100L, 0); - api = new EtherScanApi(key, EthNetwork.MAINNET, queueManager); - apiKovan = new EtherScanApi(key, EthNetwork.KOVAN, queueManager); - apiRopsten = new EtherScanApi(key, EthNetwork.ROPSTEN, queueManager); - apiRinkeby = new EtherScanApi(key, EthNetwork.RINKEBY, queueManager); + api = new EtherScanApi(ApiRunner.apiKey, EthNetwork.MAINNET, queueManager); + apiKovan = new EtherScanApi(ApiRunner.apiKey, EthNetwork.KOVAN, queueManager); + apiRopsten = new EtherScanApi(ApiRunner.apiKey, EthNetwork.ROPSTEN, queueManager); + apiRinkeby = new EtherScanApi(ApiRunner.apiKey, EthNetwork.RINKEBY, queueManager); } - public static String getKey() { - return key; + public static String getApiKey() { + return apiKey; } public static EtherScanApi getApi() { diff --git a/src/test/java/io/api/etherscan/EtherScanApiTest.java b/src/test/java/io/api/etherscan/EtherScanApiTest.java index 5071a68..be49435 100644 --- a/src/test/java/io/api/etherscan/EtherScanApiTest.java +++ b/src/test/java/io/api/etherscan/EtherScanApiTest.java @@ -79,7 +79,7 @@ public void noTimeoutUnlimitedAwait() { public void timeout() throws InterruptedException { TimeUnit.SECONDS.sleep(5); Supplier supplier = () -> new HttpExecutor(300, 300); - EtherScanApi api = new EtherScanApi(getKey(), EthNetwork.KOVAN, supplier); + EtherScanApi api = new EtherScanApi(getApiKey(), EthNetwork.KOVAN, supplier); List blocks = api.account().minedBlocks("0x0010f94b296A852aAac52EA6c5Ac72e03afD032D"); assertNotNull(blocks); } diff --git a/src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java index 181a68d..5d3884d 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java @@ -1,6 +1,9 @@ package io.api.etherscan.proxy; import io.api.ApiRunner; +import io.api.etherscan.core.impl.EtherScanApi; +import io.api.etherscan.manager.impl.QueueManager; +import io.api.etherscan.model.EthNetwork; import io.api.etherscan.model.proxy.BlockProxy; import org.junit.Test; @@ -14,9 +17,16 @@ */ public class ProxyBlockApiTest extends ApiRunner { + private final EtherScanApi api; + + public ProxyBlockApiTest() { + final QueueManager queueManager = new QueueManager(1, 5100L, 5100L, 0); + this.api = new EtherScanApi(getApiKey(), EthNetwork.MAINNET, queueManager); + } + @Test public void correct() { - Optional block = getApi().proxy().block(5120); + Optional block = api.proxy().block(5120); assertTrue(block.isPresent()); BlockProxy proxy = block.get(); assertNotNull(proxy.getHash()); @@ -49,13 +59,13 @@ public void correct() { @Test public void correctParamWithEmptyExpectedResult() { - Optional block = getApi().proxy().block(99999999999L); + Optional block = api.proxy().block(99999999999L); assertFalse(block.isPresent()); } @Test public void correctParamNegativeNo() { - Optional block = getApi().proxy().block(-1); + Optional block = api.proxy().block(-1); assertTrue(block.isPresent()); assertNotNull(block.get().getHash()); } From cd2aad313b9266d6325a0117806c3d1a149af3a2 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Mon, 1 Nov 2021 00:55:02 +0300 Subject: [PATCH 044/112] [1.1.1] QueueManager tryAcquire interrupt self [1.1.1] QueueManager tryAcquire instead of uninterrupted [1.1.1] ApiRunner close added [1.1.1] ApiRunner queue changed time [1.1.1] ApiRunner queue changed time --- .../etherscan/manager/impl/QueueManager.java | 19 ++++++++++--------- src/test/java/io/api/ApiRunner.java | 11 ++++++++++- .../etherscan/proxy/ProxyTxCountApiTest.java | 4 ++-- 3 files changed, 22 insertions(+), 12 deletions(-) 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 16f97b4..80f7c51 100644 --- a/src/main/java/io/api/etherscan/manager/impl/QueueManager.java +++ b/src/main/java/io/api/etherscan/manager/impl/QueueManager.java @@ -19,6 +19,7 @@ public class QueueManager implements IQueueManager, AutoCloseable { private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); private final Semaphore semaphore; + private final long queueResetTimeInMillis; public QueueManager(int size, int resetInSec) { this(size, resetInSec, resetInSec); @@ -28,28 +29,28 @@ public QueueManager(int size, int queueResetTimeInSec, int delayInSec) { this(size, queueResetTimeInSec, delayInSec, size); } - public QueueManager(int size, - int queueResetTimeInSec, - int delayInSec, - int initialSize) { + 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) { + public QueueManager(int size, long queueResetTimeInMillis, long delayInMillis, int initialSize) { + this.queueResetTimeInMillis = queueResetTimeInMillis; this.semaphore = new Semaphore(initialSize); this.executorService.scheduleAtFixedRate(releaseLocks(size), delayInMillis, queueResetTimeInMillis, TimeUnit.MILLISECONDS); } + @SuppressWarnings("java:S899") @Override public void takeTurn() { - semaphore.acquireUninterruptibly(); + try { + semaphore.tryAcquire(queueResetTimeInMillis, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } } private Runnable releaseLocks(int toRelease) { diff --git a/src/test/java/io/api/ApiRunner.java b/src/test/java/io/api/ApiRunner.java index 554b10f..5ccdf83 100644 --- a/src/test/java/io/api/ApiRunner.java +++ b/src/test/java/io/api/ApiRunner.java @@ -3,6 +3,7 @@ import io.api.etherscan.core.impl.EtherScanApi; import io.api.etherscan.manager.impl.QueueManager; import io.api.etherscan.model.EthNetwork; +import org.junit.AfterClass; import org.junit.Assert; public class ApiRunner extends Assert { @@ -19,7 +20,7 @@ public class ApiRunner extends Assert { ? EtherScanApi.DEFAULT_KEY : key; - final QueueManager queueManager = new QueueManager(2, 2100L, 2100L, 0); + final QueueManager queueManager = new QueueManager(1, 1200L, 1200L, 0); api = new EtherScanApi(ApiRunner.apiKey, EthNetwork.MAINNET, queueManager); apiKovan = new EtherScanApi(ApiRunner.apiKey, EthNetwork.KOVAN, queueManager); apiRopsten = new EtherScanApi(ApiRunner.apiKey, EthNetwork.ROPSTEN, queueManager); @@ -45,4 +46,12 @@ public static EtherScanApi getApiRinkeby() { public static EtherScanApi getApiKovan() { return apiKovan; } + + @AfterClass + public static void cleanup() throws Exception { + api.close(); + apiRopsten.close(); + apiRinkeby.close(); + apiKovan.close(); + } } diff --git a/src/test/java/io/api/etherscan/proxy/ProxyTxCountApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyTxCountApiTest.java index 6a0778c..b81926f 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyTxCountApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyTxCountApiTest.java @@ -32,12 +32,12 @@ public void invalidParamWithError() { @Test public void correctParamWithEmptyExpectedResultBlockNoExist() { int count = getApi().proxy().txCount(99999999999L); - assertEquals(0, count); + assertNotEquals(1, count); } @Test public void correctParamWithEmptyExpectedResult() { int count = getApi().proxy().txSendCount("0x1e03d9cce9d60f3e9f2597e13cd4c54c55330cfd"); - assertEquals(0, count); + assertNotEquals(1, count); } } From be775cfca317d55ffae850f5ed76985396d24af5 Mon Sep 17 00:00:00 2001 From: Nathan Guggenberger Date: Fri, 19 Nov 2021 12:12:13 -0600 Subject: [PATCH 045/112] Add support for ERC-721 (NFT) Tokens --- .../io/api/etherscan/core/IAccountApi.java | 20 +++++++++++++++- .../core/impl/AccountApiProvider.java | 24 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/api/etherscan/core/IAccountApi.java b/src/main/java/io/api/etherscan/core/IAccountApi.java index 642f289..25254aa 100644 --- a/src/main/java/io/api/etherscan/core/IAccountApi.java +++ b/src/main/java/io/api/etherscan/core/IAccountApi.java @@ -93,7 +93,7 @@ public interface IAccountApi { List txsInternalByHash(String txhash) throws ApiException; /** - * All token txs for given address + * All ERC-20 token txs for given address * * @param address get txs for * @param startBlock tx from this blockNumber @@ -110,6 +110,24 @@ public interface IAccountApi { @NotNull List txsToken(String address) throws ApiException; + /** + * All ERC-721 (NFT) token txs for given address + * + * @param address get txs for + * @param startBlock tx from this blockNumber + * @param endBlock tx to this blockNumber + * @return txs for address + * @throws ApiException parent exception class + */ + @NotNull + List txsNftToken(String address, long startBlock, long endBlock) throws ApiException; + + @NotNull + List txsNftToken(String address, long startBlock) throws ApiException; + + @NotNull + List txsNftToken(String address) throws ApiException; + /** * All blocks mined by 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 b512019..8f0040d 100644 --- a/src/main/java/io/api/etherscan/core/impl/AccountApiProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/AccountApiProvider.java @@ -34,6 +34,7 @@ public class AccountApiProvider extends BasicProvider implements IAccountApi { private static final String ACT_TX_ACTION = ACT_PREFIX + "txlist"; private static final String ACT_TX_INTERNAL_ACTION = ACT_PREFIX + "txlistinternal"; private static final String ACT_TX_TOKEN_ACTION = ACT_PREFIX + "tokentx"; + private static final String ACT_TX_NFT_TOKEN_ACTION = ACT_PREFIX + "tokennfttx"; private static final String ACT_MINED_ACTION = ACT_PREFIX + "getminedblocks"; private static final String BLOCK_TYPE_PARAM = "&blocktype=blocks"; @@ -229,6 +230,29 @@ public List txsToken(final String address, final long startBlock, final return getRequestUsingOffset(urlParams, TxTokenResponseTO.class); } + + @Override + public @NotNull List txsNftToken(String address) throws ApiException { + return txsNftToken(address, MIN_START_BLOCK); + } + + @Override + public @NotNull List txsNftToken(String address, long startBlock) throws ApiException { + return txsNftToken(address, startBlock, MAX_END_BLOCK); + } + + @Override + public @NotNull List txsNftToken(String address, long startBlock, long endBlock) throws ApiException { + BasicUtils.validateAddress(address); + final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); + + final String offsetParam = PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX; + final String blockParam = START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end(); + final String urlParams = ACT_TX_NFT_TOKEN_ACTION + offsetParam + ADDRESS_PARAM + address + blockParam + SORT_ASC_PARAM; + + return getRequestUsingOffset(urlParams, TxTokenResponseTO.class); + } + @NotNull @Override public List minedBlocks(final String address) throws ApiException { From 7ce4a07531b31f0401ba2b1944a4ac91e33734cf Mon Sep 17 00:00:00 2001 From: Nathan Guggenberger Date: Fri, 19 Nov 2021 12:37:16 -0600 Subject: [PATCH 046/112] Fix formatting --- .../io/api/etherscan/core/impl/AccountApiProvider.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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 8f0040d..64b9bc3 100644 --- a/src/main/java/io/api/etherscan/core/impl/AccountApiProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/AccountApiProvider.java @@ -230,19 +230,21 @@ public List txsToken(final String address, final long startBlock, final return getRequestUsingOffset(urlParams, TxTokenResponseTO.class); } - + @NotNull @Override - public @NotNull List txsNftToken(String address) throws ApiException { + public List txsNftToken(String address) throws ApiException { return txsNftToken(address, MIN_START_BLOCK); } + @NotNull @Override - public @NotNull List txsNftToken(String address, long startBlock) throws ApiException { + public List txsNftToken(String address, long startBlock) throws ApiException { return txsNftToken(address, startBlock, MAX_END_BLOCK); } + @NotNull @Override - public @NotNull List txsNftToken(String address, long startBlock, long endBlock) throws ApiException { + public List txsNftToken(String address, long startBlock, long endBlock) throws ApiException { BasicUtils.validateAddress(address); final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); From e8cd38d0499c9ac3bab716ffbd6db9394386c786 Mon Sep 17 00:00:00 2001 From: Nathan Guggenberger Date: Fri, 19 Nov 2021 12:43:09 -0600 Subject: [PATCH 047/112] fix formatting again --- .../java/io/api/etherscan/core/impl/AccountApiProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 64b9bc3..77d8b88 100644 --- a/src/main/java/io/api/etherscan/core/impl/AccountApiProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/AccountApiProvider.java @@ -244,7 +244,7 @@ public List txsNftToken(String address, long startBlock) throws ApiExce @NotNull @Override - public List txsNftToken(String address, long startBlock, long endBlock) throws ApiException { + public List txsNftToken(String address, long startBlock, long endBlock) throws ApiException { BasicUtils.validateAddress(address); final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); From 478faf99c30176c969fe310bbb6d264d2dda0497 Mon Sep 17 00:00:00 2001 From: Nathan Guggenberger Date: Sun, 28 Nov 2021 18:18:59 -0600 Subject: [PATCH 048/112] Add tests for RC-721 Tokens --- .../account/AccountTxRc721TokenTest.java | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 src/test/java/io/api/etherscan/account/AccountTxRc721TokenTest.java diff --git a/src/test/java/io/api/etherscan/account/AccountTxRc721TokenTest.java b/src/test/java/io/api/etherscan/account/AccountTxRc721TokenTest.java new file mode 100644 index 0000000..0afa12f --- /dev/null +++ b/src/test/java/io/api/etherscan/account/AccountTxRc721TokenTest.java @@ -0,0 +1,80 @@ +package io.api.etherscan.account; + +import io.api.ApiRunner; +import io.api.etherscan.error.InvalidAddressException; +import io.api.etherscan.model.TxToken; +import org.junit.Test; + +import java.util.List; + +/** + * @author NGuggs + * @since 11.28.2021 + */ +public class AccountTxRc721TokenTest extends ApiRunner { + + @Test + public void correct() { + List txs = getApi().account().txsNftToken("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67"); + assertNotNull(txs); + assertEquals(16, txs.size()); + 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 + public void correctStartBlock() { + List txs = getApi().account().txsNftToken("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67", 4762071); + System.out.println(txs); + assertNotNull(txs); + assertEquals(5, txs.size()); + assertTxs(txs); + } + + @Test + public void correctStartBlockEndBlock() { + List txs = getApi().account().txsNftToken("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67", 4761862, 4761934); + System.out.println(txs); + assertNotNull(txs); + assertEquals(11, txs.size()); + assertTxs(txs); + } + + @Test(expected = InvalidAddressException.class) + public void invalidParamWithError() { + getApi().account().txsNftToken("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); + } + + @Test + public void correctParamWithEmptyExpectedResult() { + List txs = getApi().account().txsNftToken("0x31ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); + assertNotNull(txs); + assertTrue(txs.isEmpty()); + } + + private void assertTxs(List txs) { + for (TxToken tx : txs) { + assertNotNull(tx.getBlockHash()); + assertNotNull(tx.getTokenName()); + assertNotNull(tx.getTokenSymbol()); + assertNotNull(tx.getFrom()); + assertNotNull(tx.getTo()); + assertNotNull(tx.getTimeStamp()); + assertNotNull(tx.getTokenDecimal()); + assertNotEquals(-1, (tx.getConfirmations())); + assertNotNull(tx.getGasUsed()); + assertNotEquals(-1, tx.getCumulativeGasUsed()); + assertNotEquals(-1, tx.getTransactionIndex()); + } + } +} From 6070078e86777a59751bdffd4d92de35403bf18f Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Fri, 3 Dec 2021 02:12:47 +0300 Subject: [PATCH 049/112] [1.1.1] ApiRunner default key for default key (PR from someone API_KEY is not propagated in github action) --- .../java/io/api/etherscan/manager/impl/QueueManager.java | 2 +- src/test/java/io/api/ApiRunner.java | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) 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 80f7c51..764f7d5 100644 --- a/src/main/java/io/api/etherscan/manager/impl/QueueManager.java +++ b/src/main/java/io/api/etherscan/manager/impl/QueueManager.java @@ -14,7 +14,7 @@ */ public class QueueManager implements IQueueManager, AutoCloseable { - public static final QueueManager DEFAULT_KEY_QUEUE = new QueueManager(1, 7); + 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(); diff --git a/src/test/java/io/api/ApiRunner.java b/src/test/java/io/api/ApiRunner.java index 5ccdf83..184a84e 100644 --- a/src/test/java/io/api/ApiRunner.java +++ b/src/test/java/io/api/ApiRunner.java @@ -20,7 +20,10 @@ public class ApiRunner extends Assert { ? EtherScanApi.DEFAULT_KEY : key; - final QueueManager queueManager = new QueueManager(1, 1200L, 1200L, 0); + 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); From 6f359b5dd99968f08936e512b41c4a0f44cc5272 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Fri, 3 Dec 2021 02:13:33 +0300 Subject: [PATCH 050/112] [1.2.0] Gson updated to 2.8.9 Version updated to 1.2.0 --- build.gradle | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 0e3f321..f599905 100644 --- a/build.gradle +++ b/build.gradle @@ -36,7 +36,7 @@ sonarqube { dependencies { implementation "org.jetbrains:annotations:22.0.0" - implementation "com.google.code.gson:gson:2.8.8" + implementation "com.google.code.gson:gson:2.8.9" testImplementation "junit:junit:4.13.1" } diff --git a/gradle.properties b/gradle.properties index e4aacfe..4022082 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ groupId=com.github.goodforgod artifactId=java-etherscan-api -artifactVersion=1.1.1 +artifactVersion=1.2.0 buildNumber=1 From 22c967983111939862626d612e546b7c7ed3d94a Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Fri, 3 Dec 2021 02:20:45 +0300 Subject: [PATCH 051/112] [1.2.0] README.md fixed --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cd88aaa..4468a8d 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.1.1" + compile "com.github.goodforgod:java-etherscan-api:1.2.0" } ``` @@ -24,7 +24,7 @@ dependencies { com.github.goodforgod java-etherscan-api - 1.1.1 + 1.2.0 ``` From 7fc59463be2790579c39b426351f24b582e7b465 Mon Sep 17 00:00:00 2001 From: guggio Date: Sun, 20 Feb 2022 01:15:32 +0100 Subject: [PATCH 052/112] 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 053/112] [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 054/112] [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 055/112] [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 056/112] [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 057/112] [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 058/112] [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 059/112] 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 060/112] 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 061/112] 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 062/112] 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 063/112] 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 064/112] [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 065/112] [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 066/112] [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 067/112] [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 068/112] [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 069/112] [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 070/112] [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 071/112] [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 072/112] [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 073/112] [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 074/112] [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 075/112] [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 076/112] [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 077/112] [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 078/112] [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 079/112] [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 080/112] [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 081/112] [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 082/112] [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 083/112] [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 084/112] [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 085/112] [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 086/112] [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 087/112] [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 088/112] [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 089/112] [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 090/112] [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 091/112] [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 092/112] [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 093/112] [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 094/112] [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 095/112] [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 096/112] [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 097/112] [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 098/112] [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 099/112] [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 100/112] [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 101/112] 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 102/112] 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 103/112] 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 104/112] 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 105/112] [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 106/112] [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 107/112] [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 108/112] [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 109/112] [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 110/112] [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 111/112] [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 112/112] [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"); ```