diff --git a/app/src/main/java/jfyg/etherscan/helloetherescan/MainActivity.kt b/app/src/main/java/jfyg/etherscan/helloetherescan/MainActivity.kt index ec25946..29b7dd4 100644 --- a/app/src/main/java/jfyg/etherscan/helloetherescan/MainActivity.kt +++ b/app/src/main/java/jfyg/etherscan/helloetherescan/MainActivity.kt @@ -37,7 +37,7 @@ class MainActivity : AppCompatActivity() { } //account test - account.getTransactions("0x2c1ba59d6f58433fb1eaee7d20b26ed83bda51a3") + account.getERC20Tokens("0x4e83362442b8d1bec281594cea3050c8eb01311c") .observeOn(AndroidSchedulers.mainThread()) ?.subscribeBy { Log.d(TAG, "The Account Size of Transactions is: ${it.size}") diff --git a/etherscanapi/src/main/java/jfyg/data/ERC20Token.kt b/etherscanapi/src/main/java/jfyg/data/ERC20Token.kt new file mode 100644 index 0000000..90ecf02 --- /dev/null +++ b/etherscanapi/src/main/java/jfyg/data/ERC20Token.kt @@ -0,0 +1,43 @@ +package jfyg.data + +import com.google.gson.annotations.SerializedName + +data class ERC20Token(var blockNumber: String? = null, + + var timeStamp: String? = null, + + var hash: String? = null, + + var nonce: String? = null, + + var blockHash: String? = null, + + @SerializedName("from") + var transactionFrom: String? = null, + + var contractAddress: String? = null, + + @SerializedName("to") + var transactionTo: String? = null, + + var value: String? = null, + + var tokenName: String? = null, + + var tokenSymbol: String? = null, + + var tokenDecimal: String? = null, + + var transactionIndex: String? = null, + + var gas: String? = null, + + var gasPrice: String? = null, + + var gasUsed: String? = null, + + var cumulativeGasUsed: String? = null, + + var input: String? = null, + + var confirmations: String? = null) \ No newline at end of file diff --git a/etherscanapi/src/main/java/jfyg/data/Txs.kt b/etherscanapi/src/main/java/jfyg/data/Txs.kt index ab9edc2..a13d1c8 100644 --- a/etherscanapi/src/main/java/jfyg/data/Txs.kt +++ b/etherscanapi/src/main/java/jfyg/data/Txs.kt @@ -15,10 +15,10 @@ data class Txs(var blockNumber: String? = null, var transactionIndex: String? = null, @SerializedName("from") - var transactionFrom: String? = null, + var transactionFrom: String? = null, @SerializedName("to") - var transactionTo: String? = null, + var transactionTo: String? = null, var value: String? = null, @@ -29,7 +29,7 @@ data class Txs(var blockNumber: String? = null, var isError: String? = null, @SerializedName("txreceipt_status") - var receiptStatus: String? = null, + var receiptStatus: String? = null, var input: String? = null, diff --git a/etherscanapi/src/main/java/jfyg/data/account/Account.kt b/etherscanapi/src/main/java/jfyg/data/account/Account.kt index ca2de67..e0cfca5 100644 --- a/etherscanapi/src/main/java/jfyg/data/account/Account.kt +++ b/etherscanapi/src/main/java/jfyg/data/account/Account.kt @@ -3,9 +3,11 @@ package jfyg.data.account import io.reactivex.Single import jfyg.data.Balances import jfyg.data.Blocks +import jfyg.data.ERC20Token import jfyg.data.Txs import jfyg.data.TxsInternal import jfyg.network.queries.ApiQuery +import jfyg.utils.Const import jfyg.utils.QueryUtils /** @@ -16,7 +18,7 @@ class Account : AccountContract { private val query = ApiQuery() private val genericNetworkQuery = query.accountBalance("account", "balance", - "0x82e4499D4b2A669831a3881d61BB24f7b620c61a", + Const.GENERIC_PUBLIC_ADDRESS, "latest") /** @@ -50,7 +52,7 @@ class Account : AccountContract { * Get a list of 'Normal' Transactions By Address */ override fun getTransactions(address: String?): Single> = - query.accountTransactions("account", + query.accountTxs("account", "txlist", address, "0", @@ -58,10 +60,22 @@ class Account : AccountContract { "asc").map { it.result } /** - * Get a list of 'Internal' Transactions by Address + * [BETA] Get a list of "ERC20 - Token Transfer Events" by Address + */ + override fun getERC20Tokens(address: String?): Single> = + query.accountERC20Txs("account", + "tokentx", + address, + "0", + "999999999", + "asc").map { it.result } + + + /** + * [BETA] Get a list of 'Internal' Transactions by Address */ override fun getInternalTransactions(address: String?): Single> = - query.accountInternalTransactions("account", + query.accountInternalTxs("account", "txlistinternal", address, "0", diff --git a/etherscanapi/src/main/java/jfyg/data/account/AccountContract.kt b/etherscanapi/src/main/java/jfyg/data/account/AccountContract.kt index b4f0288..29f3dbe 100644 --- a/etherscanapi/src/main/java/jfyg/data/account/AccountContract.kt +++ b/etherscanapi/src/main/java/jfyg/data/account/AccountContract.kt @@ -3,6 +3,7 @@ package jfyg.data.account import io.reactivex.Single import jfyg.data.Balances import jfyg.data.Blocks +import jfyg.data.ERC20Token import jfyg.data.Txs import jfyg.data.TxsInternal @@ -32,7 +33,12 @@ internal interface AccountContract { fun getTransactions(address: String?): Single> /** - * Get a list of 'Internal' Transactions by Address + * [BETA] Get a list of "ERC20 - Token Transfer Events" by Address + */ + fun getERC20Tokens(address: String?): Single> + + /** + * [BETA] Get a list of 'Internal' Transactions by Address */ fun getInternalTransactions(address: String?): Single> diff --git a/etherscanapi/src/main/java/jfyg/data/contract/ContractABI.kt b/etherscanapi/src/main/java/jfyg/data/contract/ContractABI.kt index dd9cbaa..6ae1a6e 100644 --- a/etherscanapi/src/main/java/jfyg/data/contract/ContractABI.kt +++ b/etherscanapi/src/main/java/jfyg/data/contract/ContractABI.kt @@ -2,6 +2,7 @@ package jfyg.data.contract import io.reactivex.Single import jfyg.network.queries.ApiQuery +import jfyg.utils.Const /** * Newly verified Contracts are synced to the API servers within 5 minutes or less @@ -12,7 +13,7 @@ class ContractABI : ContractABIContract { private val query = ApiQuery() private val abiQuery = query.contractABI("contract", "getabi", - "0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413") + Const.CONTRACT_PUBLIC_ADDRESS) /** * Return contract ABI for Verified Contract Source Code diff --git a/etherscanapi/src/main/java/jfyg/data/transaction/TxStatus.kt b/etherscanapi/src/main/java/jfyg/data/transaction/TxStatus.kt index 5ccd0de..5b307e6 100644 --- a/etherscanapi/src/main/java/jfyg/data/transaction/TxStatus.kt +++ b/etherscanapi/src/main/java/jfyg/data/transaction/TxStatus.kt @@ -4,6 +4,7 @@ import io.reactivex.Single import jfyg.data.TxExecutionStatus import jfyg.data.TxReceiptStatus import jfyg.network.queries.ApiQuery +import jfyg.utils.Const /** * https://etherscan.io/apis#transactions @@ -13,7 +14,7 @@ class TxStatus : TxStatusContract { private val query = ApiQuery() private val genericNetworkQuery = query.txReceiptStatus("transaction", "getstatus", - "0x15f8e5ea1079d9a0bb04a4c58ae5fe7654b5b2b4463375ff7ffb490aa0032f3a") + Const.TRANSACTION_PUBLIC_ADDRESS) /** * [BETA] Check Contract Execution Status (if there was an error during contract execution) diff --git a/etherscanapi/src/main/java/jfyg/network/NetworkService.kt b/etherscanapi/src/main/java/jfyg/network/NetworkService.kt index 965ff66..902cbe6 100644 --- a/etherscanapi/src/main/java/jfyg/network/NetworkService.kt +++ b/etherscanapi/src/main/java/jfyg/network/NetworkService.kt @@ -3,6 +3,7 @@ package jfyg.network import io.reactivex.Single import jfyg.network.response.account.AccountBalanceResponse import jfyg.network.response.account.AccountBlockResponse +import jfyg.network.response.account.ERC20Response import jfyg.network.response.account.AccountInternalTxResponse import jfyg.network.response.account.AccountMultiBalanceResponse import jfyg.network.response.account.AccountTxResponse @@ -59,6 +60,15 @@ internal interface NetworkService { @Query("sort") sort: String?, @Query("apikey") apikey: String?): Single + @GET("api") + fun getAccountERC20Transactions(@Query("module") module: String?, + @Query("action") action: String?, + @Query("address") address: String?, + @Query("startblock") startblock: String?, + @Query("endblock") endblock: String?, + @Query("sort") sort: String?, + @Query("apikey") apikey: String?): Single + @GET("api") fun getAccountInternalTransactions(@Query("module") module: String?, @Query("action") action: String?, diff --git a/etherscanapi/src/main/java/jfyg/network/queries/AccountApi.kt b/etherscanapi/src/main/java/jfyg/network/queries/AccountApi.kt index 9896fac..5b3dfa9 100644 --- a/etherscanapi/src/main/java/jfyg/network/queries/AccountApi.kt +++ b/etherscanapi/src/main/java/jfyg/network/queries/AccountApi.kt @@ -3,6 +3,7 @@ package jfyg.network.queries import io.reactivex.Single import jfyg.network.response.account.AccountBalanceResponse import jfyg.network.response.account.AccountBlockResponse +import jfyg.network.response.account.ERC20Response import jfyg.network.response.account.AccountInternalTxResponse import jfyg.network.response.account.AccountMultiBalanceResponse import jfyg.network.response.account.AccountTxResponse @@ -36,17 +37,27 @@ internal interface AccountApi { /** * Get a list of 'Normal' transactions by address */ - fun accountTransactions(module: String?, + fun accountTxs(module: String?, action: String?, address: String?, startblock: String?, endblock: String?, sort: String?): Single + /** + * Get a list of ERC20 transactions by address + */ + fun accountERC20Txs(module: String?, + action: String?, + address: String?, + startblock: String?, + endblock: String?, + sort: String?): Single + /** * Get a list of 'Internal' transactions by address */ - fun accountInternalTransactions(module: String?, + fun accountInternalTxs(module: String?, action: String?, address: String?, startblock: String?, diff --git a/etherscanapi/src/main/java/jfyg/network/queries/ApiQuery.kt b/etherscanapi/src/main/java/jfyg/network/queries/ApiQuery.kt index 6fed24a..4bb934d 100644 --- a/etherscanapi/src/main/java/jfyg/network/queries/ApiQuery.kt +++ b/etherscanapi/src/main/java/jfyg/network/queries/ApiQuery.kt @@ -5,6 +5,7 @@ import jfyg.ApiKey import jfyg.network.RestClient import jfyg.network.response.account.AccountBalanceResponse import jfyg.network.response.account.AccountBlockResponse +import jfyg.network.response.account.ERC20Response import jfyg.network.response.account.AccountInternalTxResponse import jfyg.network.response.account.AccountMultiBalanceResponse import jfyg.network.response.account.AccountTxResponse @@ -38,20 +39,28 @@ internal class ApiQuery : AccountApi, StatApi, ContractABIApi, TxApi { blocktype: String?): Single = RestClient().getQuery().getAccountBlock(module, action, address, blocktype, ApiKey.takeOff.callApiKey()) - override fun accountTransactions(module: String?, - action: String?, - address: String?, - startblock: String?, - endblock: String?, - sort: String?): Single = + override fun accountTxs(module: String?, + action: String?, + address: String?, + startblock: String?, + endblock: String?, + sort: String?): Single = RestClient().getQuery().getAccountTransactions(module, action, address, startblock, endblock, sort, ApiKey.takeOff.callApiKey()) - override fun accountInternalTransactions(module: String?, - action: String?, - address: String?, - startblock: String?, - endblock: String?, - sort: String?): Single = + override fun accountERC20Txs(module: String?, + action: String?, + address: String?, + startblock: String?, + endblock: String?, + sort: String?): Single = + RestClient().getQuery().getAccountERC20Transactions(module, action, address, startblock, endblock, sort, ApiKey.takeOff.callApiKey()) + + override fun accountInternalTxs(module: String?, + action: String?, + address: String?, + startblock: String?, + endblock: String?, + sort: String?): Single = RestClient().getQuery().getAccountInternalTransactions(module, action, address, startblock, endblock, sort, ApiKey.takeOff.callApiKey()) override fun contractABI(module: String?, action: String?, address: String?): Single = diff --git a/etherscanapi/src/main/java/jfyg/network/response/account/ERC20Response.kt b/etherscanapi/src/main/java/jfyg/network/response/account/ERC20Response.kt new file mode 100644 index 0000000..11b6ba0 --- /dev/null +++ b/etherscanapi/src/main/java/jfyg/network/response/account/ERC20Response.kt @@ -0,0 +1,9 @@ +package jfyg.network.response.account + +import jfyg.data.ERC20Token +import jfyg.network.response.BaseResponse + +/** + * ERC20 Transactions recorded by an account + */ +internal class ERC20Response(var result: ArrayList? = null) : BaseResponse() \ No newline at end of file diff --git a/etherscanapi/src/main/java/jfyg/utils/Const.kt b/etherscanapi/src/main/java/jfyg/utils/Const.kt index 67cbf46..cd22e1b 100644 --- a/etherscanapi/src/main/java/jfyg/utils/Const.kt +++ b/etherscanapi/src/main/java/jfyg/utils/Const.kt @@ -6,5 +6,8 @@ package jfyg.utils class Const { companion object { const val BASE_URL = "http://api.etherscan.io/" + const val GENERIC_PUBLIC_ADDRESS = "0x82e4499D4b2A669831a3881d61BB24f7b620c61a" + const val CONTRACT_PUBLIC_ADDRESS = "0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413" + const val TRANSACTION_PUBLIC_ADDRESS = "0x15f8e5ea1079d9a0bb04a4c58ae5fe7654b5b2b4463375ff7ffb490aa0032f3a" } } \ No newline at end of file diff --git a/etherscanapi/src/test/java/jfyg/data/account/AccountTest.kt b/etherscanapi/src/test/java/jfyg/data/account/AccountTest.kt index 659c223..07d4dca 100644 --- a/etherscanapi/src/test/java/jfyg/data/account/AccountTest.kt +++ b/etherscanapi/src/test/java/jfyg/data/account/AccountTest.kt @@ -8,6 +8,7 @@ import jfyg.network.response.account.AccountBlockResponse import jfyg.network.response.account.AccountInternalTxResponse import jfyg.network.response.account.AccountMultiBalanceResponse import jfyg.network.response.account.AccountTxResponse +import jfyg.network.response.account.ERC20Response import org.junit.Assert.assertEquals import org.junit.Assert import org.junit.Before @@ -162,6 +163,56 @@ internal class AccountTest { "result": "Error!" }""" + private val erc20Response = """ + { + "status": "1", + "message": "OK", + "result": [ + { + "blockNumber": "2231007", + "timeStamp": "1473473076", + "hash": "0x48b667b0d84c28e61fe3b5c8c8ba25d848d1b08a646ca79bcbb87475427e2129", + "nonce": "251", + "blockHash": "0x513be6f5e2af2f9f8912767a3f1b9a96edd4251a069649dbfb324d8ac002f48c", + "from": "0xac75b73394c329376c214663d92156afa864a77f", + "contractAddress": "0xecf8f87f810ecf450940c9f60066b4a7a501d6a7", + "to": "0x4e83362442b8d1bec281594cea3050c8eb01311c", + "value": "104000000000000000000", + "tokenName": "\u0001", + "tokenSymbol": "\u0001", + "tokenDecimal": "1", + "transactionIndex": "5", + "gas": "1000000", + "gasPrice": "22723508918", + "gasUsed": "93721", + "cumulativeGasUsed": "199121", + "input": "0x2ac9bf090000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000005f68e8131ecf80000000000000000000000000000000000000000000000000005386e53c7de1e0000", + "confirmations": "3274042" + }, + { + "blockNumber": "2240334", + s"timeStamp": "1473605946", + "hash": "0x48ca19e7184f5cb923f2a11769dbf7d5e28c80a8c93e973a96bb2d0a5897bbc6", + "nonce": "12", + "blockHash": "0x43648e9dfd01ba42a177e1d2b1037395d8946a683bb5ce626de2168b27c7915b", + "from": "0x4e83362442b8d1bec281594cea3050c8eb01311c", + "contractAddress": "0xecf8f87f810ecf450940c9f60066b4a7a501d6a7", + "to": "0xe7b4dba7370f773888d197600d9955a7cc6e0015", + "value": "180000000000000000000", + "tokenName": "\u0001", + "tokenSymbol": "\u0001", + "tokenDecimal": "1", + "transactionIndex": "4", + "gas": "1000000", + "gasPrice": "20000000000", + "gasUsed": "93721", + "cumulativeGasUsed": "196143", + "input": "0x2ac9bf090000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000009f98351204fe00000000000000000000000000000000000000000000000000005386e53c7de1e0000", + "confirmations": "3264715" + } + ] + }""" + @Before fun setUp() { @@ -209,6 +260,14 @@ internal class AccountTest { assertEquals("0", response.result?.get(1)?.isError) } + @Test + fun getERC20Token() { + val response = gson.fromJson(erc20Response, ERC20Response::class.java) + assertEquals("5", response.result?.get(0)?.transactionIndex) + assertEquals("1", response.result?.get(0)?.tokenDecimal) + assertEquals("\u0001", response.result?.get(0)?.tokenSymbol) + } + @Test fun getInternalTransactions() { val response = gson.fromJson(internalTxs, AccountInternalTxResponse::class.java)