Skip to content

Commit 333cfe4

Browse files
committed
Contract creation API
1 parent 0e1dccc commit 333cfe4

File tree

8 files changed

+196
-5
lines changed

8 files changed

+196
-5
lines changed

Diff for: src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java

+1-5
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public List<Balance> balances(@NotNull List<String> addresses) throws EtherScanE
9595
final List<List<String>> addressesAsBatches = BasicUtils.partition(addresses, 20);
9696

9797
for (final List<String> batch : addressesAsBatches) {
98-
final String urlParams = ACT_BALANCE_MULTI_ACTION + TAG_LATEST_PARAM + ADDRESS_PARAM + toAddressParam(batch);
98+
final String urlParams = ACT_BALANCE_MULTI_ACTION + TAG_LATEST_PARAM + ADDRESS_PARAM + BasicUtils.toAddressParam(batch);
9999
final BalanceResponseTO response = getRequest(urlParams, BalanceResponseTO.class);
100100
if (response.getStatus() != 1) {
101101
throw new EtherScanResponseException(response);
@@ -111,10 +111,6 @@ public List<Balance> balances(@NotNull List<String> addresses) throws EtherScanE
111111
return balances;
112112
}
113113

114-
private String toAddressParam(List<String> addresses) {
115-
return String.join(",", addresses);
116-
}
117-
118114
@NotNull
119115
@Override
120116
public List<Tx> txs(@NotNull String address) throws EtherScanException {

Diff for: src/main/java/io/goodforgod/api/etherscan/ContractAPI.java

+11
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
import io.goodforgod.api.etherscan.error.EtherScanException;
44
import io.goodforgod.api.etherscan.model.Abi;
5+
import io.goodforgod.api.etherscan.model.ContractCreation;
56
import org.jetbrains.annotations.NotNull;
67

8+
import java.util.List;
9+
710
/**
811
* EtherScan - API Descriptions <a href="https://docs.etherscan.io/api-endpoints/contracts">...</a>
912
*
@@ -21,4 +24,12 @@ public interface ContractAPI {
2124
*/
2225
@NotNull
2326
Abi contractAbi(@NotNull String address) throws EtherScanException;
27+
28+
/**
29+
* Returns a contract's deployer address and transaction hash it was created, up to 5 at a time.
30+
* @param contractAddresses - list of addresses to fetch
31+
* @throws EtherScanException parent exception class
32+
*/
33+
@NotNull
34+
List<ContractCreation> contractCreation(@NotNull List<String> contractAddresses) throws EtherScanException;
2435
}

Diff for: src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java

+30
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,15 @@
55
import io.goodforgod.api.etherscan.http.EthHttpClient;
66
import io.goodforgod.api.etherscan.manager.RequestQueueManager;
77
import io.goodforgod.api.etherscan.model.Abi;
8+
import io.goodforgod.api.etherscan.model.ContractCreation;
9+
import io.goodforgod.api.etherscan.model.response.ContractCreationResponseTO;
810
import io.goodforgod.api.etherscan.model.response.StringResponseTO;
911
import io.goodforgod.api.etherscan.util.BasicUtils;
1012
import org.jetbrains.annotations.NotNull;
1113

14+
import java.util.List;
15+
import java.util.stream.Collectors;
16+
1217
/**
1318
* Contract API Implementation
1419
*
@@ -22,6 +27,12 @@ final class ContractAPIProvider extends BasicProvider implements ContractAPI {
2227

2328
private static final String ADDRESS_PARAM = "&address=";
2429

30+
private static final String ACT_CONTRACT_CREATION_PARAM = "getcontractcreation";
31+
32+
private static final String ACT_CONTRACT_CREATION = ACT_PREFIX + ACT_CONTRACT_CREATION_PARAM;
33+
34+
private static final String ACT_CONTRACT_ADDRESSES_PARAM = "&contractaddresses=";
35+
2536
ContractAPIProvider(RequestQueueManager requestQueueManager,
2637
String baseUrl,
2738
EthHttpClient executor,
@@ -44,4 +55,23 @@ public Abi contractAbi(@NotNull String address) throws EtherScanException {
4455
? Abi.nonVerified()
4556
: Abi.verified(response.getResult());
4657
}
58+
59+
@NotNull
60+
@Override
61+
public List<ContractCreation> contractCreation(@NotNull List<String> contractAddresses) throws EtherScanException {
62+
BasicUtils.validateAddresses(contractAddresses);
63+
final String urlParam = ACT_CONTRACT_CREATION + ACT_CONTRACT_ADDRESSES_PARAM + BasicUtils.toAddressParam(contractAddresses);
64+
final ContractCreationResponseTO response = getRequest(urlParam, ContractCreationResponseTO.class);
65+
if (response.getStatus() != 1 && response.getMessage().startsWith("NOTOK")) {
66+
throw new EtherScanResponseException(response);
67+
}
68+
69+
return response.getResult().stream()
70+
.map(to -> ContractCreation.builder()
71+
.withContractCreator(to.getContractCreator())
72+
.withContractAddress(to.getContractAddress())
73+
.withTxHash(to.getTxHash())
74+
.build()
75+
).collect(Collectors.toList());
76+
}
4777
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package io.goodforgod.api.etherscan.model;
2+
3+
import java.util.Objects;
4+
5+
public class ContractCreation {
6+
private final String contractAddress;
7+
private final String contractCreator;
8+
private final String txHash;
9+
10+
private ContractCreation(String contractAddress, String contractCreator, String txHash) {
11+
this.contractAddress = contractAddress;
12+
this.contractCreator = contractCreator;
13+
this.txHash = txHash;
14+
}
15+
16+
public String getContractAddress() {
17+
return contractAddress;
18+
}
19+
20+
public String getContractCreator() {
21+
return contractCreator;
22+
}
23+
24+
public String getTxHash() {
25+
return txHash;
26+
}
27+
28+
@Override
29+
public boolean equals(Object o) {
30+
if (this == o) return true;
31+
if (o == null || getClass() != o.getClass()) return false;
32+
ContractCreation that = (ContractCreation) o;
33+
return Objects.equals(contractAddress, that.contractAddress) && Objects.equals(contractCreator, that.contractCreator) && Objects.equals(txHash, that.txHash);
34+
}
35+
36+
@Override
37+
public int hashCode() {
38+
return Objects.hash(contractAddress, contractCreator, txHash);
39+
}
40+
41+
@Override
42+
public String toString() {
43+
return "ContractCreation{" +
44+
"contractAddress='" + contractAddress + '\'' +
45+
", contractCreator='" + contractCreator + '\'' +
46+
", txHash='" + txHash + '\'' +
47+
'}';
48+
}
49+
50+
public static ContractCreationBuilder builder() {
51+
return new ContractCreationBuilder();
52+
}
53+
54+
public static final class ContractCreationBuilder {
55+
private String contractAddress;
56+
private String contractCreator;
57+
private String txHash;
58+
59+
private ContractCreationBuilder() {}
60+
61+
public ContractCreationBuilder withContractAddress(String contractAddress) {
62+
this.contractAddress = contractAddress;
63+
return this;
64+
}
65+
66+
public ContractCreationBuilder withContractCreator(String contractCreator) {
67+
this.contractCreator = contractCreator;
68+
return this;
69+
}
70+
71+
public ContractCreationBuilder withTxHash(String txHash) {
72+
this.txHash = txHash;
73+
return this;
74+
}
75+
76+
public ContractCreation build() {
77+
return new ContractCreation(contractAddress, contractCreator, txHash);
78+
}
79+
}
80+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package io.goodforgod.api.etherscan.model.response;
2+
3+
public class ContractCreationResponseTO extends BaseListResponseTO<ContractCreationTO> {
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package io.goodforgod.api.etherscan.model.response;
2+
3+
public class ContractCreationTO {
4+
5+
private String contractAddress;
6+
private String contractCreator;
7+
private String txHash;
8+
9+
public String getContractAddress() {
10+
return contractAddress;
11+
}
12+
13+
public String getContractCreator() {
14+
return contractCreator;
15+
}
16+
17+
public String getTxHash() {
18+
return txHash;
19+
}
20+
}

Diff for: src/main/java/io/goodforgod/api/etherscan/util/BasicUtils.java

+4
Original file line numberDiff line numberDiff line change
@@ -149,4 +149,8 @@ public static List<List<String>> partition(List<String> list, int pairSize) {
149149

150150
return partitioned;
151151
}
152+
153+
public static String toAddressParam(List<String> addresses) {
154+
return String.join(",", addresses);
155+
}
152156
}

Diff for: src/test/java/io/goodforgod/api/etherscan/contract/ContractApiTests.java

+46
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,13 @@
33
import io.goodforgod.api.etherscan.ApiRunner;
44
import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException;
55
import io.goodforgod.api.etherscan.model.Abi;
6+
import io.goodforgod.api.etherscan.model.ContractCreation;
67
import org.junit.jupiter.api.Test;
78

9+
import java.util.Arrays;
10+
import java.util.Collections;
11+
import java.util.List;
12+
813
/**
914
* @author GoodforGod
1015
* @since 03.11.2018
@@ -37,4 +42,45 @@ void correctParamWithEmptyExpectedResult() {
3742
assertNotNull(abi);
3843
assertTrue(abi.isVerified());
3944
}
45+
46+
@Test
47+
void correctContractCreation() {
48+
List<ContractCreation> contractCreations =
49+
getApi().contract().contractCreation(Collections.singletonList("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413"));
50+
51+
assertEquals(1, contractCreations.size());
52+
ContractCreation contractCreation = contractCreations.get(0);
53+
54+
assertEquals("0xbb9bc244d798123fde783fcc1c72d3bb8c189413", contractCreation.getContractAddress());
55+
assertEquals("0x793ea9692ada1900fbd0b80fffec6e431fe8b391", contractCreation.getContractCreator());
56+
assertEquals("0xe9ebfecc2fa10100db51a4408d18193b3ac504584b51a4e55bdef1318f0a30f9", contractCreation.getTxHash());
57+
}
58+
59+
@Test
60+
void correctMultipleContractCreation() {
61+
List<ContractCreation> contractCreations =
62+
getApi().contract().contractCreation(Arrays.asList("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413", "0x5EaC95ad5b287cF44E058dCf694419333b796123"));
63+
assertEquals(2, contractCreations.size());
64+
65+
ContractCreation contractCreation1 = ContractCreation.builder()
66+
.withContractAddress("0xbb9bc244d798123fde783fcc1c72d3bb8c189413")
67+
.withContractCreator("0x793ea9692ada1900fbd0b80fffec6e431fe8b391")
68+
.withTxHash("0xe9ebfecc2fa10100db51a4408d18193b3ac504584b51a4e55bdef1318f0a30f9")
69+
.build();
70+
71+
ContractCreation contractCreation2 = ContractCreation.builder()
72+
.withContractAddress("0x5eac95ad5b287cf44e058dcf694419333b796123")
73+
.withContractCreator("0x7c675b7450e878e5af8550b41df42d134674e61f")
74+
.withTxHash("0x79cdfec19e5a86d9022680a4d1c86d3d8cd76c21c01903a2f02c127a0a7dbfb3")
75+
.build();
76+
77+
assertTrue(contractCreations.contains(contractCreation1));
78+
assertTrue(contractCreations.contains(contractCreation2));
79+
}
80+
81+
@Test
82+
void contractCreationInvalidParamWithError() {
83+
assertThrows(EtherScanInvalidAddressException.class,
84+
() -> getApi().contract().contractCreation(Collections.singletonList("0xBBbc244D798123fDe783fCc1C72d3Bb8C189414")));
85+
}
4086
}

0 commit comments

Comments
 (0)