Skip to content

Commit 67b695d

Browse files
committed
Removed events stuff again, moved to different library
1 parent 6494764 commit 67b695d

File tree

9 files changed

+930
-0
lines changed

9 files changed

+930
-0
lines changed
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
package io.api.etherscan.core.impl;
2+
3+
import io.api.etherscan.core.IAccountApi;
4+
import io.api.etherscan.error.ApiException;
5+
import io.api.etherscan.error.EtherScanException;
6+
import io.api.etherscan.executor.IHttpExecutor;
7+
import io.api.etherscan.manager.IQueueManager;
8+
import io.api.etherscan.model.*;
9+
import io.api.etherscan.model.utility.*;
10+
import io.api.etherscan.util.BasicUtils;
11+
import org.jetbrains.annotations.NotNull;
12+
13+
import java.math.BigInteger;
14+
import java.util.ArrayList;
15+
import java.util.Collections;
16+
import java.util.List;
17+
import java.util.stream.Collectors;
18+
19+
/**
20+
* Account API Implementation
21+
*
22+
* @see IAccountApi
23+
*
24+
* @author GoodforGod
25+
* @since 28.10.2018
26+
*/
27+
public class AccountApiProvider extends BasicProvider implements IAccountApi {
28+
29+
private static final int OFFSET_MAX = 10000;
30+
31+
private static final String ACT_BALANCE_ACTION = ACT_PREFIX + "balance";
32+
private static final String ACT_TOKEN_BALANCE_PARAM = ACT_PREFIX + "tokenbalance";
33+
private static final String ACT_BALANCE_MULTI_ACTION = ACT_PREFIX + "balancemulti";
34+
private static final String ACT_TX_ACTION = ACT_PREFIX + "txlist";
35+
private static final String ACT_TX_INTERNAL_ACTION = ACT_PREFIX + "txlistinternal";
36+
private static final String ACT_TX_TOKEN_ACTION = ACT_PREFIX + "tokentx";
37+
private static final String ACT_MINED_ACTION = ACT_PREFIX + "getminedblocks";
38+
39+
private static final String BLOCK_TYPE_PARAM = "&blocktype=blocks";
40+
private static final String CONTRACT_PARAM = "&contractaddress=";
41+
private static final String START_BLOCK_PARAM = "&startblock=";
42+
private static final String TAG_LATEST_PARAM = "&tag=latest";
43+
private static final String END_BLOCK_PARAM = "&endblock=";
44+
private static final String SORT_DESC_PARAM = "&sort=desc";
45+
private static final String SORT_ASC_PARAM = "&sort=asc";
46+
private static final String ADDRESS_PARAM = "&address=";
47+
private static final String TXHASH_PARAM = "&txhash=";
48+
private static final String OFFSET_PARAM = "&offset=";
49+
private static final String PAGE_PARAM = "&page=";
50+
51+
AccountApiProvider(final IQueueManager queueManager,
52+
final String baseUrl,
53+
final IHttpExecutor executor) {
54+
super(queueManager, "account", baseUrl, executor);
55+
}
56+
57+
@NotNull
58+
@Override
59+
public Balance balance(final String address) throws ApiException {
60+
BasicUtils.validateAddress(address);
61+
62+
final String urlParams = ACT_BALANCE_ACTION + TAG_LATEST_PARAM + ADDRESS_PARAM + address;
63+
final StringResponseTO response = getRequest(urlParams, StringResponseTO.class);
64+
if (response.getStatus() != 1)
65+
throw new EtherScanException(response.getMessage() + ", with status " + response.getStatus());
66+
67+
return new Balance(address, new BigInteger(response.getResult()));
68+
}
69+
70+
@NotNull
71+
@Override
72+
public TokenBalance balance(final String address, final String contract) throws ApiException {
73+
BasicUtils.validateAddress(address);
74+
BasicUtils.validateAddress(contract);
75+
76+
final String urlParams = ACT_TOKEN_BALANCE_PARAM + ADDRESS_PARAM + address + CONTRACT_PARAM + contract;
77+
final StringResponseTO response = getRequest(urlParams, StringResponseTO.class);
78+
if (response.getStatus() != 1)
79+
throw new EtherScanException(response.getMessage() + ", with status " + response.getStatus());
80+
81+
return new TokenBalance(address, new BigInteger(response.getResult()), contract);
82+
}
83+
84+
@NotNull
85+
@Override
86+
public List<Balance> balances(final List<String> addresses) throws ApiException {
87+
if (BasicUtils.isEmpty(addresses))
88+
return Collections.emptyList();
89+
90+
BasicUtils.validateAddresses(addresses);
91+
92+
// Maximum addresses in batch request - 20
93+
final List<Balance> balances = new ArrayList<>();
94+
final List<List<String>> addressesAsBatches = BasicUtils.partition(addresses, 20);
95+
96+
for (final List<String> batch : addressesAsBatches) {
97+
final String urlParams = ACT_BALANCE_MULTI_ACTION + TAG_LATEST_PARAM + ADDRESS_PARAM + toAddressParam(batch);
98+
final BalanceResponseTO response = getRequest(urlParams, BalanceResponseTO.class);
99+
if (response.getStatus() != 1)
100+
throw new EtherScanException(response.getMessage() + ", with status " + response.getStatus());
101+
102+
if (!BasicUtils.isEmpty(response.getResult()))
103+
balances.addAll(response.getResult().stream()
104+
.map(Balance::of)
105+
.collect(Collectors.toList()));
106+
}
107+
108+
return balances;
109+
}
110+
111+
private String toAddressParam(final List<String> addresses) {
112+
return addresses.stream().collect(Collectors.joining(","));
113+
}
114+
115+
@NotNull
116+
@Override
117+
public List<Tx> txs(final String address) throws ApiException {
118+
return txs(address, MIN_START_BLOCK);
119+
}
120+
121+
@NotNull
122+
@Override
123+
public List<Tx> txs(final String address, final long startBlock) throws ApiException {
124+
return txs(address, startBlock, MAX_END_BLOCK);
125+
}
126+
127+
@NotNull
128+
@Override
129+
public List<Tx> txs(final String address, final long startBlock, final long endBlock) throws ApiException {
130+
BasicUtils.validateAddress(address);
131+
final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock);
132+
133+
final String offsetParam = PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX;
134+
final String blockParam = START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end();
135+
final String urlParams = ACT_TX_ACTION + offsetParam + ADDRESS_PARAM + address + blockParam + SORT_ASC_PARAM;
136+
137+
return getRequestUsingOffset(urlParams, TxResponseTO.class);
138+
}
139+
140+
/**
141+
* Generic search for txs using offset api param
142+
* To avoid 10k limit per response
143+
*
144+
* @param urlParams Url params for #getRequest()
145+
* @param tClass responseListTO class
146+
* @param <T> responseTO list T type
147+
* @param <R> responseListTO type
148+
* @return List of T values
149+
*/
150+
private <T, R extends BaseListResponseTO> List<T> getRequestUsingOffset(final String urlParams, Class<R> tClass)
151+
throws ApiException {
152+
final List<T> result = new ArrayList<>();
153+
int page = 1;
154+
while (true) {
155+
final String formattedUrl = String.format(urlParams, page++);
156+
final R response = getRequest(formattedUrl, tClass);
157+
BasicUtils.validateTxResponse(response);
158+
if (BasicUtils.isEmpty(response.getResult()))
159+
break;
160+
161+
result.addAll(response.getResult());
162+
if (response.getResult().size() < OFFSET_MAX)
163+
break;
164+
}
165+
166+
return result;
167+
}
168+
169+
@NotNull
170+
@Override
171+
public List<TxInternal> txsInternal(final String address) throws ApiException {
172+
return txsInternal(address, MIN_START_BLOCK);
173+
}
174+
175+
@NotNull
176+
@Override
177+
public List<TxInternal> txsInternal(final String address, final long startBlock) throws ApiException {
178+
return txsInternal(address, startBlock, MAX_END_BLOCK);
179+
}
180+
181+
@NotNull
182+
@Override
183+
public List<TxInternal> txsInternal(final String address, final long startBlock, final long endBlock) throws ApiException {
184+
BasicUtils.validateAddress(address);
185+
final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock);
186+
187+
final String offsetParam = PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX;
188+
final String blockParam = START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end();
189+
final String urlParams = ACT_TX_INTERNAL_ACTION + offsetParam + ADDRESS_PARAM + address + blockParam + SORT_ASC_PARAM;
190+
191+
return getRequestUsingOffset(urlParams, TxInternalResponseTO.class);
192+
}
193+
194+
@NotNull
195+
@Override
196+
public List<TxInternal> txsInternalByHash(final String txhash) throws ApiException {
197+
BasicUtils.validateTxHash(txhash);
198+
199+
final String urlParams = ACT_TX_INTERNAL_ACTION + TXHASH_PARAM + txhash;
200+
final TxInternalResponseTO response = getRequest(urlParams, TxInternalResponseTO.class);
201+
BasicUtils.validateTxResponse(response);
202+
203+
return BasicUtils.isEmpty(response.getResult())
204+
? Collections.emptyList()
205+
: response.getResult();
206+
}
207+
208+
@NotNull
209+
@Override
210+
public List<TxToken> txsToken(final String address) throws ApiException {
211+
return txsToken(address, MIN_START_BLOCK);
212+
}
213+
214+
@NotNull
215+
@Override
216+
public List<TxToken> txsToken(final String address, final long startBlock) throws ApiException {
217+
return txsToken(address, startBlock, MAX_END_BLOCK);
218+
}
219+
220+
@NotNull
221+
@Override
222+
public List<TxToken> txsToken(final String address, final long startBlock, final long endBlock) throws ApiException {
223+
BasicUtils.validateAddress(address);
224+
final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock);
225+
226+
final String offsetParam = PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX;
227+
final String blockParam = START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end();
228+
final String urlParams = ACT_TX_TOKEN_ACTION + offsetParam + ADDRESS_PARAM + address + blockParam + SORT_ASC_PARAM;
229+
230+
return getRequestUsingOffset(urlParams, TxTokenResponseTO.class);
231+
}
232+
233+
@NotNull
234+
@Override
235+
public List<Block> minedBlocks(final String address) throws ApiException {
236+
BasicUtils.validateAddress(address);
237+
238+
final String offsetParam = PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX;
239+
final String urlParams = ACT_MINED_ACTION + offsetParam + BLOCK_TYPE_PARAM + ADDRESS_PARAM + address;
240+
241+
return getRequestUsingOffset(urlParams, BlockResponseTO.class);
242+
}
243+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package io.api.etherscan.core.impl;
2+
3+
import com.google.gson.Gson;
4+
import io.api.etherscan.error.EtherScanException;
5+
import io.api.etherscan.error.ParseException;
6+
import io.api.etherscan.executor.IHttpExecutor;
7+
import io.api.etherscan.manager.IQueueManager;
8+
import io.api.etherscan.util.BasicUtils;
9+
10+
/**
11+
* Base provider for API Implementations
12+
*
13+
* @author GoodforGod
14+
* @see EtherScanApi
15+
* @since 28.10.2018
16+
*/
17+
abstract class BasicProvider {
18+
19+
static final int MAX_END_BLOCK = 999999999;
20+
static final int MIN_START_BLOCK = 0;
21+
22+
static final String ACT_PREFIX = "&action=";
23+
24+
private final String module;
25+
private final String baseUrl;
26+
private final IHttpExecutor executor;
27+
private final IQueueManager queue;
28+
private final Gson gson;
29+
30+
BasicProvider(final IQueueManager queue,
31+
final String module,
32+
final String baseUrl,
33+
final IHttpExecutor executor) {
34+
this.queue = queue;
35+
this.module = "&module=" + module;
36+
this.baseUrl = baseUrl;
37+
this.executor = executor;
38+
this.gson = new Gson();
39+
}
40+
41+
<T> T convert(final String json, final Class<T> tClass) {
42+
try {
43+
return gson.fromJson(json, tClass);
44+
} catch (Exception e) {
45+
throw new ParseException(e.getMessage(), e.getCause());
46+
}
47+
}
48+
49+
String getRequest(final String urlParameters) {
50+
queue.takeTurn();
51+
final String url = baseUrl + module + urlParameters;
52+
final String result = executor.get(url);
53+
if (BasicUtils.isEmpty(result))
54+
throw new EtherScanException("Server returned null value for GET request at URL - " + url);
55+
56+
return result;
57+
}
58+
59+
String postRequest(final String urlParameters, final String dataToPost) {
60+
queue.takeTurn();
61+
final String url = baseUrl + module + urlParameters;
62+
return executor.post(url, dataToPost);
63+
}
64+
65+
<T> T getRequest(final String urlParameters, final Class<T> tClass) {
66+
return convert(getRequest(urlParameters), tClass);
67+
}
68+
69+
<T> T postRequest(final String urlParameters, final String dataToPost, final Class<T> tClass) {
70+
return convert(postRequest(urlParameters, dataToPost), tClass);
71+
}
72+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package io.api.etherscan.core.impl;
2+
3+
import io.api.etherscan.core.IBlockApi;
4+
import io.api.etherscan.error.ApiException;
5+
import io.api.etherscan.executor.IHttpExecutor;
6+
import io.api.etherscan.manager.IQueueManager;
7+
import io.api.etherscan.model.UncleBlock;
8+
import io.api.etherscan.model.utility.UncleBlockResponseTO;
9+
import io.api.etherscan.util.BasicUtils;
10+
import org.jetbrains.annotations.NotNull;
11+
12+
import java.util.Optional;
13+
14+
/**
15+
* Block API Implementation
16+
*
17+
* @see IBlockApi
18+
*
19+
* @author GoodforGod
20+
* @since 28.10.2018
21+
*/
22+
public class BlockApiProvider extends BasicProvider implements IBlockApi {
23+
24+
private static final String ACT_BLOCK_PARAM = ACT_PREFIX + "getblockreward";
25+
26+
private static final String BLOCKNO_PARAM = "&blockno=";
27+
28+
BlockApiProvider(final IQueueManager queueManager,
29+
final String baseUrl,
30+
final IHttpExecutor executor) {
31+
super(queueManager, "block", baseUrl, executor);
32+
}
33+
34+
@NotNull
35+
@Override
36+
public Optional<UncleBlock> uncles(long blockNumber) throws ApiException {
37+
final String urlParam = ACT_BLOCK_PARAM + BLOCKNO_PARAM + blockNumber;
38+
final String response = getRequest(urlParam);
39+
if(BasicUtils.isEmpty(response) || response.contains("NOTOK"))
40+
return Optional.empty();
41+
42+
final UncleBlockResponseTO responseTO = convert(response, UncleBlockResponseTO.class);
43+
BasicUtils.validateTxResponse(responseTO);
44+
return (responseTO.getResult() == null || responseTO.getResult().isEmpty())
45+
? Optional.empty()
46+
: Optional.of(responseTO.getResult());
47+
}
48+
}

0 commit comments

Comments
 (0)