Updated on 2026-08-14

This commit is contained in:
Tangem 2024-10-09 14:53:35 +04:00
parent bb58d027c6
commit 0a4235ee0a
25 changed files with 160 additions and 39 deletions

View file

@ -2,6 +2,7 @@ package com.tangem.tap
import com.tangem.TangemSdkLogger
import com.tangem.blockchainsdk.BlockchainSDKFactory
import com.tangem.blockchainsdk.signer.TransactionSignerFactory
import com.tangem.common.routing.AppRouter
import com.tangem.core.analytics.filter.OneTimeEventFilter
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
@ -119,4 +120,6 @@ interface ApplicationEntryPoint {
fun getPushNotificationsFeatureToggles(): PushNotificationsFeatureToggles
fun getTangemAppLogger(): TangemAppLoggerInitializer
fun getTransactionSignerFactory(): TransactionSignerFactory
}

View file

@ -11,9 +11,15 @@ import com.tangem.LogFormat
import com.tangem.TangemSdkLogger
import com.tangem.blockchain.network.BlockchainSdkRetrofitBuilder
import com.tangem.blockchainsdk.BlockchainSDKFactory
import com.tangem.blockchainsdk.signer.TransactionSignerFactory
import com.tangem.common.routing.AppRouter
import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.api.ParamsInterceptor
import com.tangem.core.analytics.filter.OneTimeEventFilter
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.Basic
import com.tangem.core.analytics.models.Basic.TransactionSent.WalletForm
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
import com.tangem.datasource.api.common.MoshiConverter
import com.tangem.datasource.api.common.createNetworkLoggingInterceptor
@ -191,6 +197,9 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
private val tangemAppLoggerInitializer: TangemAppLoggerInitializer
get() = entryPoint.getTangemAppLogger()
private val transactionSignerFactory: TransactionSignerFactory
get() = entryPoint.getTransactionSignerFactory()
// endregion
override fun onCreate() {
@ -273,6 +282,7 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
shareManager = shareManager,
appRouter = appRouter,
pushNotificationsFeatureToggles = pushNotificationsFeatureToggles,
transactionSignerFactory = transactionSignerFactory,
),
),
)
@ -315,6 +325,25 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
logConfig = LogConfig.analyticsHandlers,
jsonConverter = MoshiConverter.sdkMoshiConverter,
)
Analytics.addParamsInterceptor(
interceptor = object : ParamsInterceptor {
override fun id(): String = "SendTransactionSignerInfoInterceptor"
override fun canBeAppliedTo(event: AnalyticsEvent): Boolean = event is Basic.TransactionSent
override fun intercept(params: MutableMap<String, String>) {
val isLastSignWithRing = store.state.globalState.isLastSignWithRing
params[AnalyticsParam.WALLET_FORM] = if (isLastSignWithRing) {
WalletForm.Ring.name
} else {
WalletForm.Card.name
}
}
},
)
factory.build(Analytics, buildData)
// ExceptionHandler.append(blockchainExceptionHandler) TODO: [REDACTED_JIRA]
}

View file

@ -0,0 +1,22 @@
package com.tangem.tap.common.libs.blockchainsdk
import com.tangem.Message
import com.tangem.TangemSdk
import com.tangem.blockchain.common.TransactionSigner
import com.tangem.blockchainsdk.signer.TransactionSignerFactory
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.TangemSigner
import com.tangem.tap.store
internal class DefaultTransactionSignerFactory : TransactionSignerFactory {
override fun createTransactionSigner(cardId: String?, sdk: TangemSdk): TransactionSigner {
return TangemSigner(
cardId = cardId,
tangemSdk = sdk,
initialMessage = Message(),
) { signResponse ->
store.dispatch(action = GlobalAction.IsSignWithRing(signResponse.isRing))
}
}
}

View file

@ -69,6 +69,8 @@ sealed class GlobalAction : Action {
val walletPublicKey: ByteArray,
) : GlobalAction()
data class IsSignWithRing(val isSignWithRing: Boolean) : GlobalAction()
data class SetConfigManager(val configManager: ConfigManager) : GlobalAction()
data class SetFeedbackManager(val feedbackManager: LegacyFeedbackManager) : GlobalAction()

View file

@ -71,6 +71,7 @@ fun globalReducer(action: Action, state: AppState, appStateHolder: AppStateHolde
)
globalState.copy(scanResponse = globalState.scanResponse.copy(card = newCardInstance))
}
is GlobalAction.IsSignWithRing -> globalState.copy(isLastSignWithRing = action.isSignWithRing)
is GlobalAction.SetFeedbackManager -> {
globalState.copy(feedbackManager = action.feedbackManager)
}

View file

@ -25,6 +25,7 @@ data class GlobalState(
val exchangeManager: CurrencyExchangeManager = CurrencyExchangeManager.dummy(),
val userCountryCode: String? = null,
val appThemeMode: AppThemeMode = AppThemeMode.DEFAULT,
val isLastSignWithRing: Boolean = false,
) : StateType
typealias CryptoCurrencyName = String

View file

@ -0,0 +1,23 @@
package com.tangem.tap.di.libs.blockchainsdk
import com.tangem.blockchainsdk.signer.TransactionSignerFactory
import com.tangem.tap.common.libs.blockchainsdk.DefaultTransactionSignerFactory
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
/**
[REDACTED_AUTHOR]
*/
@Module
@InstallIn(SingletonComponent::class)
internal class TransactionSignerFactoryModule {
@Provides
@Singleton
fun provideTransactionSignerFactory(): TransactionSignerFactory {
return DefaultTransactionSignerFactory()
}
}

View file

@ -5,17 +5,15 @@ import com.tangem.TangemSdk
import com.tangem.blockchain.common.TransactionSigner
import com.tangem.blockchain.common.Wallet
import com.tangem.common.CompletionResult
import com.tangem.domain.common.TapWorkarounds.isTangemTwins
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.isRing
import com.tangem.tap.domain.tasks.SignHashesTask
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume
class TangemSigner(
private val card: CardDTO,
private val cardId: String?,
private val tangemSdk: TangemSdk,
private val initialMessage: Message,
private val accessCode: String? = null,
private val signerCallback: (TangemSignerResponse) -> Unit,
) : TransactionSigner {
@ -24,21 +22,20 @@ class TangemSigner(
publicKey: Wallet.PublicKey,
): CompletionResult<List<ByteArray>> {
return suspendCancellableCoroutine { continuation ->
val isCardNotBackedUp = card.backupStatus?.isActive != true && !card.isTangemTwins
val task = SignHashesTask(hashes, publicKey)
tangemSdk.startSessionWithRunnable(
runnable = task,
cardId = card.cardId.takeIf { isCardNotBackedUp },
cardId = cardId,
initialMessage = initialMessage,
accessCode = accessCode,
) { result ->
when (result) {
is CompletionResult.Success -> {
signerCallback(
TangemSignerResponse(
result.data.totalSignedHashes,
result.data.remainingSignatures,
totalSignedHashes = result.data.totalSignedHashes,
remainingSignatures = result.data.remainingSignatures,
isRing = result.data.batchId?.let(::isRing) ?: false,
),
)
if (continuation.isActive) {
@ -70,4 +67,5 @@ class TangemSigner(
data class TangemSignerResponse(
val totalSignedHashes: Int?,
val remainingSignatures: Int?,
val isRing: Boolean,
)

View file

@ -12,6 +12,7 @@ class TangemSignHashesResponse(
val signatures: List<ByteArray>,
val totalSignedHashes: Int?,
val remainingSignatures: Int?,
val batchId: String?,
) : CommandResponse
class SignHashesTask(
@ -22,12 +23,14 @@ class SignHashesTask(
SignHashesCommand(hashes.toTypedArray(), publicKey.seedKey, publicKey.derivationPath).run(session) { response ->
when (response) {
is CompletionResult.Success -> {
val card = session.environment.card
callback(
CompletionResult.Success(
TangemSignHashesResponse(
response.data.signatures,
response.data.totalSignedHashes,
session.environment.card?.wallet(publicKey.seedKey)?.remainingSignatures,
signatures = response.data.signatures,
totalSignedHashes = response.data.totalSignedHashes,
remainingSignatures = card?.wallet(publicKey.seedKey)?.remainingSignatures,
batchId = card?.batchId,
),
),
)

View file

@ -211,11 +211,13 @@ class WalletConnectSdkHelper {
}
private suspend fun sendTransaction(data: WcTransactionData, cardId: String?): String? {
val sdk = store.inject(DaggerGraphState::cardSdkConfigRepository).sdk
val result = (data.walletManager as TransactionSender).send(
transactionData = data.transaction,
signer = CommonSigner(
tangemSdk = store.inject(DaggerGraphState::cardSdkConfigRepository).sdk,
signer = store.inject(DaggerGraphState::transactionSignerFactory).createTransactionSigner(
cardId = cardId,
sdk = sdk,
),
)
return when (result) {

View file

@ -28,8 +28,7 @@ import com.tangem.core.analytics.models.Basic
import com.tangem.core.ui.extensions.setStatusBarColor
import com.tangem.datasource.utils.isNullOrEmpty
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.models.scan.CardDTO.Companion.RING_BATCH_IDS
import com.tangem.domain.models.scan.CardDTO.Companion.RING_BATCH_PREFIX
import com.tangem.domain.models.scan.isRing
import com.tangem.feature.onboarding.data.model.CreateWalletResponse
import com.tangem.feature.onboarding.presentation.wallet2.analytics.SeedPhraseSource
import com.tangem.feature.onboarding.presentation.wallet2.viewmodel.SeedPhraseMediator
@ -590,8 +589,4 @@ class OnboardingWalletFragment :
}
}
}
private fun isRing(batchId: String): Boolean {
return RING_BATCH_IDS.contains(batchId) || batchId.startsWith(RING_BATCH_PREFIX)
}
}

View file

@ -7,6 +7,7 @@ import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.blockchain.extensions.Result
import com.tangem.domain.common.TapWorkarounds.isTangemTwins
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.tokens.model.CryptoCurrency
@ -105,8 +106,9 @@ suspend fun buyErc20TestnetTokens(card: CardDTO, walletManager: EthereumWalletMa
val coinValue = walletManager.wallet.amounts[AmountType.Coin]?.value ?: BigDecimal.ZERO
if (coinValue < fee.amount.value) return
val isCardNotBackedUp = card.backupStatus?.isActive != true && !card.isTangemTwins
val signer = TangemSigner(
card = card,
cardId = card.cardId.takeIf { isCardNotBackedUp },
tangemSdk = store.inject(DaggerGraphState::cardSdkConfigRepository).sdk,
initialMessage = Message(),
) { signResponse ->
@ -117,6 +119,8 @@ suspend fun buyErc20TestnetTokens(card: CardDTO, walletManager: EthereumWalletMa
remainingSignatures = signResponse.remainingSignatures,
),
)
store.dispatch(action = GlobalAction.IsSignWithRing(signResponse.isRing))
}
walletManager.send(

View file

@ -2,6 +2,7 @@ package com.tangem.tap.proxy.redux
import com.tangem.TangemSdkLogger
import com.tangem.blockchainsdk.BlockchainSDKFactory
import com.tangem.blockchainsdk.signer.TransactionSignerFactory
import com.tangem.common.routing.AppRouter
import com.tangem.core.navigation.email.EmailSender
import com.tangem.core.navigation.share.ShareManager
@ -87,4 +88,5 @@ data class DaggerGraphState(
val appRouter: AppRouter? = null,
val pushNotificationsFeatureToggles: PushNotificationsFeatureToggles? = null,
val pushNotificationsRouter: PushNotificationsRouter? = null,
val transactionSignerFactory: TransactionSignerFactory? = null,
) : StateType