Skip to content

Commit f7c42ba

Browse files
authored
Merge pull request #20 from horizontalsystems/sync-all
Improve syncing logic for ethereum and erc20 tokens
2 parents 78474b8 + 7669806 commit f7c42ba

File tree

1 file changed

+105
-95
lines changed
  • ethereumkit/src/main/java/io/horizontalsystems/ethereumkit

1 file changed

+105
-95
lines changed

ethereumkit/src/main/java/io/horizontalsystems/ethereumkit/EthereumKit.kt

Lines changed: 105 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@ import io.horizontalsystems.ethereumkit.models.Balance
77
import io.horizontalsystems.ethereumkit.models.GasPrice
88
import io.horizontalsystems.ethereumkit.models.LastBlockHeight
99
import io.horizontalsystems.ethereumkit.models.Transaction
10+
import io.horizontalsystems.ethereumkit.models.etherscan.EtherscanTransaction
1011
import io.horizontalsystems.ethereumkit.network.EtherscanService
1112
import io.horizontalsystems.hdwalletkit.HDWallet
1213
import io.horizontalsystems.hdwalletkit.Mnemonic
1314
import io.reactivex.Flowable
1415
import io.reactivex.Single
1516
import io.reactivex.android.schedulers.AndroidSchedulers
1617
import io.reactivex.disposables.CompositeDisposable
17-
import io.reactivex.functions.Function5
18+
import io.reactivex.functions.Function3
1819
import io.reactivex.schedulers.Schedulers
1920
import io.realm.OrderedCollectionChangeSet
2021
import io.realm.Realm
@@ -29,6 +30,7 @@ import org.web3j.abi.datatypes.Type
2930
import org.web3j.abi.datatypes.generated.Uint256
3031
import org.web3j.crypto.RawTransaction
3132
import org.web3j.crypto.TransactionEncoder
33+
import org.web3j.tuples.generated.Tuple3
3234
import org.web3j.utils.Convert
3335
import org.web3j.utils.Numeric
3436
import java.math.BigDecimal
@@ -105,7 +107,6 @@ class EthereumKit(seed: ByteArray, networkType: NetworkType, walletId: String) {
105107
timer = Timer(30, object : Timer.Listener {
106108
override fun onTimeIsUp() {
107109
refresh()
108-
erc20List.forEach { refresh(it.key) }
109110
}
110111
})
111112
}
@@ -138,18 +139,29 @@ class EthereumKit(seed: ByteArray, networkType: NetworkType, walletId: String) {
138139

139140
@Synchronized
140141
fun refresh() {
142+
val tokenList = erc20List.values
143+
if (kitState is KitState.Syncing) {
144+
return
145+
}
146+
147+
tokenList.find { it.kitState is KitState.Syncing }?.let {
148+
return
149+
}
150+
141151
kitState = KitState.Syncing(0.0)
152+
tokenList.forEach { it.kitState = KitState.Syncing(0.0) }
142153

143-
Flowable.zip(updateBalance(), updateLastBlockHeight(), updateTransactions(), updateTransactions(token = true), updateGasPrice(),
144-
Function5<BigDecimal, Int, Int, Int, Double, Unit> { _, _, _, _, _ ->
145-
Unit
146-
})
147-
.subscribeOn(io.reactivex.schedulers.Schedulers.io())
154+
Flowable.zip(web3j.getBlockNumber(), web3j.getGasPrice(), web3j.getBalance(receiveAddress), Function3 { h: Int, g: Double, b: BigDecimal -> Tuple3(h, g, b) })
155+
.subscribeOn(Schedulers.io())
148156
.subscribe({
149-
kitState = KitState.Synced
157+
updateLastBlockHeight(it.value1)
158+
updateGasPrice(it.value2)
159+
updateBalance(it.value3, receiveAddress, ETH_DECIMAL)
160+
161+
refreshTransactions()
150162
}, {
151-
it?.printStackTrace()
152163
kitState = KitState.NotSynced
164+
tokenList.forEach { it.kitState = KitState.NotSynced }
153165
}).let {
154166
disposables.add(it)
155167
}
@@ -203,31 +215,13 @@ class EthereumKit(seed: ByteArray, networkType: NetworkType, walletId: String) {
203215
holder.balance = it.balance.toBigDecimal()
204216
}
205217

206-
refresh(token.contractAddress)
218+
refresh()
207219
}
208220

209221
fun unregister(contractAddress: String) {
210222
erc20List.remove(contractAddress)
211223
}
212224

213-
@Synchronized
214-
fun refresh(contractAddress: String) {
215-
val erc20 = erc20List[contractAddress] ?: return
216-
217-
erc20.kitState = KitState.Syncing(0.0)
218-
219-
updateBalance(contractAddress, erc20.listener.decimal)
220-
.subscribeOn(io.reactivex.schedulers.Schedulers.io())
221-
.subscribe({
222-
erc20.kitState = KitState.Synced
223-
}, {
224-
it?.printStackTrace()
225-
erc20.kitState = KitState.NotSynced
226-
}).let {
227-
disposables.add(it)
228-
}
229-
}
230-
231225
fun feeERC20(): Double {
232226
realmFactory.realm.use { realm ->
233227
val gwei = realm.where(GasPrice::class.java).findFirst()?.gasPriceInGwei
@@ -396,92 +390,108 @@ class EthereumKit(seed: ByteArray, networkType: NetworkType, walletId: String) {
396390
}
397391
}
398392

399-
private fun updateGasPrice(): Flowable<Double> {
400-
return web3j.getGasPrice()
401-
.map { gasPrice ->
402-
realmFactory.realm.use { realm ->
403-
realm.executeTransaction {
404-
it.insertOrUpdate(GasPrice(gasPrice))
405-
}
406-
}
407-
gasPrice
408-
}
409-
.onErrorReturn {
410-
DEFAULT_GAS_PRICE
411-
}
412-
}
393+
private fun getBlockHeight(token: Boolean = false): Int {
394+
realmFactory.realm.use {
395+
val query = it.where(Transaction::class.java)
396+
if (token) {
397+
query.notEqualTo("contractAddress", "")
398+
} else {
399+
query.equalTo("contractAddress", "")
400+
}
413401

414-
private fun updateBalance(contractAddress: String? = null, decimal: Int = ETH_DECIMAL): Flowable<BigDecimal> {
415-
val flowable = if (contractAddress == null) {
416-
web3j.getBalance(receiveAddress)
417-
} else {
418-
web3j.getTokenBalance(receiveAddress, contractAddress, decimal)
402+
return query.sort("blockNumber", Sort.DESCENDING).findFirst()?.blockNumber?.toInt() ?: 0
419403
}
404+
}
420405

421-
return flowable.map { balance ->
422-
realmFactory.realm.use { realm ->
423-
realm.executeTransaction {
424-
it.insertOrUpdate(Balance(contractAddress ?: receiveAddress, balance, decimal))
425-
}
426-
}
406+
private fun refreshTransactions() {
407+
var lastBlockHeight = getBlockHeight()
408+
409+
etherscanService.getTransactionList(receiveAddress, lastBlockHeight + 1)
410+
.subscribeOn(io.reactivex.schedulers.Schedulers.io())
411+
.subscribe({
412+
saveTransactions(it.result)
413+
kitState = KitState.Synced
414+
}, {
415+
kitState = KitState.NotSynced
416+
})
417+
.let { disposables.add(it) }
418+
419+
if (erc20List.isEmpty())
420+
return
421+
422+
lastBlockHeight = getBlockHeight(token = true)
423+
etherscanService.getTokenTransactions(receiveAddress, lastBlockHeight + 1)
424+
.subscribeOn(io.reactivex.schedulers.Schedulers.io())
425+
.subscribe({
426+
saveTransactions(it.result)
427+
refreshTokensBalances()
428+
}, {
429+
erc20List.values.forEach { it.kitState = KitState.NotSynced }
430+
})
431+
.let { disposables.add(it) }
427432

428-
balance
429-
}
430433
}
431434

432-
private fun updateLastBlockHeight(): Flowable<Int> {
433-
return web3j.getBlockNumber()
434-
.map { height ->
435-
lastBlockHeight = height
436-
realmFactory.realm.use { realm ->
437-
realm.executeTransaction {
438-
it.insertOrUpdate(LastBlockHeight(height))
439-
}
440-
}
435+
private fun refreshTokensBalances() {
436+
erc20List.values.forEach { holder ->
437+
val erc20Address = holder.listener.contractAddress
438+
val erc20Decimal = holder.listener.decimal
439+
440+
web3j.getTokenBalance(receiveAddress, erc20Address, erc20Decimal)
441+
.subscribeOn(io.reactivex.schedulers.Schedulers.io())
442+
.subscribe({
443+
updateBalance(it, erc20Address, erc20Decimal)
444+
holder.kitState = KitState.Synced
445+
}, {
446+
holder.kitState = KitState.NotSynced
447+
})
448+
.let { disposables.add(it) }
449+
}
450+
}
441451

442-
listener?.onLastBlockHeightUpdate(height)
452+
//
453+
// InsertOrUpdate records
454+
//
443455

444-
erc20List.forEach {
445-
it.value.listener.onLastBlockHeightUpdate(height)
446-
}
456+
private fun updateGasPrice(gasPrice: Double) {
457+
realmFactory.realm.use { realm ->
458+
realm.executeTransaction {
459+
it.insertOrUpdate(GasPrice(gasPrice))
460+
}
461+
}
462+
}
447463

448-
height
449-
}
464+
private fun updateBalance(balance: BigDecimal, address: String, decimal: Int) {
465+
realmFactory.realm.use { realm ->
466+
realm.executeTransaction {
467+
it.insertOrUpdate(Balance(address, balance, decimal))
468+
}
469+
}
450470
}
451471

452-
private fun updateTransactions(token: Boolean = false): Flowable<Int> {
472+
private fun updateLastBlockHeight(height: Int) {
473+
lastBlockHeight = height
453474

454-
val lastBlockHeight = realmFactory.realm.use {
455-
var query = it.where(Transaction::class.java)
456-
query = if (token) {
457-
query.notEqualTo("contractAddress", "")
458-
.notEqualTo("input", "0x")
459-
} else {
460-
query.equalTo("contractAddress", "")
461-
.equalTo("input", "0x")
475+
realmFactory.realm.use { realm ->
476+
realm.executeTransaction {
477+
it.insertOrUpdate(LastBlockHeight(height))
462478
}
463-
query.sort("blockNumber", Sort.DESCENDING)
464-
.findFirst()?.blockNumber?.toInt() ?: 0
465479
}
466480

467-
val flowable = if (token) {
468-
etherscanService.getTokenTransactions(receiveAddress, lastBlockHeight + 1)
469-
} else {
470-
etherscanService.getTransactionList(receiveAddress, lastBlockHeight + 1)
481+
listener?.onLastBlockHeightUpdate(height)
482+
483+
erc20List.forEach {
484+
it.value.listener.onLastBlockHeightUpdate(height)
471485
}
486+
}
472487

473-
return flowable.map { response ->
474-
realmFactory.realm.use { realm ->
475-
realm.executeTransaction {
476-
response.result
477-
.map { tx -> Transaction(tx) }
478-
.forEach { tx ->
479-
realm.insertOrUpdate(tx)
480-
}
488+
private fun saveTransactions(list: List<EtherscanTransaction>) {
489+
realmFactory.realm.use { realm ->
490+
realm.executeTransaction {
491+
list.map { tx -> Transaction(tx) }.forEach { tx ->
492+
realm.insertOrUpdate(tx)
481493
}
482494
}
483-
484-
response.result.size
485495
}
486496
}
487497

0 commit comments

Comments
 (0)