Updated on 2026-08-14

This commit is contained in:
Tangem 2024-08-12 22:01:26 +05:00
parent f748b8539d
commit f1238f5ef8
20 changed files with 528 additions and 88 deletions

View file

@ -3,7 +3,11 @@ package com.tangem.data.staking
import android.util.Base64
import arrow.core.raise.catch
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.TransactionData
import com.tangem.blockchain.common.TransactionStatus
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchainsdk.utils.toCoinId
import com.tangem.common.extensions.hexToBytes
import com.tangem.common.extensions.toCompressedPublicKey
import com.tangem.data.common.cache.CacheRegistry
import com.tangem.data.staking.converters.*
@ -25,6 +29,7 @@ import com.tangem.datasource.local.token.StakingBalanceStore
import com.tangem.datasource.local.token.StakingYieldsStore
import com.tangem.domain.core.lce.LceFlow
import com.tangem.domain.core.lce.lceFlow
import com.tangem.domain.staking.model.StakingApproval
import com.tangem.domain.staking.model.StakingAvailability
import com.tangem.domain.staking.model.StakingEntryInfo
import com.tangem.domain.staking.model.UnsubmittedTransactionMetadata
@ -231,14 +236,26 @@ internal class DefaultStakingRepository(
}
}
override suspend fun constructTransaction(transactionId: String): StakingTransaction {
override suspend fun constructTransaction(
networkId: String,
fee: Fee,
transactionId: String,
): Pair<StakingTransaction, TransactionData.Compiled> {
return withContext(dispatchers.io) {
val transactionResponse = stakeKitApi.constructTransaction(
transactionId = transactionId,
body = ConstructTransactionRequestBody(),
)
transactionConverter.convert(transactionResponse.getOrThrow())
val transaction = transactionConverter.convert(transactionResponse.getOrThrow())
val unsignedTransaction = transaction.unsignedTransaction ?: error("No unsigned transaction available")
val transactionData = TransactionData.Compiled(
value = getTransactionDataType(networkId, unsignedTransaction),
fee = fee,
status = TransactionStatus.Unconfirmed,
)
transaction to transactionData
}
}
@ -530,10 +547,24 @@ internal class DefaultStakingRepository(
}
}
override fun isApproveNeeded(cryptoCurrency: CryptoCurrency): Boolean {
override fun getStakingApproval(cryptoCurrency: CryptoCurrency): StakingApproval {
return when (cryptoCurrency.id.getIntegrationKey()) {
Blockchain.Ethereum.id.plus(Blockchain.Polygon.toCoinId()) -> true
else -> false
Blockchain.Ethereum.id + Blockchain.Polygon.toCoinId() -> {
StakingApproval.Needed(ETHEREUM_POLYGON_APPROVE_SPENDER)
}
else -> StakingApproval.Empty
}
}
private fun getTransactionDataType(networkId: String, unsignedTransaction: String): TransactionData.Compiled.Data {
val blockchain = Blockchain.fromId(networkId)
return when (blockchain) {
Blockchain.Solana,
Blockchain.Cosmos,
-> TransactionData.Compiled.Data.Bytes(unsignedTransaction.hexToBytes())
Blockchain.Ethereum,
-> TransactionData.Compiled.Data.RawString(unsignedTransaction)
else -> error("Unsupported blockchain")
}
}
@ -579,6 +610,8 @@ internal class DefaultStakingRepository(
const val NEAR_INTEGRATION_ID = "near-near-native-staking"
const val TEZOS_INTEGRATION_ID = "tezos-xtz-native-staking"
const val ETHEREUM_POLYGON_APPROVE_SPENDER = "0x5e3Ef299fDDf15eAa0432E6e66473ace8c13D908"
// uncomment items as implementation is ready
val integrationIdMap = mapOf(
Blockchain.Solana.run { id + toCoinId() } to SOLANA_INTEGRATION_ID,

View file

@ -16,6 +16,7 @@ import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.common.extensions.hexToBytes
import com.tangem.datasource.local.walletmanager.WalletManagersStore
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.transaction.TransactionRepository
import com.tangem.domain.transaction.models.TransactionType
@ -24,6 +25,7 @@ import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.math.BigDecimal
import java.math.BigInteger
import com.tangem.blockchain.blockchains.tron.TransactionType as SdkTransactionType
@ -42,15 +44,15 @@ internal class DefaultTransactionRepository(
network: Network,
txExtras: TransactionExtras?,
hash: String?,
): TransactionData.Uncompiled? = withContext(coroutineDispatcherProvider.io) {
): TransactionData.Uncompiled = withContext(coroutineDispatcherProvider.io) {
val blockchain = Blockchain.fromId(network.id.value)
val walletManager = walletManagersFacade.getOrCreateWalletManager(
userWalletId = userWalletId,
blockchain = blockchain,
derivationPath = network.derivationPath.value,
)
) ?: error("Wallet manager not found")
return@withContext walletManager?.createTransactionDataInternal(
return@withContext walletManager.createTransactionDataInternal(
amount = amount,
fee = fee,
memo = memo,
@ -61,6 +63,48 @@ internal class DefaultTransactionRepository(
)
}
override suspend fun createApprovalTransaction(
amount: Amount,
fee: Fee,
contractAddress: String,
spenderAddress: String,
userWalletId: UserWalletId,
network: Network,
hash: String?,
): TransactionData.Uncompiled = withContext(coroutineDispatcherProvider.io) {
val blockchain = Blockchain.fromId(network.id.value)
val walletManager = walletManagersFacade.getOrCreateWalletManager(
userWalletId = userWalletId,
blockchain = blockchain,
derivationPath = network.derivationPath.value,
) ?: error("Wallet manager not found")
val approver = walletManager as? Approver ?: error("Cannot cast to Approver")
val approvalData = approver.getApproveData(
spenderAddress = spenderAddress,
value = amount,
)
val extras = createTransactionDataExtras(
data = approvalData,
network = network,
transactionType = TransactionType.APPROVE,
nonce = null,
gasLimit = null,
)
return@withContext createTransaction(
amount = amount,
fee = fee,
memo = null,
destination = contractAddress,
userWalletId = userWalletId,
network = network,
txExtras = extras,
hash = hash,
)
}
override suspend fun validateTransaction(
amount: Amount,
fee: Fee?,
@ -141,6 +185,28 @@ internal class DefaultTransactionRepository(
}
}
override suspend fun getAllowance(
userWalletId: UserWalletId,
cryptoCurrency: CryptoCurrency.Token,
spenderAddress: String,
): BigDecimal {
val walletManager = walletManagersFacade.getOrCreateWalletManager(userWalletId, cryptoCurrency.network)
val blockchain = Blockchain.fromId(cryptoCurrency.network.id.value)
val allowanceResult = (walletManager as? Approver)?.getAllowance(
spenderAddress,
Token(
symbol = blockchain.currency,
contractAddress = cryptoCurrency.contractAddress,
decimals = cryptoCurrency.decimals,
),
) ?: error("Cannot cast to Approver")
return allowanceResult.fold(
onSuccess = { it },
onFailure = { error(it) },
)
}
private fun convertToSdkTransactionType(transactionType: TransactionType): SdkTransactionType {
return when (transactionType) {
TransactionType.APPROVE -> SdkTransactionType.APPROVE