Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge 5.0 to develop #357

Merged
merged 31 commits into from
Nov 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
1963f6b
AND-4566 AND-4456 Made successful near transaction.
Sateetas Oct 5, 2023
2a4d4a9
AND-4566 Added recent transaction support.
Sateetas Oct 11, 2023
0237e61
AND-4566 Renamed model.
Sateetas Oct 16, 2023
140fdf0
AND-4917 exception cached
kozarezvlad Oct 17, 2023
0c0c149
Merge pull request #341 from tangem/bugfix/AND-4917-cache-exception-o…
kozarezvlad Oct 17, 2023
aff1ca0
AND-4566 Updated state.
Sateetas Oct 19, 2023
291cce3
AND-4566 Made changes according to review.
Sateetas Oct 19, 2023
1eb9b8a
Merge pull request #340 from tangem/feature/AND-4566_near_part2
Sateetas Oct 19, 2023
d917915
AND-4969 Fixed NEAR fee calculation.
Sateetas Oct 19, 2023
b008485
AND-4924 changed api order
kozarezvlad Oct 17, 2023
29271d1
Merge pull request #342 from tangem/feature/AND-4969_fix_near_fee_cal…
Sateetas Oct 20, 2023
5bf903b
AND-4997 Fixed NEAR network layer.
Sateetas Oct 23, 2023
bda1f38
AND-4997 Fixed providers order.
Sateetas Oct 23, 2023
09472aa
Merge pull request #344 from tangem/AND-4997_near_network_providers
Sateetas Oct 24, 2023
3cd6f69
AND-4636 Finalized transaction types
Yoggam1 Oct 25, 2023
df13a86
Merge pull request #346 from tangem/feature/AND-4636_finalize_transac…
Yoggam1 Oct 25, 2023
55665d2
AND-5037 Fixed destination and source address parsing for BTC transac…
Yoggam1 Oct 27, 2023
a416648
AND-5056 Removed Create_Account action.
Sateetas Oct 27, 2023
d72a4b6
AND-5056 Removed unused stuff.
Sateetas Oct 27, 2023
3c6d239
Merge pull request #347 from tangem/bugfix/AND-5037_unknown_address_fix
Yoggam1 Oct 30, 2023
b5d52d9
AND-5097 Fixed explorer links
Yoggam1 Oct 24, 2023
8e0b869
Merge pull request #348 from tangem/bugfix/AND-5097_fix_explorer_links
Yoggam1 Oct 30, 2023
fc0deae
AND-5056 Added deposit.
Sateetas Oct 30, 2023
4eb157f
AND-5031 checking empty txs
kozarezvlad Oct 30, 2023
3f0acea
AND-5097 Fixed Ethereum like blockchains hash parsing
Yoggam1 Oct 31, 2023
e90de30
Merge pull request #351 from tangem/bugfix/AND-5097_ethereum_like_has…
Yoggam1 Oct 31, 2023
435b333
AND-5102 Fixed tx history loading
Yoggam1 Oct 31, 2023
13916ab
AND-5122 Fixed tx history loading for ERC20 tokens
Yoggam1 Oct 31, 2023
bfd2e02
AND-5147 changed api order for ETC
kozarezvlad Nov 1, 2023
95605e9
Merge pull request #356 from tangem/feature/AND-5056_finalize_near
kozarezvlad Nov 2, 2023
202669a
Merge branch 'develop' into merge_5.0_to_develop
kozarezvlad Nov 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.PaginationWrapper
import com.tangem.blockchain.common.toBlockchainSdkError
import com.tangem.blockchain.common.txhistory.TransactionHistoryItem
import com.tangem.blockchain.common.txhistory.TransactionHistoryItem.TransactionDirection
import com.tangem.blockchain.common.txhistory.TransactionHistoryItem.TransactionStatus
import com.tangem.blockchain.common.txhistory.TransactionHistoryItem.TransactionType
import com.tangem.blockchain.common.txhistory.TransactionHistoryProvider
Expand All @@ -25,11 +24,21 @@ internal class BitcoinTransactionHistoryProvider(
private val blockBookApi: BlockBookApi,
) : TransactionHistoryProvider {

override suspend fun getTransactionHistoryState(address: String): TransactionHistoryState {
override suspend fun getTransactionHistoryState(
address: String,
filterType: TransactionHistoryRequest.FilterType,
): TransactionHistoryState {
return try {
val addressResponse = withContext(Dispatchers.IO) { blockBookApi.getAddress(address) }
if (addressResponse.txs > 0) {
TransactionHistoryState.Success.HasTransactions(addressResponse.txs)
val response = withContext(Dispatchers.IO) {
blockBookApi.getTransactions(
address = address,
page = 1,
pageSize = 1, // We don't need to know all transactions to define state
filterType = filterType,
)
}
if (!response.transactions.isNullOrEmpty()) {
TransactionHistoryState.Success.HasTransactions(response.txs)
} else {
TransactionHistoryState.Success.Empty
}
Expand All @@ -55,9 +64,9 @@ internal class BitcoinTransactionHistoryProvider(
?: emptyList()
Result.Success(
PaginationWrapper(
page = response.page,
totalPages = response.totalPages,
itemsOnPage = response.itemsOnPage,
page = response.page ?: request.page.number,
totalPages = response.totalPages ?: 0,
itemsOnPage = response.itemsOnPage ?: 0,
items = txs
)
)
Expand All @@ -67,81 +76,86 @@ internal class BitcoinTransactionHistoryProvider(
}

private fun GetAddressResponse.Transaction.toTransactionHistoryItem(walletAddress: String): TransactionHistoryItem {
val isOutgoing = vin.any { it.addresses.contains(walletAddress) }
val isOutgoing = vin.any { it.addresses?.contains(walletAddress) == true }
return TransactionHistoryItem(
txHash = txid,
timestamp = TimeUnit.SECONDS.toMillis(blockTime.toLong()),
direction = extractTransactionDirection(
isIncoming = !isOutgoing,
tx = this,
walletAddress = walletAddress
),
isOutgoing = isOutgoing,
destinationType = extractDestinationType(tx = this, walletAddress = walletAddress),
sourceType = sourceType(tx = this, walletAddress = walletAddress),
status = if (confirmations > 0) TransactionStatus.Confirmed else TransactionStatus.Unconfirmed,
type = TransactionType.Transfer,
amount = extractAmount(
isIncoming = !isOutgoing,
isOutgoing = isOutgoing,
tx = this,
walletAddress = walletAddress,
blockchain = blockchain,
)
)
}

private fun extractTransactionDirection(
isIncoming: Boolean,
private fun extractDestinationType(
tx: GetAddressResponse.Transaction,
walletAddress: String,
): TransactionDirection {
val address: TransactionHistoryItem.Address = if (isIncoming) {
val inputsWithOtherAddresses = tx.vin
.filter { !it.addresses.contains(walletAddress) }
.flatMap { it.addresses }
.toSet()
when {
inputsWithOtherAddresses.isEmpty() -> TransactionHistoryItem.Address.Single(rawAddress = walletAddress)
inputsWithOtherAddresses.size == 1 -> TransactionHistoryItem.Address.Single(
rawAddress = inputsWithOtherAddresses.first()
)
else -> TransactionHistoryItem.Address.Multiple
}
} else {
val outputsWithOtherAddresses = tx.vout
.filter { !it.addresses.contains(walletAddress) }
.flatMap { it.addresses }
.toSet()
when {
outputsWithOtherAddresses.isEmpty() -> TransactionHistoryItem.Address.Single(rawAddress = walletAddress)
outputsWithOtherAddresses.size == 1 -> TransactionHistoryItem.Address.Single(
rawAddress = outputsWithOtherAddresses.first()
)
else -> TransactionHistoryItem.Address.Multiple
}
): TransactionHistoryItem.DestinationType {
val outputsWithOtherAddresses = tx.vout
.filter { it.addresses?.contains(walletAddress) == false }
.mapNotNull { it.addresses }
.flatten()
.toSet()
return when {
outputsWithOtherAddresses.isEmpty() -> TransactionHistoryItem.DestinationType.Single(
TransactionHistoryItem.AddressType.User(walletAddress)
)

outputsWithOtherAddresses.size == 1 -> TransactionHistoryItem.DestinationType.Single(
TransactionHistoryItem.AddressType.User(outputsWithOtherAddresses.first())
)

else -> TransactionHistoryItem.DestinationType.Multiple(
outputsWithOtherAddresses.map { TransactionHistoryItem.AddressType.User(it) }
)
}
}

private fun sourceType(
tx: GetAddressResponse.Transaction,
walletAddress: String,
): TransactionHistoryItem.SourceType {
val inputsWithOtherAddresses = tx.vin
.filter { it.addresses?.contains(walletAddress) == false }
.mapNotNull { it.addresses }
.flatten()
.toSet()
return when {
inputsWithOtherAddresses.isEmpty() -> TransactionHistoryItem.SourceType.Single(walletAddress)
inputsWithOtherAddresses.size == 1 -> TransactionHistoryItem.SourceType.Single(inputsWithOtherAddresses.first())
else -> TransactionHistoryItem.SourceType.Multiple(inputsWithOtherAddresses.toList())
}
return if (isIncoming) TransactionDirection.Incoming(address) else TransactionDirection.Outgoing(address)
}

private fun extractAmount(
isIncoming: Boolean,
isOutgoing: Boolean,
tx: GetAddressResponse.Transaction,
walletAddress: String,
blockchain: Blockchain,
): Amount {
return try {
val amount = if (isIncoming) {
val outputs = tx.vout
.find { it.addresses.contains(walletAddress) }
?.value.toBigDecimalOrDefault()
val inputs = tx.vin
.find { it.addresses.contains(walletAddress) }
?.value.toBigDecimalOrDefault()
outputs - inputs
} else {
val amount = if (isOutgoing) {
val outputs = tx.vout
.filter { !it.addresses.contains(walletAddress) }
.filter { it.addresses?.contains(walletAddress) == false }
.mapNotNull { it.value?.toBigDecimalOrNull() }
.sumOf { it }
val fee = tx.fees.toBigDecimalOrDefault()
outputs + fee
} else {
val outputs = tx.vout
.find { it.addresses?.contains(walletAddress) == true}
?.value.toBigDecimalOrDefault()
val inputs = tx.vin
.find { it.addresses?.contains(walletAddress) == true }
?.value.toBigDecimalOrDefault()
outputs - inputs
}

Amount(value = amount.movePointLeft(blockchain.decimals()), blockchain = blockchain)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ open class BitcoinWalletManager(
balanceDif = it.value.sumOf { transaction -> transaction.balanceDif },
hash = it.value[0].hash,
date = it.value[0].date,
isConfirmed = it.value[0].isConfirmed
isConfirmed = it.value[0].isConfirmed,
destination = it.value[0].destination,
source = it.value[0].source,
)
}
return BitcoinAddressInfo(balance, unspentOutputs, finalTransactions, hasUnconfirmed)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,6 @@ class BlockchainInfoNetworkProvider() : BitcoinNetworkProvider {
val addressData = addressDeferred.await()
val unspents = unspentsDeferred.await()

val transactions = addressData.transactions!!.map {
BasicTransactionData(
balanceDif = it.balanceDif!!.toBigDecimal().movePointLeft(decimals),
hash = it.hash!!,
isConfirmed = it.blockHeight != 0L,
date = Calendar.getInstance().apply { this.timeInMillis = it.time!! * 1000 }
)
}

val unspentOutputs = unspents.unspentOutputs!!.map {
BitcoinUnspentOutput(
amount = it.amount!!.toBigDecimal().movePointLeft(decimals),
Expand All @@ -60,7 +51,7 @@ class BlockchainInfoNetworkProvider() : BitcoinNetworkProvider {
balance = addressData.finalBalance?.toBigDecimal()?.movePointLeft(decimals)
?: 0.toBigDecimal(),
unspentOutputs = unspentOutputs,
recentTransactions = transactions
recentTransactions = addressData.transactions.toRecentTransactions(walletAddress = address),
))
}
} catch (exception: Exception) {
Expand Down Expand Up @@ -123,6 +114,42 @@ class BlockchainInfoNetworkProvider() : BitcoinNetworkProvider {
}
}

private fun List<BlockchainInfoTransaction>?.toRecentTransactions(walletAddress: String): List<BasicTransactionData> {
return this?.map { it.toBasicTransactionData(walletAddress) } ?: emptyList()
}

private fun BlockchainInfoTransaction.toBasicTransactionData(walletAddress: String): BasicTransactionData {
val balanceDiff = balanceDif ?: 0
val isIncoming = balanceDiff > 0
val date = Calendar.getInstance().also { calendar ->
if (time != null) calendar.timeInMillis = time * 100
}
var source = "unknown"
var destination = "unknown"
if (isIncoming) {
inputs
.firstOrNull { it.previousOutput?.address != walletAddress }
?.previousOutput
?.address
?.let { source = it }
destination = walletAddress
} else {
source = walletAddress
outputs
.firstOrNull { it.address != walletAddress }
?.address
?.let { destination = it }
}
return BasicTransactionData(
balanceDif = balanceDiff.toBigDecimal().movePointLeft(decimals),
hash = hash.orEmpty(),
date = date,
isConfirmed = blockHeight != 0L,
destination = destination,
source = source,
)
}

private suspend fun getRemainingTransactions(
address: String,
transactionsTotal: Int,
Expand Down Expand Up @@ -154,4 +181,4 @@ class BlockchainInfoNetworkProvider() : BitcoinNetworkProvider {
Result.Failure(exception.toBlockchainSdkError())
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ data class BlockchainInfoAddress(
@Json(name = "n_tx")
val transactionCount: Int? = null,

@Json(name = "address")
val address: String? = null,

@Json(name = "txs")
val transactions: List<BlockchainInfoTransaction>? = null
val transactions: List<BlockchainInfoTransaction>? = null,
)

@JsonClass(generateAdapter = true)
Expand All @@ -28,7 +31,13 @@ data class BlockchainInfoTransaction(
@Json(name = "vin_sz")
val inputCount: Int? = null,

val time: Long? = null
val time: Long? = null,

@Json(name = "inputs")
val inputs: List<BlockchainInfoInput> = emptyList(),

@Json(name = "outputs")
val outputs: List<BlockchainInfoOutput> = emptyList(),
)

@JsonClass(generateAdapter = true)
Expand All @@ -49,7 +58,7 @@ data class BlockchainInfoUtxo(
val amount: Long? = null,

@Json(name = "script")
val outputScript: String? = null
val outputScript: String? = null,
)

@JsonClass(generateAdapter = true)
Expand All @@ -58,5 +67,47 @@ data class BlockchainInfoFees(
val regularFeePerByte: Int? = null,

@Json(name = "priority")
val priorityFeePerByte: Int? = null
)
val priorityFeePerByte: Int? = null,
)

@JsonClass(generateAdapter = true)
data class BlockchainInfoInput(
@Json(name = "sequence")
val sequence: Int?,

@Json(name = "witness")
val witness: String?,

@Json(name = "script")
val script: String?,

@Json(name = "n")
val index: Int?,

@Json(name = "prev_out")
val previousOutput: BlockchainInfoOutput?,
)

@JsonClass(generateAdapter = true)
data class BlockchainInfoOutput(
@Json(name = "type")
val type: Int?,

@Json(name = "spent")
val spent: Boolean?,

@Json(name = "value")
val value: Long?,

@Json(name = "script")
val script: String?,

@Json(name = "addr")
val address: String?,

@Json(name = "n")
val index: Int?,

@Json(name = "tx_index")
val txIndex: Long?,
)
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ internal fun Blockchain.getEthereumJsonRpcProviders(
val providers = when (this) {
Blockchain.Arbitrum -> listOfNotNull(
EthereumJsonRpcProvider(baseUrl = "https://arb1.arbitrum.io/rpc/"),
getNowNodesProvider(baseUrl = "https://arbitrum.nownodes.io/", config = config),
getInfuraProvider(baseUrl = "https://arbitrum-mainnet.infura.io/v3/", config = config)
)
Blockchain.ArbitrumTestnet -> listOf(
Expand Down Expand Up @@ -46,18 +47,18 @@ internal fun Blockchain.getEthereumJsonRpcProviders(
)
)
Blockchain.Ethereum -> listOfNotNull(
getInfuraProvider(baseUrl = "https://mainnet.infura.io/v3/", config = config),
getNowNodesProvider(baseUrl = "https://eth.nownodes.io/", config = config),
getGetBlockProvider(baseUrl = "https://eth.getblock.io/mainnet/", config = config)
getGetBlockProvider(baseUrl = "https://eth.getblock.io/mainnet/", config = config),
getInfuraProvider(baseUrl = "https://mainnet.infura.io/v3/", config = config),
)
Blockchain.EthereumTestnet -> listOfNotNull(
getNowNodesProvider(baseUrl = "https://eth-goerli.nownodes.io/", config = config),
getInfuraProvider(baseUrl = "https://goerli.infura.io/v3/", config = config)
)
Blockchain.EthereumClassic -> listOfNotNull(
EthereumJsonRpcProvider(baseUrl = "https://etc.etcdesktop.com/"),
getGetBlockProvider(baseUrl = "https://etc.getblock.io/mainnet/", config = config),
EthereumJsonRpcProvider(baseUrl = "https://www.ethercluster.com/etc/"),
EthereumJsonRpcProvider(baseUrl = "https://etc.etcdesktop.com/"),
EthereumJsonRpcProvider(baseUrl = "https://blockscout.com/etc/mainnet/api/eth-rpc/"),
EthereumJsonRpcProvider(baseUrl = "https://etc.mytokenpocket.vip/"),
EthereumJsonRpcProvider(baseUrl = "https://besu-de.etc-network.info/"),
Expand Down
Loading