Skip to content

Commit ca37666

Browse files
committed
Added Tron explorer
1 parent 379e95a commit ca37666

13 files changed

+250
-55
lines changed

build.gradle

+15-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
plugins {
22
id 'jacoco'
33
id 'idea'
4+
id 'java'
5+
id 'application'
46
id 'org.jetbrains.kotlin.jvm' version '1.9.21'
57
id 'org.jetbrains.kotlin.plugin.spring' version '1.9.21'
68
id 'org.jetbrains.kotlin.kapt' version '1.9.21'
@@ -17,7 +19,15 @@ java.targetCompatibility = JavaVersion.VERSION_17
1719
repositories {
1820
mavenCentral()
1921
jcenter()
20-
maven { url "https://jitpack.io" }
22+
// maven {
23+
// url "https://jitpack.io"
24+
// }
25+
26+
maven {
27+
url "https://dl.bintray.com/tronj/tronj"
28+
}
29+
30+
2131
}
2232

2333
dependencies {
@@ -39,16 +49,16 @@ dependencies {
3949
implementation('org.jetbrains.kotlinx:kotlinx-coroutines-jdk8')
4050

4151
// Ethereum
42-
implementation('org.web3j:core:4.12.0'){
43-
exclude group : 'org.bouncycastle', module : 'bcprov-jdk18on'
44-
exclude group : 'com.squareup.okhttp3', module : 'okhttp'
52+
implementation('org.web3j:core:4.12.0') {
53+
exclude group: 'org.bouncycastle', module: 'bcprov-jdk18on'
54+
exclude group: 'com.squareup.okhttp3', module: 'okhttp'
4555
}
4656
implementation('org.bouncycastle:bcprov-jdk18on:1.78.1')
4757
implementation('com.squareup.okhttp3:okhttp:4.12.0')
4858

4959
// Binance
5060
implementation('com.google.protobuf:protobuf-java:4.26.0')
51-
implementation('com.github.binance-chain:java-sdk:v1.1.0-bscAlpha.0')
61+
//implementation('com.github.binance-chain:java-sdk:v1.1.0-bscAlpha.0')
5262

5363
// Utils
5464
implementation('commons-validator:commons-validator:1.9.0')

src/main/kotlin/io/openfuture/state/blockchain/tron/TronBlockchain.kt

+6-6
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,15 @@ import java.math.BigInteger
2727

2828
@Component
2929
@ConditionalOnProperty(value = ["production.mode.enabled"], havingValue = "true")
30-
class TronBlockchain(@Qualifier("web3jTronTestnet") private val web3jTronTestnet: Web3j) : Blockchain() {
30+
class TronBlockchain(@Qualifier("web3jTron") private val web3jTron: Web3j) : Blockchain() {
3131

32-
override suspend fun getLastBlockNumber(): Int = web3jTronTestnet.ethBlockNumber()
32+
override suspend fun getLastBlockNumber(): Int = web3jTron.ethBlockNumber()
3333
.sendAsync().await()
3434
.blockNumber.toInt()
3535

3636
override suspend fun getBlock(blockNumber: Int): UnifiedBlock {
3737
val parameter = DefaultBlockParameterNumber(blockNumber.toLong())
38-
val block = web3jTronTestnet.ethGetBlockByNumber(parameter, true)
38+
val block = web3jTron.ethGetBlockByNumber(parameter, true)
3939
.sendAsync().await()
4040
.block
4141
val transactions = obtainTransactions(block)
@@ -45,7 +45,7 @@ class TronBlockchain(@Qualifier("web3jTronTestnet") private val web3jTronTestnet
4545

4646
override suspend fun getBalance(address: String): BigDecimal {
4747
val parameter = DefaultBlockParameterName.LATEST
48-
val balanceWei = web3jTronTestnet.ethGetBalance(address, parameter)
48+
val balanceWei = web3jTron.ethGetBalance(address, parameter)
4949
.sendAsync().await()
5050
.balance
5151
return Convert.fromWei(balanceWei.toString(), Convert.Unit.ETHER)
@@ -59,7 +59,7 @@ class TronBlockchain(@Qualifier("web3jTronTestnet") private val web3jTronTestnet
5959
listOf(object : TypeReference<Uint256>() {})
6060
)
6161
val encodedFunction = FunctionEncoder.encode(functionBalance)
62-
val ethCall: EthCall = web3jTronTestnet.ethCall(
62+
val ethCall: EthCall = web3jTron.ethCall(
6363
Transaction.createEthCallTransaction(address, contractAddress, encodedFunction),
6464
DefaultBlockParameterName.LATEST
6565
).sendAsync().await()
@@ -87,7 +87,7 @@ class TronBlockchain(@Qualifier("web3jTronTestnet") private val web3jTronTestnet
8787
}
8888

8989
private suspend fun findContractAddress(transactionHash: String) =
90-
web3jTronTestnet.ethGetTransactionReceipt(transactionHash)
90+
web3jTron.ethGetTransactionReceipt(transactionHash)
9191
.sendAsync().await()
9292
.transactionReceipt.get()
9393
.contractAddress
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package io.openfuture.state.blockchain.tron
2+
3+
import io.openfuture.state.blockchain.Blockchain
4+
import io.openfuture.state.blockchain.dto.UnifiedBlock
5+
import io.openfuture.state.blockchain.dto.UnifiedTransaction
6+
import io.openfuture.state.domain.CurrencyCode
7+
import io.openfuture.state.util.HashUtils.decodeBase58
8+
import io.openfuture.state.util.HashUtils.toHexString
9+
import io.openfuture.state.util.toLocalDateTime
10+
import kotlinx.coroutines.future.await
11+
import org.springframework.beans.factory.annotation.Qualifier
12+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
13+
import org.springframework.stereotype.Component
14+
import org.web3j.abi.FunctionEncoder
15+
import org.web3j.abi.FunctionReturnDecoder
16+
import org.web3j.abi.TypeReference
17+
import org.web3j.abi.Utils
18+
import org.web3j.abi.datatypes.Address
19+
import org.web3j.abi.datatypes.generated.Uint256
20+
import org.web3j.protocol.Web3j
21+
import org.web3j.protocol.core.DefaultBlockParameterName
22+
import org.web3j.protocol.core.DefaultBlockParameterNumber
23+
import org.web3j.protocol.core.methods.request.Transaction
24+
import org.web3j.protocol.core.methods.response.EthBlock
25+
import org.web3j.protocol.core.methods.response.EthCall
26+
import org.web3j.utils.Convert
27+
import java.math.BigDecimal
28+
import java.math.BigInteger
29+
30+
@Component
31+
class TronShastaBlockchain(@Qualifier("web3jTronTestnet") private val web3jTronTestnet: Web3j) : Blockchain() {
32+
33+
override suspend fun getLastBlockNumber(): Int = web3jTronTestnet.ethBlockNumber()
34+
.sendAsync().await()
35+
.blockNumber.toInt()
36+
37+
override suspend fun getBlock(blockNumber: Int): UnifiedBlock {
38+
val parameter = DefaultBlockParameterNumber(blockNumber.toLong())
39+
val block = web3jTronTestnet.ethGetBlockByNumber(parameter, true)
40+
.sendAsync().await()
41+
.block
42+
val transactions = obtainTransactions(block)
43+
val date = block.timestamp.toLong().toLocalDateTime()
44+
return UnifiedBlock(transactions, date, block.number.toLong(), block.hash)
45+
}
46+
47+
override suspend fun getBalance(address: String): BigDecimal {
48+
val parameter = DefaultBlockParameterName.LATEST
49+
val ethAddress = base58ToEthAddress(address)
50+
val balanceWei = web3jTronTestnet.ethGetBalance(ethAddress, parameter)
51+
.sendAsync().await()
52+
.balance
53+
return Convert.fromWei(balanceWei.toString(), Convert.Unit.MWEI)
54+
}
55+
56+
private fun base58ToEthAddress(address: String): String {
57+
val addressDecode58 = address.decodeBase58().toHexString()
58+
//eth address is 42 length
59+
return "0x" + addressDecode58.substring(2, 42)
60+
}
61+
62+
override suspend fun getContractBalance(address: String, contractAddress: String): BigDecimal {
63+
val ethAddress = base58ToEthAddress(address)
64+
val ethContractAddress = base58ToEthAddress(contractAddress)
65+
println("ethAddress: $ethAddress")
66+
println("ethCContractAddress: $ethContractAddress")
67+
val functionBalance = org.web3j.abi.datatypes.Function(
68+
"balanceOf",
69+
listOf(Address(ethAddress)),
70+
listOf(object : TypeReference<Uint256>() {})
71+
)
72+
val encodedFunction = FunctionEncoder.encode(functionBalance)
73+
val ethCall: EthCall = web3jTronTestnet.ethCall(
74+
Transaction.createEthCallTransaction(ethAddress, ethContractAddress, encodedFunction),
75+
DefaultBlockParameterName.LATEST
76+
).sendAsync().await()
77+
78+
val value = ethCall.value
79+
val decode = FunctionReturnDecoder.decode(value, functionBalance.outputParameters)
80+
val contractBalance = BigInteger(value.substring(2, value.length), 16)
81+
decode.iterator().forEach { a -> println("a ${a.value} with ${a.typeAsString}") }
82+
83+
println("Value $value")
84+
85+
return Convert.fromWei(contractBalance.toString(), Convert.Unit.MWEI)
86+
}
87+
88+
override suspend fun getCurrencyCode(): CurrencyCode {
89+
return CurrencyCode.TRON
90+
}
91+
92+
private suspend fun obtainTransactions(ethBlock: EthBlock.Block): List<UnifiedTransaction> = ethBlock.transactions
93+
.map { it.get() as EthBlock.TransactionObject }
94+
.map { tx ->
95+
val to = tx.to ?: findContractAddress(tx.hash)
96+
val amount = Convert.fromWei(tx.value.toBigDecimal(), Convert.Unit.MWEI)
97+
UnifiedTransaction(tx.hash, tx.from, to, amount, true, to)
98+
}
99+
100+
private suspend fun findContractAddress(transactionHash: String) =
101+
web3jTronTestnet.ethGetTransactionReceipt(transactionHash)
102+
.sendAsync().await()
103+
.transactionReceipt.get()
104+
.contractAddress
105+
106+
companion object {
107+
private val DECODE_TYPES = Utils.convert(
108+
listOf(
109+
object : TypeReference<Address>(true) {},
110+
object : TypeReference<Uint256>() {}
111+
)
112+
)
113+
114+
private const val TRANSFER_METHOD_SIGNATURE = "0xa9059cbb"
115+
private const val TRANSFER_INPUT_LENGTH = 138
116+
}
117+
}

src/main/kotlin/io/openfuture/state/client/BinanceHttpClientApi.kt src/main/kotlin/io/openfuture/state/client/CoinGateHttpClientApi.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import org.springframework.stereotype.Component
77
import org.springframework.web.reactive.function.client.WebClient
88

99
@Component
10-
class BinanceHttpClientApi(builder: WebClient.Builder) {
10+
class CoinGateHttpClientApi(builder: WebClient.Builder) {
1111

1212
val client: WebClient = builder.build()
1313

src/main/kotlin/io/openfuture/state/client/CoinGeckoApiClient.kt

-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package io.openfuture.state.client
22

33
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
44
import com.fasterxml.jackson.annotation.JsonProperty
5-
import org.bitcoinj.core.Ping
65

76

87
interface CoinGeckoApiClient {

src/main/kotlin/io/openfuture/state/client/CoinGeckoHttpClient.kt

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package io.openfuture.state.client
22

3-
import org.bitcoinj.core.Ping
43
import org.springframework.stereotype.Component
54

65
@Component
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package io.openfuture.state.config
2+
3+
import org.springframework.beans.factory.annotation.Value
4+
import org.springframework.stereotype.Component
5+
6+
@Component
7+
class AppProperties{
8+
@Value("\${production.mode.enabled}")
9+
lateinit var isProdEnabled: String
10+
}
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,25 @@
11
package io.openfuture.state.controller
22

3-
import io.openfuture.state.blockchain.Blockchain
4-
import io.openfuture.state.blockchain.binance.BinanceBlockchain
5-
import io.openfuture.state.blockchain.bitcoin.BitcoinBlockchain
6-
import io.openfuture.state.blockchain.ethereum.EthereumBlockchain
7-
import io.openfuture.state.client.BinanceHttpClientApi
3+
import io.openfuture.state.client.CoinGateHttpClientApi
84
import io.openfuture.state.client.ExchangeRate
9-
import io.openfuture.state.domain.CoinGateRate
105
import io.openfuture.state.domain.CurrencyCode
11-
import org.springframework.web.bind.annotation.GetMapping
12-
import org.springframework.web.bind.annotation.PathVariable
13-
import org.springframework.web.bind.annotation.RequestMapping
14-
import org.springframework.web.bind.annotation.RequestParam
15-
import org.springframework.web.bind.annotation.RestController
16-
import java.math.BigDecimal
17-
import java.math.RoundingMode
18-
import java.util.*
6+
import org.springframework.web.bind.annotation.*
197

208
@RestController
219
@RequestMapping("/api/currency/rate")
2210
class ExchangeRateController(
23-
val binanceHttpClientApi: BinanceHttpClientApi,
24-
val blockchains: List<Blockchain>
11+
val coinGateHttpClientApi: CoinGateHttpClientApi
2512
) {
2613

2714
@GetMapping("/{baseTicker}")
2815
suspend fun getRate(@PathVariable baseTicker: String ): ExchangeRate {
29-
// for (blockchain in blockchains) {
30-
// if (blockchain.getName().lowercase().startsWith("EthereumBlockchain") || blockchain.getName().lowercase().startsWith("GoerliBlockchain")) {
31-
// val price = binanceHttpClientApi.getExchangeRate(blockchain).price
32-
// return BigDecimal.ONE.divide(price, price.scale(), RoundingMode.HALF_UP).stripTrailingZeros()
33-
// }
34-
// }
35-
// return BigDecimal.ONE
3616
val currencyCode = CurrencyCode.entries.find { it.code == baseTicker }
37-
return binanceHttpClientApi.getExchangeRate(currencyCode!!)
17+
return coinGateHttpClientApi.getExchangeRate(currencyCode!!)
3818
}
3919

4020
@GetMapping("/all")
4121
suspend fun getAllRates(@RequestParam("ticker", required = false) ticker: String?): Any {
42-
return binanceHttpClientApi.getAllRateFromApi(ticker)
22+
return coinGateHttpClientApi.getAllRateFromApi(ticker)
4323
}
4424

4525
}

src/main/kotlin/io/openfuture/state/controller/WalletControllerV2.kt

+22-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package io.openfuture.state.controller
22

3+
import io.openfuture.state.config.AppProperties
34
import io.openfuture.state.controller.request.BalanceRequest
45
import io.openfuture.state.service.BlockchainLookupService
56
import io.openfuture.state.service.WalletService
67
import io.openfuture.state.service.dto.AddWatchResponse
78
import io.openfuture.state.service.dto.WalletBalanceResponse
9+
import org.springframework.beans.factory.annotation.Autowired
810
import org.springframework.web.bind.annotation.PostMapping
911
import org.springframework.web.bind.annotation.RequestBody
1012
import org.springframework.web.bind.annotation.RequestMapping
@@ -17,6 +19,9 @@ class WalletControllerV2(
1719
private val blockchainLookupService: BlockchainLookupService
1820
) {
1921

22+
@Autowired
23+
lateinit var appProperties: AppProperties
24+
2025
@PostMapping("add")
2126
suspend fun addWallet(@RequestBody request: AddWalletStateForUserRequest): AddWatchResponse {
2227
return walletService.addWallet(request)
@@ -25,15 +30,26 @@ class WalletControllerV2(
2530
@PostMapping("/balance")
2631
suspend fun getBalance(@RequestBody request: BalanceRequest): WalletBalanceResponse {
2732

28-
// val CONTRACT_ADDRESS = "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238" // USDC
33+
// val CONTRACT_ADDRESS = "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238" // USDC - ETH
2934
// val CONTRACT_ADDRESS = "0x337610d27c682E347C9cD60BD4b3b107C9d34dDd" // USDT - BNB
3035
// val CONTRACT_ADDRESS = "0xdAC17F958D2ee523a2206206994597C13D831ec7" // USDT - TRX
3136

32-
val blockchain = when (request.blockchainName) {
33-
"ETH" -> "GoerliBlockchain"
34-
"BNB" -> "BinanceTestnetBlockchain"
35-
"TRX" -> "TronBlockchain"
36-
else -> "GoerliBlockchain"
37+
val blockchain = if (appProperties.isProdEnabled == "true") {
38+
when (request.blockchainName) {
39+
"ETH" -> "EthereumBlockchain"
40+
"BNB" -> "BinanceBlockchain"
41+
"TRX" -> "TronBlockchain"
42+
"BTC" -> "BitcoinBlockchain"
43+
else -> "EthereumBlockchain"
44+
}
45+
} else {
46+
when (request.blockchainName) {
47+
"ETH" -> "GoerliBlockchain"
48+
"BNB" -> "BinanceTestnetBlockchain"
49+
"TRX" -> "TronShastaBlockchain"
50+
else -> "GoerliBlockchain"
51+
}
52+
3753
}
3854
val chain = blockchainLookupService.findBlockchain(blockchain)
3955

src/main/kotlin/io/openfuture/state/property/TronProperties.kt

+2-3
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,12 @@ package io.openfuture.state.property
33
import org.springframework.boot.context.properties.ConfigurationProperties
44
import org.springframework.boot.context.properties.ConstructorBinding
55
import org.springframework.validation.annotation.Validated
6-
import javax.validation.constraints.NotBlank
7-
import javax.validation.constraints.NotNull
86

97
@Validated
108
@ConstructorBinding
119
@ConfigurationProperties(prefix = "tron")
1210
data class TronProperties (
1311
val mainnetAddress: String,
14-
val testnetAddress: String
12+
val testnetAddress: String,
13+
val apiKey: String
1514
)

0 commit comments

Comments
 (0)