202 lines
No EOL
7.5 KiB
Kotlin
202 lines
No EOL
7.5 KiB
Kotlin
package com.tangem.tap.proxy
|
|
|
|
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
|
import com.tangem.Message
|
|
import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionExtras
|
|
import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager
|
|
import com.tangem.blockchain.common.Amount
|
|
import com.tangem.blockchain.common.Blockchain
|
|
import com.tangem.blockchain.common.BlockchainSdkError
|
|
import com.tangem.blockchain.common.Token
|
|
import com.tangem.blockchain.common.TransactionExtras
|
|
import com.tangem.blockchain.common.TransactionSender
|
|
import com.tangem.blockchain.common.TransactionSigner
|
|
import com.tangem.blockchain.common.WalletManager
|
|
import com.tangem.blockchain.extensions.Result
|
|
import com.tangem.blockchain.extensions.SimpleResult
|
|
import com.tangem.common.core.TangemSdkError
|
|
import com.tangem.common.extensions.hexToBytes
|
|
import com.tangem.domain.common.extensions.fromNetworkId
|
|
import com.tangem.lib.crypto.TransactionManager
|
|
import com.tangem.lib.crypto.models.Currency
|
|
import com.tangem.lib.crypto.models.ProxyAmount
|
|
import com.tangem.lib.crypto.models.transactions.SendTxResult
|
|
import com.tangem.tap.common.redux.global.GlobalAction
|
|
import com.tangem.tap.domain.TangemSigner
|
|
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
|
|
import com.tangem.tap.store
|
|
import com.tangem.tap.tangemSdk
|
|
import java.math.BigDecimal
|
|
import java.math.BigInteger
|
|
|
|
class TransactionManagerImpl(
|
|
private val appStateHolder: AppStateHolder,
|
|
) : TransactionManager {
|
|
|
|
@Throws(IllegalStateException::class)
|
|
override suspend fun sendTransaction(
|
|
networkId: String,
|
|
amountToSend: BigDecimal,
|
|
currencyToSend: Currency,
|
|
feeAmount: BigDecimal,
|
|
destinationAddress: String,
|
|
dataToSign: String,
|
|
): SendTxResult {
|
|
val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" }
|
|
val walletManager = getActualWalletManager(blockchain)
|
|
val amount = createAmount(amountToSend, currencyToSend, blockchain)
|
|
|
|
val txData = walletManager.createTransaction(
|
|
amount = amount,
|
|
fee = Amount(value = feeAmount, blockchain = blockchain),
|
|
destination = destinationAddress,
|
|
).copy(hash = dataToSign, extras = createExtras(walletManager, feeAmount, dataToSign))
|
|
|
|
val signer = transactionSigner(walletManager)
|
|
|
|
val sendResult = try {
|
|
(walletManager as TransactionSender).send(txData, signer)
|
|
} catch (ex: Exception) {
|
|
FirebaseCrashlytics.getInstance().recordException(ex)
|
|
return SendTxResult.UnknownError(ex)
|
|
}
|
|
return handleSendResult(sendResult)
|
|
}
|
|
|
|
@Throws(IllegalStateException::class)
|
|
override suspend fun getFee(
|
|
networkId: String,
|
|
amountToSend: BigDecimal,
|
|
currencyToSend: Currency,
|
|
destinationAddress: String,
|
|
): ProxyAmount {
|
|
val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" }
|
|
val walletManager = getActualWalletManager(blockchain)
|
|
val fee = (walletManager as TransactionSender).getFee(
|
|
amount = createAmount(amountToSend, currencyToSend, blockchain),
|
|
destination = destinationAddress,
|
|
)
|
|
when (fee) {
|
|
is Result.Success -> {
|
|
return convertToProxyAmount(fee.data.firstOrNull() ?: error("no fee found"))
|
|
}
|
|
is Result.Failure -> {
|
|
error(fee.error.message ?: fee.error.customMessage)
|
|
}
|
|
}
|
|
}
|
|
|
|
override fun getNativeAddress(networkId: String): String {
|
|
return "" //todo implement
|
|
}
|
|
|
|
private fun handleSendResult(result: SimpleResult): SendTxResult {
|
|
when (result) {
|
|
is SimpleResult.Success -> {
|
|
return SendTxResult.Success
|
|
}
|
|
is SimpleResult.Failure -> {
|
|
val error = (result.error as? BlockchainSdkError) ?: return SendTxResult.UnknownError()
|
|
when (error) {
|
|
is BlockchainSdkError.WrappedTangemError -> {
|
|
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 transactionSigner(walletManager: WalletManager): TransactionSigner {
|
|
val actualCard = requireNotNull(appStateHolder.getActualCard()) { "no card found" }
|
|
return TangemSigner(
|
|
card = actualCard,
|
|
tangemSdk = tangemSdk,
|
|
initialMessage = Message("test transaction title", "test description"),
|
|
) { signResponse ->
|
|
store.dispatch(
|
|
GlobalAction.UpdateWalletSignedHashes(
|
|
walletSignedHashes = signResponse.totalSignedHashes,
|
|
walletPublicKey = walletManager.wallet.publicKey.seedKey,
|
|
remainingSignatures = signResponse.remainingSignatures,
|
|
),
|
|
)
|
|
}
|
|
}
|
|
|
|
private fun getActualWalletManager(blockchain: Blockchain): WalletManager {
|
|
val card = appStateHolder.getActualCard()
|
|
if (card != null) {
|
|
val blockchainNetwork = BlockchainNetwork(blockchain, card)
|
|
val walletManager = appStateHolder.walletState?.getWalletManager(blockchainNetwork)
|
|
if (walletManager != null) {
|
|
return walletManager
|
|
} else {
|
|
error("no wallet manager found")
|
|
}
|
|
} else {
|
|
error("card not found")
|
|
}
|
|
}
|
|
|
|
private fun createExtras(
|
|
walletManager: WalletManager,
|
|
feeAmount: BigDecimal,
|
|
transactionHash: String,
|
|
): TransactionExtras? {
|
|
return when (walletManager) {
|
|
is EthereumWalletManager -> {
|
|
return EthereumTransactionExtras(
|
|
data = transactionHash.removePrefix(HEX_PREFIX).hexToBytes(),
|
|
gasLimit = BigInteger.valueOf(DEFAULT_GAS_LIMIT),
|
|
)
|
|
}
|
|
else -> {
|
|
null
|
|
}
|
|
}
|
|
}
|
|
|
|
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 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,
|
|
value = amount.value ?: BigDecimal.ZERO,
|
|
decimals = amount.decimals,
|
|
)
|
|
}
|
|
|
|
companion object {
|
|
private const val HEX_PREFIX = "0x"
|
|
private const val DEFAULT_GAS_LIMIT = 300000L
|
|
}
|
|
} |