Updated on 2026-08-14
This commit is contained in:
commit
8164aa0c1a
1109 changed files with 35558 additions and 15187 deletions
|
|
@ -1,37 +1,17 @@
|
|||
package com.tangem.tap.proxy
|
||||
|
||||
import androidx.core.text.isDigitsOnly
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.tangem.Message
|
||||
import com.tangem.blockchain.blockchains.algorand.AlgorandTransactionExtras
|
||||
import com.tangem.blockchain.blockchains.binance.BinanceTransactionExtras
|
||||
import com.tangem.blockchain.blockchains.cosmos.CosmosTransactionExtras
|
||||
import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionExtras
|
||||
import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager
|
||||
import com.tangem.blockchain.blockchains.hedera.HederaTransactionExtras
|
||||
import com.tangem.blockchain.blockchains.optimism.EthereumOptimisticRollupWalletManager
|
||||
import com.tangem.blockchain.blockchains.stellar.StellarMemo
|
||||
import com.tangem.blockchain.blockchains.stellar.StellarTransactionExtras
|
||||
import com.tangem.blockchain.blockchains.ton.TonTransactionExtras
|
||||
import com.tangem.blockchain.blockchains.xrp.XrpTransactionBuilder
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.blockchain.common.transaction.TransactionSendResult
|
||||
import com.tangem.blockchain.extensions.Result
|
||||
import com.tangem.blockchain.externallinkprovider.TxExploreState
|
||||
import com.tangem.blockchain.network.ResultChecker
|
||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.lib.crypto.TransactionManager
|
||||
import com.tangem.lib.crypto.models.*
|
||||
import com.tangem.lib.crypto.models.transactions.SendTxResult
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.domain.TangemSigner
|
||||
import java.math.BigDecimal
|
||||
import java.math.BigInteger
|
||||
import java.math.MathContext
|
||||
|
|
@ -39,84 +19,10 @@ import java.math.RoundingMode
|
|||
|
||||
@Suppress("LargeClass")
|
||||
class TransactionManagerImpl(
|
||||
private val appStateHolder: AppStateHolder,
|
||||
private val cardSdkConfigRepository: CardSdkConfigRepository,
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
private val userWalletsListManager: UserWalletsListManager,
|
||||
) : TransactionManager {
|
||||
|
||||
override suspend fun sendApproveTransaction(
|
||||
txData: ApproveTxData,
|
||||
derivationPath: String?,
|
||||
analyticsData: AnalyticsData,
|
||||
): SendTxResult {
|
||||
val blockchain = requireNotNull(Blockchain.fromNetworkId(txData.networkId)) { "blockchain not found" }
|
||||
val walletManager = getActualWalletManager(blockchain, derivationPath)
|
||||
walletManager.update()
|
||||
val amount = Amount(value = BigDecimal.ZERO, blockchain = blockchain)
|
||||
return sendTransactionInternal(
|
||||
walletManager = walletManager,
|
||||
amount = amount,
|
||||
blockchain = blockchain,
|
||||
feeAmount = txData.feeAmount,
|
||||
gasLimit = txData.gasLimit,
|
||||
destinationAddress = txData.destinationAddress,
|
||||
dataToSign = txData.dataToSign,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun sendTransaction(
|
||||
txData: SwapTxData,
|
||||
isSwap: Boolean,
|
||||
derivationPath: String?,
|
||||
analyticsData: AnalyticsData,
|
||||
): SendTxResult {
|
||||
val blockchain = requireNotNull(Blockchain.fromNetworkId(txData.networkId)) { "blockchain not found" }
|
||||
val walletManager = getActualWalletManager(blockchain, derivationPath)
|
||||
walletManager.update()
|
||||
val amount = if (isSwap) {
|
||||
createAmountForSwap(txData.amountToSend, txData.currencyToSend, blockchain)
|
||||
} else {
|
||||
createAmount(txData.amountToSend, txData.currencyToSend, blockchain)
|
||||
}
|
||||
return sendTransactionInternal(
|
||||
walletManager = walletManager,
|
||||
amount = amount,
|
||||
blockchain = blockchain,
|
||||
feeAmount = txData.feeAmount,
|
||||
gasLimit = txData.gasLimit,
|
||||
destinationAddress = txData.destinationAddress,
|
||||
dataToSign = txData.dataToSign,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
private suspend fun sendTransactionInternal(
|
||||
walletManager: WalletManager,
|
||||
amount: Amount,
|
||||
blockchain: Blockchain,
|
||||
feeAmount: BigDecimal,
|
||||
gasLimit: Int,
|
||||
destinationAddress: String,
|
||||
dataToSign: String,
|
||||
): SendTxResult {
|
||||
val txData = walletManager.createTransaction(
|
||||
amount = amount,
|
||||
fee = Fee.Common(Amount(value = feeAmount, blockchain = blockchain)),
|
||||
destination = destinationAddress,
|
||||
).copy(hash = dataToSign, extras = createExtras(walletManager, gasLimit, dataToSign))
|
||||
|
||||
val signer = transactionSigner(walletManager)
|
||||
|
||||
val sendResult = try {
|
||||
(walletManager as? TransactionSender)?.send(txData, signer) ?: error("Cannot cast to TransactionSender")
|
||||
} catch (ex: Exception) {
|
||||
FirebaseCrashlytics.getInstance().recordException(ex)
|
||||
return SendTxResult.UnknownError(ex)
|
||||
}
|
||||
return handleSendResult(result = sendResult)
|
||||
}
|
||||
|
||||
override fun getExplorerTransactionLink(networkId: String, txAddress: String): String {
|
||||
val blockchain = Blockchain.fromNetworkId(networkId) ?: error("blockchain not found")
|
||||
return when (val txUrlState = blockchain.getExploreTxUrl(txAddress)) {
|
||||
|
|
@ -125,39 +31,11 @@ class TransactionManagerImpl(
|
|||
}
|
||||
}
|
||||
|
||||
override fun getMemoExtras(networkId: String, memo: String?): TransactionExtras? {
|
||||
val blockchain = Blockchain.fromNetworkId(networkId)
|
||||
if (memo == null) return null
|
||||
return when (blockchain) {
|
||||
Blockchain.Stellar -> {
|
||||
val xlmMemo = if (memo.isNotEmpty() && memo.isDigitsOnly()) {
|
||||
StellarMemo.Id(memo.toBigInteger())
|
||||
} else {
|
||||
StellarMemo.Text(memo)
|
||||
}
|
||||
StellarTransactionExtras(xlmMemo)
|
||||
}
|
||||
Blockchain.Binance -> BinanceTransactionExtras(memo)
|
||||
Blockchain.XRP -> memo.toLongOrNull()?.let { XrpTransactionBuilder.XrpTransactionExtras(it) }
|
||||
Blockchain.Cosmos -> CosmosTransactionExtras(memo)
|
||||
Blockchain.TON -> TonTransactionExtras(memo)
|
||||
Blockchain.Hedera -> HederaTransactionExtras(memo)
|
||||
Blockchain.Algorand -> AlgorandTransactionExtras(memo)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun updateWalletManager(networkId: String, derivationPath: String?) {
|
||||
val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" }
|
||||
getActualWalletManager(blockchain, derivationPath).update()
|
||||
}
|
||||
|
||||
override fun calculateFee(networkId: String, gasPrice: String, estimatedGas: Int): BigDecimal {
|
||||
val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" }
|
||||
val gasPriceValue = requireNotNull(gasPrice.toLongOrNull()) { "gasprice should be Long" }
|
||||
return (gasPriceValue * estimatedGas).toBigDecimal().movePointLeft(blockchain.decimals())
|
||||
}
|
||||
|
||||
@Throws(IllegalStateException::class)
|
||||
override suspend fun getFee(
|
||||
networkId: String,
|
||||
|
|
@ -364,60 +242,6 @@ class TransactionManagerImpl(
|
|||
return createMultipleProxyFees(gasPrice, gas, blockchain)
|
||||
}
|
||||
|
||||
private fun handleSendResult(result: Result<TransactionSendResult>): SendTxResult {
|
||||
when (result) {
|
||||
is Result.Success -> {
|
||||
return SendTxResult.Success
|
||||
}
|
||||
is Result.Failure -> {
|
||||
if (ResultChecker.isNetworkError(result)) return SendTxResult.NetworkError(result.error)
|
||||
val error = result.error as? BlockchainSdkError ?: return SendTxResult.UnknownError()
|
||||
when (error) {
|
||||
is BlockchainSdkError.WrappedTangemError -> {
|
||||
val errorByCode = mapErrorByCode(error)
|
||||
if (errorByCode != null) {
|
||||
return errorByCode
|
||||
}
|
||||
val tangemSdkError = error.tangemError as? TangemSdkError ?: return SendTxResult.UnknownError()
|
||||
if (tangemSdkError is TangemSdkError.UserCancelled) return SendTxResult.UserCancelledError
|
||||
return SendTxResult.TangemSdkError(tangemSdkError.code, tangemSdkError.cause)
|
||||
}
|
||||
else -> {
|
||||
return SendTxResult.TangemSdkError(error.code, error.cause)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun mapErrorByCode(error: BlockchainSdkError.WrappedTangemError): SendTxResult? {
|
||||
return when (error.code) {
|
||||
USER_CANCELLED_ERROR_CODE -> {
|
||||
return SendTxResult.UserCancelledError
|
||||
}
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun transactionSigner(walletManager: WalletManager): TransactionSigner {
|
||||
val actualCard = requireNotNull(appStateHolder.getActualCard()) { "no card found" }
|
||||
return TangemSigner(
|
||||
card = actualCard,
|
||||
tangemSdk = cardSdkConfigRepository.sdk,
|
||||
initialMessage = Message(),
|
||||
) { signResponse ->
|
||||
appStateHolder.mainStore?.dispatch(
|
||||
GlobalAction.UpdateWalletSignedHashes(
|
||||
walletSignedHashes = signResponse.totalSignedHashes,
|
||||
walletPublicKey = walletManager.wallet.publicKey.seedKey,
|
||||
remainingSignatures = signResponse.remainingSignatures,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getActualWalletManager(blockchain: Blockchain, derivationPath: String?): WalletManager {
|
||||
val selectedUserWallet = requireNotNull(
|
||||
userWalletsListManager.selectedUserWalletSync,
|
||||
|
|
@ -431,24 +255,6 @@ class TransactionManagerImpl(
|
|||
return requireNotNull(walletManager) { "no wallet manager found" }
|
||||
}
|
||||
|
||||
private fun createExtras(
|
||||
walletManager: WalletManager,
|
||||
gasLimit: Int,
|
||||
transactionHash: String,
|
||||
): TransactionExtras? {
|
||||
return when (walletManager) {
|
||||
is EthereumWalletManager -> {
|
||||
return EthereumTransactionExtras(
|
||||
data = transactionHash.removePrefix(HEX_PREFIX).hexToBytes(),
|
||||
gasLimit = gasLimit.toBigInteger(),
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create proxy fees
|
||||
*
|
||||
|
|
@ -502,46 +308,6 @@ class TransactionManagerImpl(
|
|||
)
|
||||
}
|
||||
|
||||
private fun createAmount(amount: BigDecimal, currency: Currency, blockchain: Blockchain): Amount {
|
||||
return when (currency) {
|
||||
is Currency.NativeToken -> {
|
||||
Amount(value = amount, blockchain = blockchain)
|
||||
}
|
||||
is Currency.NonNativeToken -> {
|
||||
Amount(convertNonNativeToken(currency), amount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createAmountForSwap(amount: BigDecimal, currency: Currency?, blockchain: Blockchain): Amount {
|
||||
return when (currency) {
|
||||
is Currency.NativeToken,
|
||||
null,
|
||||
-> {
|
||||
Amount(value = amount, blockchain = blockchain)
|
||||
}
|
||||
is Currency.NonNativeToken -> {
|
||||
// 1. when creates swap amount for NonNativeToken, amount should be ZERO
|
||||
// 2. Amount has .Coin type, as workaround to use destinationAddress in bsdk, not contractAddress
|
||||
Amount(
|
||||
currencySymbol = currency.symbol,
|
||||
value = BigDecimal.ZERO,
|
||||
decimals = currency.decimalCount,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun convertNonNativeToken(token: Currency.NonNativeToken): Token {
|
||||
return Token(
|
||||
name = token.name,
|
||||
symbol = token.symbol,
|
||||
contractAddress = token.contractAddress,
|
||||
decimals = token.decimalCount,
|
||||
id = token.id,
|
||||
)
|
||||
}
|
||||
|
||||
private fun convertToProxyAmount(amount: Amount): ProxyAmount {
|
||||
return ProxyAmount(
|
||||
currencySymbol = amount.currencySymbol,
|
||||
|
|
@ -565,8 +331,6 @@ class TransactionManagerImpl(
|
|||
}
|
||||
|
||||
companion object {
|
||||
private const val HEX_PREFIX = "0x"
|
||||
private const val USER_CANCELLED_ERROR_CODE = 50002
|
||||
private const val MULTIPLIER_GAS_PRICE_FOR_NORMAL_FEE = 150 // 50%
|
||||
private const val MULTIPLIER_GAS_PRICE_FOR_PRIORITY_FEE = 200 // 50%
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue