From 8ac903ba8092d285efcf47552be3f8188bd5f048 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 20 Dec 2021 23:16:27 +0300 Subject: [PATCH 01/20] Updated on 2026-08-14 --- .../walletconnect/WalletConnectManager.kt | 24 +++- .../WalletConnectNetworkUtils.kt | 39 ++++++ .../walletconnect/WalletConnectRepository.kt | 1 - .../walletconnect/WalletConnectSdkHelper.kt | 21 ++- .../walletconnect/WalletConnectAction.kt | 6 +- .../walletconnect/WalletConnectMiddleware.kt | 123 +++++++++++------- .../walletconnect/WalletConnectReducer.kt | 3 + .../redux/walletconnect/WalletConnectState.kt | 51 +++++++- 8 files changed, 200 insertions(+), 68 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectNetworkUtils.kt diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt index 7c74264429..7dce9c8061 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt @@ -60,6 +60,15 @@ class WalletConnectManager { setupConnectionTimeoutCheck(session) } + fun updateSession(session: WalletConnectSession) { + val updatedSession = sessions[session.session]?.copy( + wallet = session.wallet + ) + if (updatedSession != null) { + sessions[session.session] = updatedSession + } + } + private fun setupConnectionTimeoutCheck(session: WCSession) { scope.launch { delay(20_000) @@ -81,7 +90,7 @@ class WalletConnectManager { client = WCClient(httpClient = okHttpClient), session = session.session, peerMeta = session.peerMeta, - wallet = session.wallet + wallet = session.wallet, ) .also { setListeners(it.client) @@ -96,11 +105,12 @@ class WalletConnectManager { val activeData = sessions[session] ?: return removeSimilarSessions(activeData) - val key = activeData.wallet.derivedPublicKey ?: activeData.wallet.walletPublicKey - val accounts = listOf(Blockchain.Ethereum.makeAddresses(key).first().value) + val key = activeData.wallet.derivedPublicKey ?: activeData.wallet.walletPublicKey ?: return + val blockchain = activeData.wallet.getBlockchainForSession() + val accounts = listOf(blockchain.makeAddresses(key).first().value) val approved = activeData.client.approveSession( accounts = accounts, - chainId = activeData.wallet.chainId + chainId = blockchain.getChainId() ?: Blockchain.Ethereum.getChainId()!! ) if (approved) { val walletConnectSession = WalletConnectSession( @@ -119,7 +129,7 @@ class WalletConnectManager { fun removeSimilarSessions(activeData: WalletConnectActiveData) { val sessionsToRemove = sessions.filter { - it.value.wallet.walletPublicKey == activeData.wallet.walletPublicKey + it.value.wallet.walletPublicKey?.equals(activeData.wallet.walletPublicKey) == true && it.value.peerMeta?.url == activeData.peerMeta?.url && it.value.session != activeData.session } @@ -246,7 +256,9 @@ class WalletConnectManager { val sessionData = data.toWalletConnectSession() sessionData?.let { store.dispatchOnMain(WalletConnectAction.AcceptOpeningSession( - sessionData)) + session = sessionData, + chainId = client.chainId?.toIntOrNull() + )) } FirebaseAnalyticsHandler.logWcEvent( FirebaseAnalyticsHandler.WcAnalyticsEvent.Session( diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectNetworkUtils.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectNetworkUtils.kt new file mode 100644 index 0000000000..4907e8d40c --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectNetworkUtils.kt @@ -0,0 +1,39 @@ +package com.tangem.tap.domain.walletconnect + +import com.tangem.blockchain.common.Blockchain +import com.trustwallet.walletconnect.models.WCPeerMeta + +class WalletConnectNetworkUtils { + + companion object { + + fun parseBlockchain( + chainId: Int?, + peer: WCPeerMeta, + isTestNet: Boolean? = null + ): Blockchain? { + return when { + chainId != null -> { + Blockchain.fromChainId(chainId) + } + peer.url.contains("matic.network") || peer.name == "Polygon" -> { + Blockchain.Polygon + } + peer.url.contains("binance.org") || peer.name.contains("Binance") -> { + if (peer.icons.firstOrNull()?.contains("testnet") == true) { + Blockchain.BinanceTestnet + } else { + Blockchain.Binance + } + } + peer.name.contains("BSC") -> { + Blockchain.BSC + } + else -> { + if (isTestNet == true) Blockchain.EthereumTestnet else Blockchain.Ethereum + } + } + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectRepository.kt index a4654d6429..67dab3645d 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectRepository.kt @@ -88,7 +88,6 @@ data class SessionDao( session = session.session, peerMeta = session.peerMeta ) - } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt index 85ef0eaac6..35a1789f89 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt @@ -35,17 +35,16 @@ class WalletConnectSdkHelper { ): WcTransactionData? { val factory = store.state.globalState.tapWalletManager.walletManagerFactory val publicKey = Wallet.PublicKey( - session.wallet.walletPublicKey, + session.wallet.walletPublicKey ?: return null, session.wallet.derivedPublicKey, session.wallet.derivationPath, ) - val walletManager = factory.makeEthereumWalletManager( - session.wallet.cardId, - publicKey, - emptyList(), - isTestNet = session.wallet.isTestNet - ) ?: return null - + val blockchain = session.wallet.getBlockchainForSession() + val walletManager = factory.makeWalletManager( + session.wallet.cardId, + blockchain, + publicKey + ) ?: return null try { walletManager.update() @@ -54,8 +53,6 @@ class WalletConnectSdkHelper { return null } - val blockchain = walletManager.wallet.blockchain - val balance = walletManager.wallet.amounts[AmountType.Coin]?.value ?: return null @@ -213,12 +210,12 @@ class WalletConnectSdkHelper { suspend fun signPersonalMessage(hashToSign: ByteArray, wallet: WalletForSession): String? { val key = wallet.derivedPublicKey ?: wallet.walletPublicKey - val command = SignHashCommand(hashToSign, wallet.walletPublicKey, wallet.derivationPath) + val command = SignHashCommand(hashToSign, wallet.walletPublicKey!!, wallet.derivationPath) return when (val result = tangemSdkManager.runTaskAsync(command, wallet.cardId)) { is CompletionResult.Success -> { val hash = result.data.signature return EthereumUtils.prepareSignedMessageData( - hash, hashToSign, CryptoUtils.decompressPublicKey(key) + hash, hashToSign, CryptoUtils.decompressPublicKey(key!!) ) } is CompletionResult.Failure -> { diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt index 16a3f638a4..1c19721743 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt @@ -2,6 +2,7 @@ package com.tangem.tap.features.details.redux.walletconnect import android.app.Activity import com.tangem.tap.common.redux.NotificationAction +import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.wallet.R import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage import com.trustwallet.walletconnect.models.ethereum.WCEthereumTransaction @@ -27,13 +28,16 @@ sealed class WalletConnectAction : Action { data class OpenSession( val wcUri: String, val wallet: WalletForSession, + val scanResponse: ScanResponse ) : WalletConnectAction() object RefuseOpeningSession : WalletConnectAction() data class OpeningSessionTimeout(val session: WCSession) : WalletConnectAction() - data class AcceptOpeningSession(val session: WalletConnectSession) : WalletConnectAction() + data class AcceptOpeningSession( + val session: WalletConnectSession, val chainId: Int?, + ) : WalletConnectAction() data class ApproveSession( val session: WCSession, diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt index 2c1aab1596..06043d4df2 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt @@ -1,7 +1,6 @@ package com.tangem.tap.features.details.redux.walletconnect import com.tangem.blockchain.common.* -import com.tangem.common.extensions.guard import com.tangem.tap.* import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.getFromClipboard @@ -14,9 +13,12 @@ import com.tangem.tap.domain.extensions.makeWalletManagerForApp import com.tangem.tap.domain.isMultiwalletAllowed import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.domain.walletconnect.WalletConnectManager +import com.tangem.tap.domain.walletconnect.WalletConnectNetworkUtils import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.wallet.R +import com.trustwallet.walletconnect.models.WCPeerMeta import org.rekotlin.Middleware +import timber.log.Timber class WalletConnectMiddleware { private val walletConnectManager = WalletConnectManager() @@ -48,8 +50,13 @@ class WalletConnectMiddleware { } is WalletConnectAction.ShowClipboardOrScanQrDialog -> { - store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.ClipboardOrScanQr( - action.wcUri))) + store.dispatchOnMain( + GlobalAction.ShowDialog( + WalletConnectDialog.ClipboardOrScanQr( + action.wcUri + ) + ) + ) } is WalletConnectAction.ScanCard -> { @@ -78,14 +85,23 @@ class WalletConnectMiddleware { } is WalletConnectAction.RefuseOpeningSession -> { - store.dispatch(GlobalAction.ShowDialog( - WalletConnectDialog.OpeningSessionRejected - )) + store.dispatch( + GlobalAction.ShowDialog( + WalletConnectDialog.OpeningSessionRejected + ) + ) } is WalletConnectAction.AcceptOpeningSession -> { - store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.ApproveWcSession( - action.session))) + generateWalletKeys(action.session, action.chainId) + + store.dispatchOnMain( + GlobalAction.ShowDialog( + WalletConnectDialog.ApproveWcSession( + action.session + ) + ) + ) } is WalletConnectAction.ApproveSession -> { @@ -134,60 +150,79 @@ class WalletConnectMiddleware { return@ScanCard } - val walletManager = getWalletManager(scanResponse).guard { - store.dispatchOnMain(WalletConnectAction.UnsupportedCard) - return@ScanCard - } - - val wallet = walletManager.wallet - val derivedKey = if (wallet.publicKey.blockchainKey.contentEquals(wallet.publicKey.seedKey)) { - null - } else { - walletManager.wallet.publicKey.blockchainKey - } - store.dispatchOnMain(WalletConnectAction.OpenSession( - wcUri = wcUri, - wallet = WalletForSession( - card.cardId, - wallet.publicKey.seedKey, - derivedKey, - wallet.publicKey.derivationPath, - card.isTestCard - ), - )) + store.dispatchOnMain( + WalletConnectAction.OpenSession( + wcUri = wcUri, + wallet = WalletForSession( + scanResponse.card.cardId, + null, + null, + null, + scanResponse.card.isTestCard + ), + scanResponse = scanResponse + ) + ) }, { store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession(null)) }, R.string.wallet_connect_scan_card_message)) } - private fun getWalletManager(scanResponse: ScanResponse): WalletManager? { + private fun generateWalletKeys(session: WalletConnectSession, chainId: Int?) { + val scanResponse = store.state.walletConnectState.scanResponse ?: return + val walletManager = getWalletManager(scanResponse, session.peerMeta, chainId) ?: return + + val wallet = walletManager.wallet + val derivedKey = + if (wallet.publicKey.blockchainKey.contentEquals(wallet.publicKey.seedKey)) { + null + } else { + walletManager.wallet.publicKey.blockchainKey + } + val updatedWallet = session.wallet.copy( + walletPublicKey = wallet.publicKey.seedKey, + derivedPublicKey = derivedKey, + derivationPath = wallet.publicKey.derivationPath, + blockchain = wallet.blockchain + ) + val updatedSession = session.copy(wallet = updatedWallet) + walletConnectManager.updateSession(updatedSession) + } + + private fun getWalletManager( + scanResponse: ScanResponse, peer: WCPeerMeta, chainId: Int? + ): WalletManager? { val card = scanResponse.card val factory = store.state.globalState.tapWalletManager.walletManagerFactory - val wcBlockchain = if (scanResponse.card.isTestCard) { - Blockchain.EthereumTestnet - } else { - Blockchain.Ethereum - } + val blockchain = WalletConnectNetworkUtils.parseBlockchain( + chainId = chainId, peer = peer, isTestNet = scanResponse.card.isTestCard + ) ?: if (scanResponse.card.isTestCard) Blockchain.EthereumTestnet else Blockchain.Ethereum + + Timber.d(blockchain.fullName) + return if (store.state.globalState.scanResponse?.card?.cardId == card.cardId) { - store.state.walletState.getWalletManager(wcBlockchain) - ?: factory.makeWalletManagerForApp(scanResponse, wcBlockchain) + store.state.walletState.getWalletManager(blockchain) + ?: factory.makeWalletManagerForApp(scanResponse, blockchain) ?.also { walletManager -> store.dispatch(WalletAction.MultiWallet.AddWalletManagers(walletManager)) store.dispatch(WalletAction.MultiWallet.AddBlockchain(walletManager.wallet.blockchain)) } } else { + val walletManager = factory.makeWalletManagerForApp(scanResponse, blockchain) if (currenciesRepository.loadCardCurrencies(card.cardId)?.blockchains?.contains( - wcBlockchain) == true + blockchain + ) != true ) { - factory.makeWalletManagerForApp(scanResponse, wcBlockchain) - } else { - factory.makeWalletManagerForApp(scanResponse, wcBlockchain) - ?.also { - currenciesRepository.saveAddedBlockchain(card.cardId, wcBlockchain) - } + walletManager?.let { + currenciesRepository.saveAddedBlockchain( + card.cardId, + blockchain + ) + } } + return walletManager } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectReducer.kt index c8b6f3b1b7..c96f72fc9e 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectReducer.kt @@ -16,6 +16,9 @@ class WalletConnectReducer { sessions = state.sessions + action.session ) } + is WalletConnectAction.OpenSession -> { + state.copy(scanResponse = action.scanResponse) + } is WalletConnectAction.SetSessionsRestored -> WalletConnectState(sessions = action.sessions) diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt index 2757d8cea9..e2713b92d1 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt @@ -1,10 +1,12 @@ package com.tangem.tap.features.details.redux.walletconnect import com.squareup.moshi.JsonClass +import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.common.WalletManager import com.tangem.common.hdWallet.DerivationPath import com.tangem.tap.common.redux.StateDialog +import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.features.details.ui.walletconnect.dialogs.PersonalSignDialogData import com.tangem.tap.features.details.ui.walletconnect.dialogs.TransactionRequestDialogData import com.trustwallet.walletconnect.models.WCPeerMeta @@ -13,6 +15,7 @@ import com.trustwallet.walletconnect.models.session.WCSession data class WalletConnectState( val loading: Boolean = false, val sessions: List = listOf(), + val scanResponse: ScanResponse? = null ) data class WalletConnectSession( @@ -21,18 +24,58 @@ data class WalletConnectSession( val wallet: WalletForSession, val session: WCSession, val peerMeta: WCPeerMeta, -) +) { + fun getAddress(): String? { + val key = wallet.derivedPublicKey ?: wallet.walletPublicKey ?: return null + return wallet.blockchain?.makeAddresses(key)?.first()?.value + } +} @JsonClass(generateAdapter = true) data class WalletForSession( val cardId: String, - val walletPublicKey: ByteArray, + val walletPublicKey: ByteArray?, val derivedPublicKey: ByteArray?, val derivationPath: DerivationPath?, val isTestNet: Boolean = false, + val blockchain: Blockchain? = if (isTestNet) Blockchain.EthereumTestnet else Blockchain.Ethereum ) { - val chainId - get() = if (isTestNet) 4 else 1 + + fun getBlockchainForSession(): Blockchain { + return blockchain ?: if (isTestNet) Blockchain.EthereumTestnet else Blockchain.Ethereum + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as WalletForSession + + if (cardId != other.cardId) return false + if (walletPublicKey != null) { + if (other.walletPublicKey == null) return false + if (!walletPublicKey.contentEquals(other.walletPublicKey)) return false + } else if (other.walletPublicKey != null) return false + if (derivedPublicKey != null) { + if (other.derivedPublicKey == null) return false + if (!derivedPublicKey.contentEquals(other.derivedPublicKey)) return false + } else if (other.derivedPublicKey != null) return false + if (derivationPath != other.derivationPath) return false + if (isTestNet != other.isTestNet) return false + if (blockchain != other.blockchain) return false + + return true + } + + override fun hashCode(): Int { + var result = cardId.hashCode() + result = 31 * result + (walletPublicKey?.contentHashCode() ?: 0) + result = 31 * result + (derivedPublicKey?.contentHashCode() ?: 0) + result = 31 * result + (derivationPath?.hashCode() ?: 0) + result = 31 * result + isTestNet.hashCode() + result = 31 * result + (blockchain?.hashCode() ?: 0) + return result + } } From 198d625848df73981a53eff5ca014ae8a67071f0 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 29 Dec 2021 08:10:27 +0300 Subject: [PATCH 02/20] Updated on 2026-08-14 --- .../com/tangem/tap/common/DialogManager.kt | 9 + .../tap/domain/walletconnect/BnbHelper.kt | 79 +++++++++ .../walletconnect/WalletConnectManager.kt | 163 +++++++++++++----- .../walletconnect/WalletConnectSdkHelper.kt | 70 ++++++-- .../walletconnect/WalletConnectAction.kt | 18 ++ .../walletconnect/WalletConnectMiddleware.kt | 34 ++++ .../redux/walletconnect/WalletConnectState.kt | 35 +++- .../dialogs/BnbTransactionDialog.kt | 66 +++++++ .../middlewares/MultiWalletMiddleware.kt | 2 +- .../main/res/values/strings_untranslated.xml | 17 ++ 10 files changed, 432 insertions(+), 61 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/domain/walletconnect/BnbHelper.kt create mode 100644 app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/BnbTransactionDialog.kt diff --git a/app/src/main/java/com/tangem/tap/common/DialogManager.kt b/app/src/main/java/com/tangem/tap/common/DialogManager.kt index 8f1491368c..7f3bf094a7 100644 --- a/app/src/main/java/com/tangem/tap/common/DialogManager.kt +++ b/app/src/main/java/com/tangem/tap/common/DialogManager.kt @@ -79,6 +79,15 @@ class DialogManager : StoreSubscriber { TransactionDialog.create(state.dialog.dialogData, context) is WalletConnectDialog.PersonalSign -> PersonalSignDialog.create(state.dialog.data, context) + is WalletConnectDialog.BnbTransactionDialog -> + BnbTransactionDialog.create( + data = state.dialog.data, + session = state.dialog.session, + sessionId = state.dialog.sessionId, + cardId = state.dialog.cardId, + dAppName = state.dialog.dAppName, + context = context + ) is BackupDialog.AddMoreBackupCards -> AddMoreBackupCardsDialog.create(context) is BackupDialog.BackupInProgress -> BackupInProgressDialog.create(context) is BackupDialog.UnfinishedBackupFound -> UnfinishedBackupFoundDialog.create(context) diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/BnbHelper.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/BnbHelper.kt new file mode 100644 index 0000000000..a8f3eb83c1 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/BnbHelper.kt @@ -0,0 +1,79 @@ +package com.tangem.tap.domain.walletconnect + +import com.github.salomonbrys.kotson.registerTypeAdapter +import com.google.gson.GsonBuilder +import com.tangem.blockchain.common.Blockchain +import com.tangem.common.extensions.calculateSha256 +import com.tangem.tap.features.details.redux.walletconnect.BinanceMessageData +import com.tangem.tap.features.details.redux.walletconnect.TradeData +import com.trustwallet.walletconnect.models.binance.WCBinanceTradeOrder +import com.trustwallet.walletconnect.models.binance.WCBinanceTransferOrder +import com.trustwallet.walletconnect.models.binance.tradeOrderSerializer +import timber.log.Timber + +class BnbHelper { + companion object { + + fun createMessageData(order: WCBinanceTransferOrder): BinanceMessageData.Transfer { + val input = order.msgs.first().inputs.first() + val output = order.msgs.first().inputs.first() + + val currency = + input.coins.firstOrNull()?.denom ?: Blockchain.Binance + val amount = input.coins + .mapNotNull { if (it.denom == currency) it.amount else null } + .sum() + .toBigDecimal() + .movePointRight(Blockchain.Binance.decimals()) + .toString() + + val gson = GsonBuilder() + .registerTypeAdapter(tradeOrderSerializer) + .serializeNulls() + .create() + + return BinanceMessageData.Transfer( + outputAddress = output.address, + amount = amount, + address = input.address, + data = gson.toJson(order).toByteArray().calculateSha256() + ) + } + + fun createMessageData(order: WCBinanceTradeOrder): BinanceMessageData.Trade { + val address = order.msgs.first().sender + + val tradeData = order.msgs.map { + val price = it.price.toBigDecimal() + .movePointLeft(Blockchain.Binance.decimals()) + .stripTrailingZeros() + val quantity = it.quantity.toBigDecimal() + .movePointLeft(Blockchain.Binance.decimals()) + .stripTrailingZeros() + + val amount = price * quantity + val symbol = it.symbol.substringBefore("-") + + TradeData( + price = "$price ${Blockchain.Binance.currency}", + quantity = "$quantity $symbol", + amount = "$amount ${Blockchain.Binance.currency}", + symbol = symbol + ) + } + val gson = GsonBuilder() + .registerTypeAdapter(tradeOrderSerializer) + .serializeNulls() + .create() + val serialized = gson.toJson(order) + Timber.d(serialized) + + return BinanceMessageData.Trade( + tradeData = tradeData, + address = address, + data = serialized.toByteArray().calculateSha256() + ) + } + + } +} diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt index 7dce9c8061..c5e727e23b 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt @@ -11,6 +11,10 @@ import com.tangem.tap.store import com.tangem.tap.walletConnectRepository import com.trustwallet.walletconnect.WCClient import com.trustwallet.walletconnect.models.WCPeerMeta +import com.trustwallet.walletconnect.models.binance.WCBinanceCancelOrder +import com.trustwallet.walletconnect.models.binance.WCBinanceTradeOrder +import com.trustwallet.walletconnect.models.binance.WCBinanceTransferOrder +import com.trustwallet.walletconnect.models.binance.WCBinanceTxConfirmParam import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage import com.trustwallet.walletconnect.models.ethereum.WCEthereumTransaction import com.trustwallet.walletconnect.models.session.WCSession @@ -121,8 +125,11 @@ class WalletConnectManager { peerMeta = activeData.peerMeta!! ) walletConnectRepository.saveSession(walletConnectSession) - store.dispatchOnMain(WalletConnectAction.ApproveSession.Success( - walletConnectSession)) + store.dispatchOnMain( + WalletConnectAction.ApproveSession.Success( + walletConnectSession + ) + ) } } @@ -158,7 +165,8 @@ class WalletConnectManager { private fun onSessionClosed(session: WCSession) { FirebaseAnalyticsHandler.logWcEvent( FirebaseAnalyticsHandler.WcAnalyticsEvent.Session( - FirebaseAnalyticsHandler.WcSessionEvent.Disconnect, sessions[session]?.peerMeta?.url) + FirebaseAnalyticsHandler.WcSessionEvent.Disconnect, sessions[session]?.peerMeta?.url + ) ) sessions.remove(session) walletConnectRepository.removeSession(session) @@ -180,17 +188,23 @@ class WalletConnectManager { type = type ).guard { sessions[session.session] = activeData.copy(transactionData = null) - store.dispatchOnMain(WalletConnectAction.RejectRequest( - session.session, - id - )) + store.dispatchOnMain( + WalletConnectAction.RejectRequest( + session.session, + id + ) + ) return@launch } sessions[session.session] = activeData.copy(transactionData = data) - store.dispatchOnMain(WalletConnectAction.SetDataToSend(data)) - store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.RequestTransaction( - data.dialogData))) + store.dispatchOnMain( + GlobalAction.ShowDialog( + WalletConnectDialog.RequestTransaction( + data.dialogData + ) + ) + ) } } @@ -200,13 +214,34 @@ class WalletConnectManager { scope.launch { val hash = WalletConnectSdkHelper().completeTransaction(data).guard { sessions[data.session.session] = activeData.copy(transactionData = null) - store.dispatchOnMain(WalletConnectAction.RejectRequest(data.session.session, - data.id - )) + store.dispatchOnMain( + WalletConnectAction.RejectRequest( + data.session.session, + data.id + ) + ) return@launch } - acceptRequest(data.session.session, data.id, hash) - sessions[data.session.session] = activeData.copy(transactionData = null) + acceptRequest(session, data.id, hash) + sessions[session] = activeData.copy(transactionData = null) + } + } + + fun signBnb( + id: Long, data: ByteArray, sessionData: WCSession, + ) { + val activeData = sessions[sessionData] ?: return + scope.launch { + val hash = WalletConnectSdkHelper().signBnbTransaction(data, activeData).guard { + store.dispatchOnMain( + WalletConnectAction.RejectRequest( + sessionData, + id + ) + ) + return@launch + } + acceptRequest(sessionData, id, hash) } } @@ -221,9 +256,13 @@ class WalletConnectManager { message = message, session = session, id = id ) sessions[session.session] = activeData.copy(personalSignData = data) - store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.PersonalSign( - data.dialogData - ))) + store.dispatchOnMain( + GlobalAction.ShowDialog( + WalletConnectDialog.PersonalSign( + data.dialogData + ) + ) + ) } } @@ -234,9 +273,12 @@ class WalletConnectManager { val hash = WalletConnectSdkHelper().signPersonalMessage(data.hash, activeData.wallet) .guard { sessions[data.session.session] = activeData.copy(transactionData = null) - store.dispatchOnMain(WalletConnectAction.RejectRequest(data.session.session, - data.id - )) + store.dispatchOnMain( + WalletConnectAction.RejectRequest( + data.session.session, + data.id + ) + ) return@launch } sessions[session] = activeData.copy(personalSignData = data) @@ -255,14 +297,17 @@ class WalletConnectManager { sessions[session] = data val sessionData = data.toWalletConnectSession() sessionData?.let { - store.dispatchOnMain(WalletConnectAction.AcceptOpeningSession( - session = sessionData, - chainId = client.chainId?.toIntOrNull() - )) + store.dispatchOnMain( + WalletConnectAction.AcceptOpeningSession( + session = sessionData, + chainId = client.chainId?.toIntOrNull() + ) + ) } FirebaseAnalyticsHandler.logWcEvent( FirebaseAnalyticsHandler.WcAnalyticsEvent.Session( - FirebaseAnalyticsHandler.WcSessionEvent.Connect, peer.url) + FirebaseAnalyticsHandler.WcSessionEvent.Connect, peer.url + ) ) } } @@ -279,12 +324,14 @@ class WalletConnectManager { ) ) sessions[client.session]?.toWalletConnectSession()?.let { sessionData -> - store.dispatchOnMain(WalletConnectAction.HandleTransactionRequest( - transaction = transaction, - session = sessionData, - id = id, - type = WcTransactionType.EthSendTransaction - )) + store.dispatchOnMain( + WalletConnectAction.HandleTransactionRequest( + transaction = transaction, + session = sessionData, + id = id, + type = WcTransactionType.EthSendTransaction + ) + ) } } client.onEthSignTransaction = { id: Long, transaction: WCEthereumTransaction -> @@ -295,12 +342,14 @@ class WalletConnectManager { ) ) sessions[client.session]?.toWalletConnectSession()?.let { sessionData -> - store.dispatchOnMain(WalletConnectAction.HandleTransactionRequest( - transaction = transaction, - session = sessionData, - id = id, - type = WcTransactionType.EthSignTransaction - )) + store.dispatchOnMain( + WalletConnectAction.HandleTransactionRequest( + transaction = transaction, + session = sessionData, + id = id, + type = WcTransactionType.EthSignTransaction + ) + ) } } client.onEthSign = { id: Long, message: WCEthereumSignMessage -> @@ -311,12 +360,42 @@ class WalletConnectManager { ) ) sessions[client.session]?.toWalletConnectSession()?.let { sessionData -> - store.dispatchOnMain(WalletConnectAction.HandlePersonalSignRequest( - message, - sessionData, - id)) + store.dispatchOnMain( + WalletConnectAction.HandlePersonalSignRequest( + message, + sessionData, + id + ) + ) } } + + + client.onBnbCancel = { id: Long, order: WCBinanceCancelOrder -> + + } + client.onBnbTrade = { id: Long, order: WCBinanceTradeOrder -> + sessions[client.session]?.toWalletConnectSession()?.let { sessionData -> + store.dispatchOnMain( + WalletConnectAction.BinanceTransaction.Trade( + id = id, order = order, sessionData = sessionData + ) + ) + } + } + client.onBnbTransfer = { id: Long, order: WCBinanceTransferOrder -> + sessions[client.session]?.toWalletConnectSession()?.let { sessionData -> + store.dispatchOnMain( + WalletConnectAction.BinanceTransaction.Transfer( + id = id, order = order, sessionData = sessionData + ) + ) + } + } + client.onBnbTxConfirm = { id: Long, order: WCBinanceTxConfirmParam -> + // send empty approve request if status is OK + if (order.ok) client.approveRequest(id, "") + } client.onDisconnect = { code: Int, reason: String -> val session = client.session if (session != null) { diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt index 35a1789f89..74f1abc2b0 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt @@ -10,6 +10,7 @@ import com.tangem.blockchain.extensions.* import com.tangem.common.CompletionResult import com.tangem.common.core.TangemSdkError import com.tangem.common.extensions.hexToBytes +import com.tangem.common.extensions.toHexString import com.tangem.crypto.CryptoUtils import com.tangem.operations.sign.SignHashCommand import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler @@ -33,19 +34,8 @@ class WalletConnectSdkHelper { id: Long, type: WcTransactionType, ): WcTransactionData? { - val factory = store.state.globalState.tapWalletManager.walletManagerFactory - val publicKey = Wallet.PublicKey( - session.wallet.walletPublicKey ?: return null, - session.wallet.derivedPublicKey, - session.wallet.derivationPath, - ) - val blockchain = session.wallet.getBlockchainForSession() - val walletManager = factory.makeWalletManager( - session.wallet.cardId, - blockchain, - publicKey - ) ?: return null + val walletManager = getWalletManager(session) ?: return null try { walletManager.update() } catch (exception: Exception) { @@ -53,13 +43,14 @@ class WalletConnectSdkHelper { return null } + val wallet = walletManager.wallet val balance = - walletManager.wallet.amounts[AmountType.Coin]?.value ?: return null + wallet.amounts[AmountType.Coin]?.value ?: return null val gas = transaction.gas?.hexToBigDecimal() ?: transaction.gasLimit?.hexToBigDecimal() ?: return null - val decimals = blockchain.decimals() + val decimals = wallet.blockchain.decimals() val value = (transaction.value ?: "0").hexToBigDecimal() ?.movePointLeft(decimals) ?: return null @@ -79,8 +70,8 @@ class WalletConnectSdkHelper { val total = value + fee val transactionData = TransactionData( - amount = Amount(value, blockchain), - fee = Amount(fee, blockchain), + amount = Amount(value, wallet.blockchain), + fee = Amount(fee, wallet.blockchain), sourceAddress = transaction.from, destinationAddress = transaction.to!!, extras = EthereumTransactionExtras( @@ -112,6 +103,21 @@ class WalletConnectSdkHelper { ) } + private fun getWalletManager(session: WalletConnectSession): WalletManager? { + val factory = store.state.globalState.tapWalletManager.walletManagerFactory + val publicKey = Wallet.PublicKey( + session.wallet.walletPublicKey ?: return null, + session.wallet.derivedPublicKey, + session.wallet.derivationPath, + ) + val blockchain = session.wallet.getBlockchainForSession() + return factory.makeWalletManager( + session.wallet.cardId, + blockchain, + publicKey + ) + } + suspend fun completeTransaction(data: WcTransactionData): String? { return when (data.type) { WcTransactionType.EthSendTransaction -> sendTransaction(data) @@ -172,6 +178,35 @@ class WalletConnectSdkHelper { } } + suspend fun signBnbTransaction(data: ByteArray, session: WalletConnectActiveData): String? { + val command = SignHashCommand( + hash = data, + walletPublicKey = session.wallet.walletPublicKey ?: return null, + derivationPath = session.wallet.derivationPath + ) + val result = tangemSdkManager.runTaskAsync(command, initialMessage = Message()) + return when (result) { + is CompletionResult.Success -> { + val key = session.wallet.derivedPublicKey + ?: session.wallet.walletPublicKey + getBnbResultString( + key.toHexString(), + result.data.signature.toHexString() + ) + } + is CompletionResult.Failure -> { + (result.error as? TangemSdkError)?.let { error -> + FirebaseAnalyticsHandler.logCardSdkError( + error, + FirebaseAnalyticsHandler.ActionToLog.WalletConnectTransaction, + ) + } + Timber.e(result.error.customMessage) + null + } + } + } + fun prepareDataForPersonalSign( message: WCEthereumSignMessage, session: WalletConnectSession, @@ -234,5 +269,8 @@ class WalletConnectSdkHelper { companion object { private const val ETH_MESSAGE_PREFIX = "\u0019Ethereum Signed Message:\n" private const val HEX_PREFIX = "0x" + fun getBnbResultString(publicKey: String, signature: String): String { + return "{\"signature\":\"$signature\",\"publicKey\":\"$publicKey\"}" + } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt index 1c19721743..f5f509cc74 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt @@ -4,6 +4,8 @@ import android.app.Activity import com.tangem.tap.common.redux.NotificationAction import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.wallet.R +import com.trustwallet.walletconnect.models.binance.WCBinanceTradeOrder +import com.trustwallet.walletconnect.models.binance.WCBinanceTransferOrder import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage import com.trustwallet.walletconnect.models.ethereum.WCEthereumTransaction import com.trustwallet.walletconnect.models.session.WCSession @@ -83,4 +85,20 @@ sealed class WalletConnectAction : Action { object NotifyCameraPermissionIsRequired : WalletConnectAction(), NotificationAction { override val messageResource = R.string.common_camera_denied_alert_message } + + object BinanceTransaction : WalletConnectAction() { + data class Trade( + val id: Long, val order: WCBinanceTradeOrder, + val sessionData: WalletConnectSession, + ) : WalletConnectAction() + + data class Transfer( + val id: Long, val order: WCBinanceTransferOrder, + val sessionData: WalletConnectSession, + ) : WalletConnectAction() + + data class Sign( + val id: Long, val data: ByteArray, val sessionData: WCSession, + ) : WalletConnectAction() + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt index 06043d4df2..92c6d69072 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt @@ -12,6 +12,7 @@ import com.tangem.tap.domain.TapWorkarounds.isTestCard import com.tangem.tap.domain.extensions.makeWalletManagerForApp import com.tangem.tap.domain.isMultiwalletAllowed import com.tangem.tap.domain.tasks.product.ScanResponse +import com.tangem.tap.domain.walletconnect.BnbHelper import com.tangem.tap.domain.walletconnect.WalletConnectManager import com.tangem.tap.domain.walletconnect.WalletConnectNetworkUtils import com.tangem.tap.features.wallet.redux.WalletAction @@ -136,6 +137,39 @@ class WalletConnectMiddleware { is WalletConnectAction.SignMessage -> { walletConnectManager.sendSignedMessage(action.session) } + is WalletConnectAction.BinanceTransaction.Trade -> { + val messageData = BnbHelper.createMessageData(action.order) + store.dispatchOnMain( + GlobalAction.ShowDialog( + WalletConnectDialog.BnbTransactionDialog( + data = messageData, + session = action.sessionData.session, + sessionId = action.id, + cardId = action.sessionData.wallet.cardId, + dAppName = action.sessionData.peerMeta.name + ) + ) + ) + } + is WalletConnectAction.BinanceTransaction.Transfer -> { + val messageData = BnbHelper.createMessageData(action.order) + store.dispatchOnMain( + GlobalAction.ShowDialog( + WalletConnectDialog.BnbTransactionDialog( + data = messageData, + session = action.sessionData.session, + sessionId = action.id, + cardId = action.sessionData.wallet.cardId, + dAppName = action.sessionData.peerMeta.name + ) + ) + ) + } + is WalletConnectAction.BinanceTransaction.Sign -> { + walletConnectManager.signBnb( + action.id, action.data, action.sessionData + ) + } } next(action) } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt index e2713b92d1..b00b7d9ff9 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt @@ -89,6 +89,12 @@ sealed class WalletConnectDialog : StateDialog { WalletConnectDialog() data class PersonalSign(val data: PersonalSignDialogData) : WalletConnectDialog() + data class BnbTransactionDialog(val data: BinanceMessageData, + val session: WCSession, + val sessionId: Long, + val cardId: String, + val dAppName: String + ) : WalletConnectDialog() } data class WcTransactionData( @@ -102,7 +108,7 @@ data class WcTransactionData( enum class WcTransactionType { EthSignTransaction, - EthSendTransaction, + EthSendTransaction } data class WcPersonalSignData( @@ -111,4 +117,29 @@ data class WcPersonalSignData( val id: Long, val dialogData: PersonalSignDialogData, - ) \ No newline at end of file + ) + +sealed class BinanceMessageData( + val address: String, + val data: ByteArray, +) { + class Trade( + val tradeData: List, + address: String, + data: ByteArray, + ) : BinanceMessageData(address, data) + + class Transfer( + val outputAddress: String, + val amount: String, + address: String, + data: ByteArray, + ) : BinanceMessageData(address, data) +} + +data class TradeData( + val price: String, + val quantity: String, + val amount: String, + val symbol: String +) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/BnbTransactionDialog.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/BnbTransactionDialog.kt new file mode 100644 index 0000000000..2ec39ca844 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/BnbTransactionDialog.kt @@ -0,0 +1,66 @@ +package com.tangem.tap.features.details.ui.walletconnect.dialogs + +import android.content.Context +import androidx.appcompat.app.AlertDialog +import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.features.details.redux.walletconnect.BinanceMessageData +import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction +import com.tangem.tap.store +import com.tangem.wallet.R +import com.trustwallet.walletconnect.models.session.WCSession + +class BnbTransactionDialog { + companion object { + fun create( + data: BinanceMessageData, + session: WCSession, + sessionId: Long, + cardId: String, + dAppName: String, + context: Context, + ): AlertDialog { + + val message = when (data) { + is BinanceMessageData.Trade -> data.tradeData.map { + context.getString( + R.string.wallet_connect_bnb_trade_order_message, + it.symbol, + it.price, + it.quantity, + it.amount + ) + }.joinToString ( "\n\n" ) + is BinanceMessageData.Transfer -> context.getString( + R.string.wallet_connect_bnb_transaction_message, + data.address, + data.outputAddress, + data.amount + ) + } + + val fullMessage = context.getString( + R.string.wallet_connect_bnb_sign_message, + dAppName, cardId, message + ) + val positiveButtonTitle = context.getText(R.string.common_sign) + + return AlertDialog.Builder(context).apply { + setTitle(context.getString(R.string.wallet_connect)) + setMessage(fullMessage) + setPositiveButton(positiveButtonTitle) { _, _ -> + store.dispatch(WalletConnectAction.BinanceTransaction.Sign( + id = sessionId, + data = data.data, + sessionData = session + )) + } + setNegativeButton(context.getText(R.string.common_reject)) { _, _ -> + store.dispatch(WalletConnectAction.RejectRequest(session, sessionId)) + } + setOnDismissListener { + store.dispatch(GlobalAction.HideDialog) + } + }.create() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt index ace2084261..b7444dcb16 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt @@ -132,7 +132,7 @@ class MultiWalletMiddleware { val walletManager = walletState?.getWalletManager(Blockchain.Ethereum) ?: walletFactory.makeWalletManagerForApp(scanResponse, Blockchain.Ethereum) - val tokenFinder = walletManager as TokenFinder + val tokenFinder = walletManager as? TokenFinder ?: return scope.launch { val result = tokenFinder.findTokens() withContext(Dispatchers.Main) { diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml index faad406cb3..ce001133db 100644 --- a/app/src/main/res/values/strings_untranslated.xml +++ b/app/src/main/res/values/strings_untranslated.xml @@ -294,4 +294,21 @@ Access code is available only for cards with a backup. + + + The BNB transaction has been successfully signed and sent to the Dapp + Dapp %s, requesting to\n +sign BNB transaction with\n +Card: %s\n +\n +%s + Transaction details:\n +From: %s\n +To: %s\n +Amount: %s + Trade order for %s\n +Price: %s\n +Amount to receive: %s\n +Amount to pay: %s + From 3276202ff1f973eb36f3747acd82eaf9a7a8aa3e Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 10 Jan 2022 10:59:24 +0300 Subject: [PATCH 03/20] Updated on 2026-08-14 --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index ff53e7b789..b35d59c395 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,7 +77,7 @@ dependencies { implementation 'com.google.android.play:core-ktx:1.8.1' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' - implementation 'com.tangem:blockchain:develop-52' + implementation 'com.tangem:blockchain:develop-53' implementation 'com.tangem.tangem-sdk-kotlin:core:develop-115' implementation 'com.tangem.tangem-sdk-kotlin:android:develop-115' From 64a31540c626fd76dc71d1f35bec4698cbf85ee6 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 11 Jan 2022 13:13:30 +0300 Subject: [PATCH 04/20] Updated on 2026-08-14 --- .../tap/domain/walletconnect/EthSignHelper.kt | 99 +++++++++++++++++++ .../walletconnect/WalletConnectManager.kt | 39 +++++++- .../walletconnect/WalletConnectSdkHelper.kt | 34 +++++-- 3 files changed, 160 insertions(+), 12 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/domain/walletconnect/EthSignHelper.kt diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/EthSignHelper.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/EthSignHelper.kt new file mode 100644 index 0000000000..fe8a593787 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/EthSignHelper.kt @@ -0,0 +1,99 @@ +package com.tangem.tap.domain.walletconnect + +import com.github.salomonbrys.kotson.fromJson +import com.google.gson.Gson +import com.google.gson.GsonBuilder +import com.google.gson.JsonArray +import com.google.gson.JsonParser +import com.google.gson.annotations.SerializedName +import com.trustwallet.walletconnect.JSONRPC_VERSION +import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage + +class EthSignHelper { + companion object { + private val gson: Gson by lazy { + GsonBuilder() + .setPrettyPrinting() + .serializeNulls() + .create() + } + + fun tryToParseEthTypedMessage(data: String): WCEthereumSignMessage? { + val request = + gson.fromJson(data) + return if (request.method == WCMethodExtended.ETH_SIGN_TYPE_DATA_V4) { + WCEthereumSignMessage( + listOf( + request.params[0].asString, + request.params[1].asString, + ), + WCEthereumSignMessage.WCSignType.TYPED_MESSAGE + ) + } else { + null + } + } + + fun tryToParseEthTypedMessageString(message: String): String? { + return try { + val messageString = message + .replace("\\", "") + .removePrefix("\"") + .removeSuffix("\"") + val messageJson = JsonParser().parse(messageString) + val filteredMap = gson.fromJson>(messageJson) + .filterKeys { it == "domain" || it == "message"} + + gson.toJson(filteredMap) + } catch (exception: Exception) { + null + } + + } + } +} + +data class CustomJsonRpcRequests( + val id: Long, + val jsonrpc: String = JSONRPC_VERSION, + val method: WCMethodExtended?, + val params: JsonArray +) + +enum class WCMethodExtended { + @SerializedName("wc_sessionRequest") + SESSION_REQUEST, + + @SerializedName("wc_sessionUpdate") + SESSION_UPDATE, + + @SerializedName("eth_sign") + ETH_SIGN, + + @SerializedName("personal_sign") + ETH_PERSONAL_SIGN, + + @SerializedName("eth_signTypedData") + ETH_SIGN_TYPE_DATA, + + @SerializedName("eth_signTypedData_v4") + ETH_SIGN_TYPE_DATA_V4, + + @SerializedName("eth_signTransaction") + ETH_SIGN_TRANSACTION, + + @SerializedName("eth_sendTransaction") + ETH_SEND_TRANSACTION, + + @SerializedName("bnb_sign") + BNB_SIGN, + + @SerializedName("bnb_tx_confirmation") + BNB_TRANSACTION_CONFIRM, + + @SerializedName("get_accounts") + GET_ACCOUNTS, + + @SerializedName("trust_signTransaction") + SIGN_TRANSACTION; +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt index c5e727e23b..2a071fa5ce 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt @@ -11,10 +11,7 @@ import com.tangem.tap.store import com.tangem.tap.walletConnectRepository import com.trustwallet.walletconnect.WCClient import com.trustwallet.walletconnect.models.WCPeerMeta -import com.trustwallet.walletconnect.models.binance.WCBinanceCancelOrder -import com.trustwallet.walletconnect.models.binance.WCBinanceTradeOrder -import com.trustwallet.walletconnect.models.binance.WCBinanceTransferOrder -import com.trustwallet.walletconnect.models.binance.WCBinanceTxConfirmParam +import com.trustwallet.walletconnect.models.binance.* import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage import com.trustwallet.walletconnect.models.ethereum.WCEthereumTransaction import com.trustwallet.walletconnect.models.session.WCSession @@ -26,6 +23,17 @@ import okhttp3.logging.HttpLoggingInterceptor import timber.log.Timber import java.util.* import java.util.concurrent.TimeUnit +import kotlin.collections.MutableMap +import kotlin.collections.filter +import kotlin.collections.first +import kotlin.collections.forEach +import kotlin.collections.get +import kotlin.collections.listOf +import kotlin.collections.map +import kotlin.collections.mutableMapOf +import kotlin.collections.set +import kotlin.collections.toMap +import kotlin.collections.toMutableMap class WalletConnectManager { @@ -402,6 +410,29 @@ class WalletConnectManager { onSessionClosed(session) } } + client.onCustomRequest = { id: Long, data: String -> + Timber.d("Custom Request") + Timber.d(data) + + val message = EthSignHelper.tryToParseEthTypedMessage(data) + if (message != null) { + Timber.d("onEthSign_v4: $message") + FirebaseAnalyticsHandler.logWcEvent( + FirebaseAnalyticsHandler.WcAnalyticsEvent.Action( + FirebaseAnalyticsHandler.WcAction.PersonalSign + ) + ) + sessions[client.session]?.toWalletConnectSession()?.let { sessionData -> + store.dispatchOnMain( + WalletConnectAction.HandlePersonalSignRequest( + message, + sessionData, + id + ) + ) + } + } + } } companion object { diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt index 74f1abc2b0..ad8b9084be 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt @@ -212,8 +212,15 @@ class WalletConnectSdkHelper { session: WalletConnectSession, id: Long, ): WcPersonalSignData { - val messageData = message.data.removePrefix(HEX_PREFIX).hexToBytes() - val messageString = message.data.hexToAscii() ?: message.data + val messageData = + try { + message.data.removePrefix(HEX_PREFIX).hexToBytes() + } catch (exception: Exception) { + message.data.asciiToHex()!!.hexToBytes() + } + val messageString = message.data.hexToAscii() + ?: EthSignHelper.tryToParseEthTypedMessageString(message.data) + ?: message.data val prefixData = (ETH_MESSAGE_PREFIX + messageData.size.toString()).toByteArray() val hashToSign = (prefixData + messageData).toKeccak() @@ -235,12 +242,23 @@ class WalletConnectSdkHelper { } private fun String.hexToAscii(): String? { - return removePrefix(HEX_PREFIX).hexToBytes() - .map { - val char = it.toInt().toChar() - if (char.isAscii()) char else return null - } - .joinToString("") + return try { + removePrefix(HEX_PREFIX).hexToBytes() + .map { + val char = it.toInt().toChar() + if (char.isAscii()) char else return null + } + .joinToString("") + } catch (exception: Exception) { + return null + } + } + + private fun String.asciiToHex(): String? { + return map { + if (!it.isAscii()) return null + Integer.toHexString(it.code) + }.joinToString("") } suspend fun signPersonalMessage(hashToSign: ByteArray, wallet: WalletForSession): String? { From f31f68b8790274380e078a560aa0d3e5682e20e8 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 21 Jan 2022 17:02:23 +0300 Subject: [PATCH 05/20] Updated on 2026-08-14 --- .../features/onboarding/OnboardingManager.kt | 19 ---------------- .../wallet/redux/OnboardingWalletAction.kt | 3 +-- .../redux/OnboardingWalletMiddleware.kt | 17 +++++++++----- .../wallet/redux/OnboardingWalletReducer.kt | 2 +- .../wallet/redux/OnboardingWalletState.kt | 3 +-- .../wallet/ui/OnboardingWalletFragment.kt | 17 +++++++++++++- .../res/layout/fragment_onboarding_wallet.xml | 22 ++++++++++++++----- 7 files changed, 46 insertions(+), 37 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingManager.kt b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingManager.kt index 3aa671c5c3..9c3037a33c 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingManager.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingManager.kt @@ -37,25 +37,6 @@ class OnboardingManager( return scanResponse.card.getOrLoadCardArtworkUrl(cardInfo) } -// suspend fun loadArtwork(): Artwork { -// cardInfo = OnlineCardVerifier().getCardInfo(scanResponse.card.cardId, scanResponse.card.cardPublicKey) -// return loadArtwork(scanResponse.card.getOrLoadCardArtworkUrl(cardInfo)) -// } -// -// suspend fun loadArtwork(url: String): Artwork { -// return withContext(Dispatchers.Main) { -// suspendCoroutine { continuation -> -// UrlBitmapLoader().loadBitmap(url) { -// val result = when (it) { -// is Result.Success -> Artwork(url, it.data) -// is Result.Failure -> Artwork(url, null) -// } -// continuation.resume(result) -// } -// } -// } -// } - suspend fun updateBalance(walletManager: WalletManager): OnboardingWalletBalance { val balance = when (val result = walletManager.safeUpdate()) { is Result.Success -> { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt index 9bfdaaf381..57ede6fd8a 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt @@ -1,7 +1,6 @@ package com.tangem.tap.features.onboarding.products.wallet.redux import android.graphics.Bitmap -import com.tangem.tap.features.wallet.redux.Artwork import org.rekotlin.Action sealed class OnboardingWalletAction : Action { @@ -14,7 +13,7 @@ sealed class OnboardingWalletAction : Action { object ProceedBackup : OnboardingWalletAction() object LoadArtwork : OnboardingWalletAction() - class SetArtworkUrl(val artwork: Artwork) : OnboardingWalletAction() + class SetArtworkUrl(val artworkUrl: String?) : OnboardingWalletAction() object OnBackPressed : OnboardingWalletAction() } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt index 1816465f6e..cdccf7265a 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt @@ -14,8 +14,8 @@ import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.extensions.hasWallets import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.features.home.redux.HomeAction -import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteAction import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletMiddleware.Companion.BUY_WALLET_URL +import com.tangem.tap.features.wallet.redux.Artwork import kotlinx.coroutines.launch import org.rekotlin.Action import org.rekotlin.Middleware @@ -61,11 +61,16 @@ private fun handleWalletAction(action: Action) { } store.dispatch(action) } - is OnboardingNoteAction.LoadCardArtwork -> { -// scope.launch { -// val artwork = onboardingManager.loadArtwork() -// store.dispatchOnMain(OnboardingWalletAction.SetArtworkUrl(artwork)) -// } + is OnboardingWalletAction.LoadArtwork -> { + scope.launch { + val artwork = onboardingManager?.loadArtworkUrl() + val cardArtwork = if (artwork == Artwork.DEFAULT_IMG_URL) { + null + } else { + artwork + } + store.dispatchOnMain(OnboardingWalletAction.SetArtworkUrl(cardArtwork)) + } } is OnboardingWalletAction.CreateWallet -> { scope.launch { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt index 89542c532e..ce0c07b3c6 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt @@ -24,7 +24,7 @@ private fun internalReduce(action: Action, appState: AppState): OnboardingWallet OnboardingWalletState(step = OnboardingWalletStep.CreateWallet) is OnboardingWalletAction.ProceedBackup -> state.copy(step = OnboardingWalletStep.Backup) is OnboardingWalletAction.SetArtworkUrl -> { - state.copy(artwork = action.artwork) + state.copy(cardArtworkUrl = action.artworkUrl) } is OnboardingWalletAction.Done -> state.copy(step = OnboardingWalletStep.Done) else -> state diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt index a048261a6d..7aaa0fe88f 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt @@ -2,7 +2,6 @@ package com.tangem.tap.features.onboarding.products.wallet.redux import android.graphics.Bitmap import com.tangem.tap.common.redux.StateDialog -import com.tangem.tap.features.wallet.redux.Artwork import org.rekotlin.StateType /** @@ -11,7 +10,7 @@ import org.rekotlin.StateType data class OnboardingWalletState( val step: OnboardingWalletStep = OnboardingWalletStep.None, val backupState: BackupState = BackupState(), - val artwork: Artwork? = null, + val cardArtworkUrl: String? = null, val showConfetti: Boolean = false, ) : StateType { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt index 5a1ccf6684..b550b82a5b 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt @@ -2,12 +2,14 @@ package com.tangem.tap.features.onboarding.products.wallet.ui import android.os.Bundle import android.view.* +import android.widget.ImageView import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment import androidx.transition.TransitionInflater import androidx.transition.TransitionManager import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.tabs.TabLayoutMediator +import com.squareup.picasso.Picasso import com.tangem.common.CardIdFormatter import com.tangem.common.core.CardIdDisplayFormat import com.tangem.tap.common.extensions.dispatchOpenUrl @@ -62,6 +64,8 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet), store.dispatch(OnboardingWalletAction.Init) toolbar.setNavigationOnClickListener { activity?.onBackPressed() } + store.dispatch(OnboardingWalletAction.LoadArtwork) + addBackPressHandler(this) } @@ -84,7 +88,10 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet), requireActivity().invalidateOptionsMenu() - state.artwork?.artwork?.let { imv_front_card.setImageBitmap(it) } + loadImageIntoImageView(state.cardArtworkUrl, imv_front_card) + loadImageIntoImageView(state.cardArtworkUrl, imv_first_backup_card) + loadImageIntoImageView(state.cardArtworkUrl, imv_second_backup_card) + pb_state.max = 6 pb_state.progress = state.getProgressStep() @@ -99,6 +106,14 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet), } } + private fun loadImageIntoImageView(url: String?, view: ImageView) { + Picasso.get() + .load(url) + .error(R.drawable.card_placeholder_black) + .placeholder(R.drawable.card_placeholder_black) + ?.into(view) + } + private fun setupCreateWalletState() { btn_main_action.setText(R.string.onboarding_create_wallet_button_create_wallet) btn_main_action.setOnClickListener { store.dispatch(OnboardingWalletAction.CreateWallet) } diff --git a/app/src/main/res/layout/fragment_onboarding_wallet.xml b/app/src/main/res/layout/fragment_onboarding_wallet.xml index 7f004c814d..e29397b2bb 100644 --- a/app/src/main/res/layout/fragment_onboarding_wallet.xml +++ b/app/src/main/res/layout/fragment_onboarding_wallet.xml @@ -78,28 +78,38 @@ From 37c26e919298f73d03cf75bc57d53678ac83fe52 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 26 Jan 2022 10:37:57 +0300 Subject: [PATCH 06/20] Updated on 2026-08-14 --- app/build.gradle | 3 + app/src/main/AndroidManifest.xml | 4 +- app/src/main/assets/tangem-app-config | 2 +- .../java/com/tangem/tap/TapApplication.kt | 18 ++- .../tap/common/analytics/AnalyticsEvent.kt | 54 ++++++++ .../tap/common/analytics/AnalyticsHandler.kt | 71 ++++++++++- .../analytics/AppsFlyerAnalyticsHandler.kt | 33 +++++ .../analytics/FirebaseAnalyticsHandler.kt | 119 +++--------------- .../analytics/GlobalAnalyticsHandler.kt | 41 ++++++ .../com/tangem/tap/common/extensions/Store.kt | 4 +- .../tap/common/redux/global/GlobalAction.kt | 2 + .../common/redux/global/GlobalMidlleware.kt | 4 +- .../tap/common/redux/global/GlobalReducer.kt | 2 + .../tap/common/redux/global/GlobalState.kt | 2 + .../com/tangem/tap/domain/TangemSdkManager.kt | 27 ++-- .../com/tangem/tap/domain/TapWalletManager.kt | 7 +- .../configurable/config/ConfigManager.kt | 7 +- .../domain/configurable/config/JsonModels.kt | 1 + .../tap/domain/twins/TwinCardsManager.kt | 21 ++-- .../walletconnect/WalletConnectManager.kt | 48 +++---- .../walletconnect/WalletConnectSdkHelper.kt | 18 +-- .../details/redux/DetailsMiddleware.kt | 13 +- .../tap/features/home/redux/HomeMiddleware.kt | 11 +- .../twins/redux/TwinCardsMiddleware.kt | 6 +- .../redux/OnboardingWalletMiddleware.kt | 11 +- .../wallet/ui/OnboardingWalletFragment.kt | 4 +- .../send/redux/middlewares/SendMiddleware.kt | 11 +- .../redux/middlewares/WalletMiddleware.kt | 8 +- .../redux/middlewares/WarningsMiddleware.kt | 3 +- .../ui/adapters/WarningMessagesAdapter.kt | 8 +- dependencies.gradle | 4 +- 31 files changed, 359 insertions(+), 208 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/common/analytics/AppsFlyerAnalyticsHandler.kt create mode 100644 app/src/main/java/com/tangem/tap/common/analytics/GlobalAnalyticsHandler.kt diff --git a/app/build.gradle b/app/build.gradle index b35d59c395..e362e0e86d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -110,6 +110,9 @@ dependencies { implementation 'com.google.firebase:firebase-config-ktx' implementation 'com.google.firebase:firebase-analytics-ktx' + // AppsFlyer + implementation 'com.appsflyer:af-android-sdk:6.5.1' + // WalletConnect implementation 'com.github.salomonbrys.kotson:kotson:2.5.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f3c400a3ec..f358215715 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -15,6 +15,7 @@ + @@ -35,7 +36,8 @@ android:roundIcon="@mipmap/ic_launcher" android:supportsRtl="true" android:theme="@style/AppTheme" - tools:ignore="GoogleAppIndexingWarning"> + tools:ignore="GoogleAppIndexingWarning" + tools:replace="android:fullBackupContent"> = emptyMap() + ) + + abstract fun logCardSdkError( error: TangemSdkError, - actionToLog: FirebaseAnalyticsHandler.ActionToLog, - parameters: Map? = null, + actionToLog: Analytics.ActionToLog, + parameters: Map? = null, card: Card? = null ) + + abstract fun logError( + error: Throwable, + params: Map = emptyMap() + ) + + protected fun prepareParams(card: Card?, blockchain: String? = null): Map { + return mapOf( + AnalyticsParam.FIRMWARE.param to card?.firmwareVersion?.stringValue, + AnalyticsParam.BATCH_ID.param to card?.batchId, + AnalyticsParam.BLOCKCHAIN.param to blockchain, + ).filterNotNull() + } + + fun logWcEvent(event: Analytics.WcAnalyticsEvent) { + when (event) { + is Analytics.WcAnalyticsEvent.Action -> { + triggerEvent( + event = AnalyticsEvent.WC_SUCCESS_RESPONSE, + params = mapOf( + AnalyticsParam.WALLET_CONNECT_ACTION.param to event.action.name + ) + ) + } + is Analytics.WcAnalyticsEvent.Error -> { + val params = mapOf( + AnalyticsParam.WALLET_CONNECT_ACTION.param to event.action?.name, + AnalyticsParam.ERROR_DESCRIPTION.param to event.error.message + ) + .filterNotNull() + logError(event.error, params) + } + is Analytics.WcAnalyticsEvent.InvalidRequest -> + triggerEvent( + event = AnalyticsEvent.WC_INVALID_REQUEST, + params = mapOf( + AnalyticsParam.WALLET_CONNECT_REQUEST.param to event.json + ).filterNotNull() + ) + is Analytics.WcAnalyticsEvent.Session -> { + val analyticsEvent = when (event.event) { + Analytics.WcSessionEvent.Disconnect -> AnalyticsEvent.WC_SESSION_DISCONNECTED + Analytics.WcSessionEvent.Connect -> AnalyticsEvent.WC_NEW_SESSION + } + triggerEvent( + event = analyticsEvent, + params = mapOf( + AnalyticsParam.WALLET_CONNECT_DAPP_URL.param to event.url + ).filterNotNull() + ) + } + } + + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/AppsFlyerAnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/AppsFlyerAnalyticsHandler.kt new file mode 100644 index 0000000000..9692defe86 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/analytics/AppsFlyerAnalyticsHandler.kt @@ -0,0 +1,33 @@ +package com.tangem.tap.common.analytics + +import android.app.Application +import com.appsflyer.AppsFlyerLib +import com.tangem.common.card.Card +import com.tangem.common.core.TangemSdkError + +class AppsFlyerAnalyticsHandler(val context: Application): AnalyticsHandler() { + + override fun triggerEvent( + event: AnalyticsEvent, + card: Card?, + blockchain: String?, + params: Map + ) { + AppsFlyerLib.getInstance().logEvent(context, + event.event, prepareParams(card, blockchain)) + } + + override fun logCardSdkError( + error: TangemSdkError, + actionToLog: Analytics.ActionToLog, + parameters: Map?, + card: Card? + ) { + //TODO + } + + override fun logError(error: Throwable, params: Map) { + //TODO + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/FirebaseAnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/FirebaseAnalyticsHandler.kt index 33383e1089..106c07aae9 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/FirebaseAnalyticsHandler.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/FirebaseAnalyticsHandler.kt @@ -7,11 +7,16 @@ import com.google.firebase.crashlytics.FirebaseCrashlytics import com.google.firebase.ktx.Firebase import com.tangem.common.card.Card import com.tangem.common.core.TangemSdkError -import com.tangem.tap.common.extensions.filterNotNull -object FirebaseAnalyticsHandler : AnalyticsHandler { - override fun triggerEvent(event: AnalyticsEvent, card: Card?, blockchain: String?) { - Firebase.analytics.logEvent(event.event, setData(card, blockchain)) +object FirebaseAnalyticsHandler : AnalyticsHandler() { + override fun triggerEvent( + event: AnalyticsEvent, + card: Card?, + blockchain: String?, + params: Map + ) { + val fullParams = prepareParams(card, blockchain) + params + Firebase.analytics.logEvent(event.event, fullParams.toBundle()) } fun logException(name: String, throwable: Throwable) { @@ -22,16 +27,20 @@ object FirebaseAnalyticsHandler : AnalyticsHandler { )) } + private fun Map.toBundle(): Bundle { + return bundleOf(*this.toList().toTypedArray()) + } + override fun logCardSdkError( error: TangemSdkError, - actionToLog: ActionToLog, + actionToLog: Analytics.ActionToLog, parameters: Map?, card: Card?, ) { if (error is TangemSdkError.UserCancelled) return val params = parameters?.toMutableMap() ?: mutableMapOf() - if (card != null) params + getParamsFromCard(card) + if (card != null) params + prepareParams(card) params[AnalyticsParam.ACTION] = actionToLog.key params[AnalyticsParam.ERROR_CODE] = error.code.toString() params[AnalyticsParam.ERROR_DESCRIPTION] = error.javaClass.simpleName @@ -44,100 +53,8 @@ object FirebaseAnalyticsHandler : AnalyticsHandler { FirebaseCrashlytics.getInstance().recordException(cardError) } - private fun getParamsFromCard(card: Card): Map { - return mapOf( - AnalyticsParam.FIRMWARE to card.firmwareVersion.stringValue, - AnalyticsParam.BATCH_ID to card.batchId - ).filterNotNull() + override fun logError(error: Throwable, params: Map) { + params.forEach { (key, value) -> FirebaseCrashlytics.getInstance().setCustomKey(key, value) } + FirebaseCrashlytics.getInstance().recordException(error) } - - private fun setData(card: Card?, blockchain: String?): Bundle { - if (card == null) return bundleOf() - return bundleOf( - AnalyticsParam.BLOCKCHAIN.param to (blockchain), - AnalyticsParam.BATCH_ID.param to card.batchId, - AnalyticsParam.FIRMWARE.param to card.firmwareVersion.stringValue - ) - } - - fun logWcEvent(event: WcAnalyticsEvent) { - when (event) { - is WcAnalyticsEvent.Action -> { - - Firebase.analytics.logEvent( - AnalyticsEvent.WC_SUCCESS_RESPONSE.event, bundleOf( - AnalyticsParam.WALLET_CONNECT_ACTION.param to event.action.name - ) - ) - } - is WcAnalyticsEvent.Error -> { - mapOf( - AnalyticsParam.WALLET_CONNECT_ACTION to event.action?.name, - AnalyticsParam.ERROR_DESCRIPTION to event.error.message - ) - .filterNotNull() - .forEach { - FirebaseCrashlytics.getInstance().setCustomKey(it.key.param, it.value) - } - FirebaseCrashlytics.getInstance().recordException(event.error) - } - is WcAnalyticsEvent.InvalidRequest -> - Firebase.analytics.logEvent( - AnalyticsEvent.WC_INVALID_REQUEST.event, bundleOf( - AnalyticsParam.WALLET_CONNECT_REQUEST.param to event.json - ) - ) - is WcAnalyticsEvent.Session -> { - val analyticsEvent = when (event.event) { - WcSessionEvent.Disconnect -> AnalyticsEvent.WC_SESSION_DISCONNECTED - WcSessionEvent.Connect -> AnalyticsEvent.WC_NEW_SESSION - } - Firebase.analytics.logEvent( - analyticsEvent.event, bundleOf( - AnalyticsParam.WALLET_CONNECT_DAPP_URL.param to event.url - ) - ) - } - } - - } - - enum class AnalyticsParam(val param: String) { - BLOCKCHAIN("blockchain"), - BATCH_ID("batch_id"), - FIRMWARE("firmware"), - ACTION("action"), - ERROR_DESCRIPTION("error_description"), - ERROR_CODE("error_code"), - NEW_SECURITY_OPTION("new_security_option"), - ERROR_KEY("Tangem SDK error key"), - WALLET_CONNECT_ACTION("wallet_connect_action"), - WALLET_CONNECT_REQUEST("wallet_connect_request"), - WALLET_CONNECT_DAPP_URL("wallet_connect_dapp_url"), - } - - enum class ActionToLog(val key: String) { - Scan("tap_scan_task"), - SendTransaction("send_transaction"), - WalletConnectSign("wallet_connect_personal_sign"), - WalletConnectTransaction("wallet_connect_tx_sign"), - ReadPinSettings("read_pin_settings"), - ChangeSecOptions("change_sec_options"), - CreateWallet("create_wallet"), - PurgeWallet("purge_wallet"), - WriteIssuerData("write_issuer_data"), - } - - enum class WcSessionEvent { Disconnect, Connect } - - sealed class WcAnalyticsEvent { - data class Error(val error: Throwable, val action: WcAction?) : - WcAnalyticsEvent() - - data class Session(val event: WcSessionEvent, val url: String?) : WcAnalyticsEvent() - data class Action(val action: WcAction) : WcAnalyticsEvent() - data class InvalidRequest(val json: String?) : WcAnalyticsEvent() - } - - enum class WcAction { PersonalSign, SignTransaction, SendTransaction } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/GlobalAnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/GlobalAnalyticsHandler.kt new file mode 100644 index 0000000000..da77c37e05 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/analytics/GlobalAnalyticsHandler.kt @@ -0,0 +1,41 @@ +package com.tangem.tap.common.analytics + +import android.app.Application +import com.tangem.common.card.Card +import com.tangem.common.core.TangemSdkError + +class GlobalAnalyticsHandler(val analyticsHandlers: List) : + AnalyticsHandler() { + override fun triggerEvent( + event: AnalyticsEvent, + card: Card?, + blockchain: String?, + params: Map + ) { + analyticsHandlers.forEach { it.triggerEvent(event, card, blockchain) } + } + + override fun logCardSdkError( + error: TangemSdkError, + actionToLog: Analytics.ActionToLog, + parameters: Map?, + card: Card? + ) { + analyticsHandlers.forEach { it.logCardSdkError(error, actionToLog, parameters, card) } + } + + override fun logError(error: Throwable, params: Map) { + analyticsHandlers.forEach { it.logError(error, params) } + } + + companion object { + fun createDefaultAnalyticHandlers(context: Application): GlobalAnalyticsHandler { + return GlobalAnalyticsHandler( + listOf( + FirebaseAnalyticsHandler, + AppsFlyerAnalyticsHandler(context) + ) + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Store.kt b/app/src/main/java/com/tangem/tap/common/extensions/Store.kt index 83aa152f76..678caeb9f0 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Store.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Store.kt @@ -18,8 +18,8 @@ fun Store<*>.dispatchOnMain(action: Action) { } } -suspend fun Store<*>.onCardScanned(scanResponse: ScanResponse, addAnalytics: Boolean = true) { - store.state.globalState.tapWalletManager.onCardScanned(scanResponse, addAnalytics) +suspend fun Store<*>.onCardScanned(scanResponse: ScanResponse) { + store.state.globalState.tapWalletManager.onCardScanned(scanResponse) } fun Store<*>.dispatchOpenUrl(url: String) { diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt index bbea6e43f9..5f0db04ae5 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt @@ -3,6 +3,7 @@ package com.tangem.tap.common.redux.global import com.tangem.blockchain.common.WalletManager import com.tangem.common.CompletionResult import com.tangem.common.core.TangemError +import com.tangem.tap.common.analytics.GlobalAnalyticsHandler import com.tangem.tap.common.redux.* import com.tangem.tap.domain.TapError import com.tangem.tap.domain.configurable.config.ConfigManager @@ -69,6 +70,7 @@ sealed class GlobalAction : Action { data class SetConfigManager(val configManager: ConfigManager) : GlobalAction() data class SetWarningManager(val warningManager: WarningMessagesManager) : GlobalAction() data class SetFeedbackManager(val feedbackManager: FeedbackManager) : GlobalAction() + data class SetAnanlyticHandlers(val analyticsHandlers: GlobalAnalyticsHandler) : GlobalAction() data class SendFeedback(val emailData: EmailData) : GlobalAction() data class UpdateFeedbackInfo(val walletManagers: List) : GlobalAction() diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt index b16fbe21c7..6200feb947 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt @@ -4,7 +4,6 @@ import com.tangem.common.CompletionResult import com.tangem.common.core.TangemSdkError import com.tangem.common.services.Result import com.tangem.tap.* -import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler import com.tangem.tap.common.extensions.dispatchDialogShow import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.withMainContext @@ -89,9 +88,8 @@ private val globalMiddlewareHandler: Middleware = { dispatch, appState is GlobalAction.ScanCard -> { scope.launch { val result = tangemSdkManager.scanProduct( - FirebaseAnalyticsHandler, + store.state.globalState.analyticsHandlers, currenciesRepository, - action.shouldDeriveWC, action.messageResId ) withMainContext { diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt index e549a2f386..94621117bc 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt @@ -46,6 +46,8 @@ fun globalReducer(action: Action, state: AppState): GlobalState { globalState.copy(configManager = action.configManager) } is GlobalAction.SetWarningManager -> globalState.copy(warningManager = action.warningManager) + is GlobalAction.SetAnanlyticHandlers -> + globalState.copy(analyticsHandlers = action.analyticsHandlers) is GlobalAction.UpdateWalletSignedHashes -> { val card = globalState.scanResponse?.card ?: return globalState val wallet = card.wallet(action.walletPublicKey) ?: return globalState diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt index f634a6a0ff..c607125cb8 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt @@ -1,5 +1,6 @@ package com.tangem.tap.common.redux.global +import com.tangem.tap.common.analytics.AnalyticsHandler import com.tangem.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.domain.PayIdManager @@ -28,6 +29,7 @@ data class GlobalState( val dialog: StateDialog? = null, val moonpayStatus: MoonpayStatus? = null, val resources: AndroidResources = AndroidResources(), + val analyticsHandlers: AnalyticsHandler? = null, ) : StateType diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt index 4c3f0c78d4..0daf937bc0 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -19,9 +19,9 @@ import com.tangem.operations.CommandResponse import com.tangem.operations.pins.CheckUserCodesCommand import com.tangem.operations.pins.CheckUserCodesResponse import com.tangem.operations.pins.SetUserCodeCommand +import com.tangem.tap.common.analytics.Analytics import com.tangem.tap.common.analytics.AnalyticsEvent import com.tangem.tap.common.analytics.AnalyticsHandler -import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler import com.tangem.tap.common.extensions.ByteArrayKey import com.tangem.tap.domain.tasks.CreateWalletAndRescanTask import com.tangem.tap.domain.tasks.DerivationTask @@ -39,12 +39,11 @@ import kotlin.coroutines.suspendCoroutine class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Context) { suspend fun scanProduct( - analyticsHandler: AnalyticsHandler, + analyticsHandler: AnalyticsHandler?, currenciesRepository: CurrenciesRepository, - shouldDeriveWC: Boolean, messageRes: Int? = null, ): CompletionResult { - analyticsHandler.triggerEvent(AnalyticsEvent.READY_TO_SCAN, null) + analyticsHandler?.triggerEvent(AnalyticsEvent.READY_TO_SCAN, null) val message = Message(context.getString(messageRes ?: R.string.initial_message_scan_header)) return runTaskAsyncReturnOnMain(ScanProductTask(null, currenciesRepository), null, message) @@ -62,13 +61,23 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co } private fun sendScanFailuresToAnalytics( - analyticsHandler: AnalyticsHandler, + analyticsHandler: AnalyticsHandler?, result: CompletionResult ) { - if (result is CompletionResult.Failure && result.error is TangemSdkError) { - (result.error as? TangemSdkError)?.let { error -> - analyticsHandler.logCardSdkError(error, FirebaseAnalyticsHandler.ActionToLog.Scan) - } + when (result) { + is CompletionResult.Success -> + analyticsHandler?.triggerEvent( + event = AnalyticsEvent.CARD_IS_SCANNED, + card = result.data.card, + blockchain = result.data.walletData?.blockchain + ) + is CompletionResult.Failure -> + (result.error as? TangemSdkError)?.let { error -> + analyticsHandler?.logCardSdkError( + error = error, + actionToLog = Analytics.ActionToLog.Scan + ) + } } } diff --git a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt index 9f6d0f13e1..cfd628a093 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -2,8 +2,6 @@ package com.tangem.tap.domain import com.tangem.blockchain.common.* import com.tangem.common.services.Result -import com.tangem.tap.common.analytics.AnalyticsEvent -import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler import com.tangem.tap.common.extensions.dispatchDebugErrorNotification import com.tangem.tap.common.extensions.safeUpdate import com.tangem.tap.common.redux.global.FiatCurrencyName @@ -71,10 +69,7 @@ class TapWalletManager { handleFiatRatesResult(results) } - suspend fun onCardScanned(data: ScanResponse, addAnalyticsEvent: Boolean = false) { - if (addAnalyticsEvent) { - FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.CARD_IS_SCANNED, data.card, data.walletData?.blockchain) - } + suspend fun onCardScanned(data: ScanResponse) { store.state.globalState.feedbackManager?.infoHolder?.setCardInfo(data.card) updateConfigManager(data) diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt b/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt index cae1861582..412537befb 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt @@ -11,6 +11,7 @@ data class Config( val coinMarketCapKey: String = "f6622117-c043-47a0-8975-9d673ce484de", val moonPayApiKey: String = "pk_test_kc90oYTANy7UQdBavDKGfL4K9l6VEPE", val moonPayApiSecretKey: String = "sk_test_V8w4M19LbDjjYOt170s0tGuvXAgyEb1C", + val appsFlyerDevKey: String = "", val blockchainSdkConfig: BlockchainSdkConfig = BlockchainSdkConfig(), val isSendingToPayIdEnabled: Boolean = true, val isTopUpEnabled: Boolean = false, @@ -84,7 +85,8 @@ class ConfigManager( blockchairAuthorizationToken = values.blockchairAuthorizationToken, blockcypherTokens = values.blockcypherTokens, infuraProjectId = values.infuraProjectId - ) + ), + appsFlyerDevKey = values.appsFlyerDevKey, ) defaultConfig = defaultConfig.copy( coinMarketCapKey = values.coinMarketCapKey, @@ -95,7 +97,8 @@ class ConfigManager( blockchairAuthorizationToken = values.blockchairAuthorizationToken, blockcypherTokens = values.blockcypherTokens, infuraProjectId = values.infuraProjectId - ) + ), + appsFlyerDevKey = values.appsFlyerDevKey, ) } diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt b/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt index c71a4a16dd..edbbf6345c 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt @@ -19,6 +19,7 @@ class ConfigValueModel( val blockchairAuthorizationToken: String?, val blockcypherTokens: Set?, val infuraProjectId: String?, + val appsFlyerDevKey: String, ) class ConfigModel(val features: FeatureModel?, val configValues: ConfigValueModel?) { diff --git a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt index 861f3db801..53bd278cc7 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt @@ -11,12 +11,17 @@ import com.tangem.common.core.TangemSdkError import com.tangem.common.extensions.hexToBytes import com.tangem.common.extensions.toHexString import com.tangem.operations.wallet.CreateWalletResponse -import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler +import com.tangem.tap.common.analytics.Analytics +import com.tangem.tap.common.analytics.AnalyticsHandler import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.network.createMoshi import com.tangem.tap.tangemSdkManager -class TwinCardsManager(private val card: Card, assetReader: AssetReader) { +class TwinCardsManager( + private val card: Card, + assetReader: AssetReader, + val analyticsHandler: AnalyticsHandler? +) { private val currentCardId: String = card.cardId @@ -31,9 +36,9 @@ class TwinCardsManager(private val card: Card, assetReader: AssetReader) { is CompletionResult.Success -> currentCardPublicKey = response.data.wallet.publicKey.toHexString() is CompletionResult.Failure -> { (response.error as? TangemSdkError)?.let { error -> - FirebaseAnalyticsHandler.logCardSdkError( + analyticsHandler?.logCardSdkError( error, - FirebaseAnalyticsHandler.ActionToLog.CreateWallet, + Analytics.ActionToLog.CreateWallet, card = card ) } @@ -61,9 +66,9 @@ class TwinCardsManager(private val card: Card, assetReader: AssetReader) { } is CompletionResult.Failure -> { (response.error as? TangemSdkError)?.let { error -> - FirebaseAnalyticsHandler.logCardSdkError( + analyticsHandler?.logCardSdkError( error, - FirebaseAnalyticsHandler.ActionToLog.CreateWallet, + Analytics.ActionToLog.CreateWallet, card = card ) } @@ -81,9 +86,9 @@ class TwinCardsManager(private val card: Card, assetReader: AssetReader) { is CompletionResult.Success -> Result.Success(response.data) is CompletionResult.Failure -> { (response.error as? TangemSdkError)?.let { error -> - FirebaseAnalyticsHandler.logCardSdkError( + analyticsHandler?.logCardSdkError( error, - FirebaseAnalyticsHandler.ActionToLog.WriteIssuerData, + Analytics.ActionToLog.WriteIssuerData, card = card ) } diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt index 2a071fa5ce..49510ff791 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt @@ -2,7 +2,7 @@ package com.tangem.tap.domain.walletconnect import com.tangem.blockchain.common.Blockchain import com.tangem.common.extensions.guard -import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler +import com.tangem.tap.common.analytics.Analytics import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.features.details.redux.walletconnect.* @@ -23,17 +23,7 @@ import okhttp3.logging.HttpLoggingInterceptor import timber.log.Timber import java.util.* import java.util.concurrent.TimeUnit -import kotlin.collections.MutableMap -import kotlin.collections.filter -import kotlin.collections.first -import kotlin.collections.forEach -import kotlin.collections.get -import kotlin.collections.listOf -import kotlin.collections.map -import kotlin.collections.mutableMapOf import kotlin.collections.set -import kotlin.collections.toMap -import kotlin.collections.toMutableMap class WalletConnectManager { @@ -171,9 +161,9 @@ class WalletConnectManager { } private fun onSessionClosed(session: WCSession) { - FirebaseAnalyticsHandler.logWcEvent( - FirebaseAnalyticsHandler.WcAnalyticsEvent.Session( - FirebaseAnalyticsHandler.WcSessionEvent.Disconnect, sessions[session]?.peerMeta?.url + store.state.globalState.analyticsHandlers?.logWcEvent( + Analytics.WcAnalyticsEvent.Session( + Analytics.WcSessionEvent.Disconnect, sessions[session]?.peerMeta?.url ) ) sessions.remove(session) @@ -312,9 +302,9 @@ class WalletConnectManager { ) ) } - FirebaseAnalyticsHandler.logWcEvent( - FirebaseAnalyticsHandler.WcAnalyticsEvent.Session( - FirebaseAnalyticsHandler.WcSessionEvent.Connect, peer.url + store.state.globalState.analyticsHandlers?.logWcEvent( + Analytics.WcAnalyticsEvent.Session( + Analytics.WcSessionEvent.Connect, peer.url ) ) } @@ -326,9 +316,9 @@ class WalletConnectManager { } client.onEthSendTransaction = { id: Long, transaction: WCEthereumTransaction -> Timber.d("onEthSendTransaction: $transaction") - FirebaseAnalyticsHandler.logWcEvent( - FirebaseAnalyticsHandler.WcAnalyticsEvent.Action( - FirebaseAnalyticsHandler.WcAction.SendTransaction + store.state.globalState.analyticsHandlers?.logWcEvent( + Analytics.WcAnalyticsEvent.Action( + Analytics.WcAction.SendTransaction ) ) sessions[client.session]?.toWalletConnectSession()?.let { sessionData -> @@ -344,9 +334,9 @@ class WalletConnectManager { } client.onEthSignTransaction = { id: Long, transaction: WCEthereumTransaction -> Timber.d("onEthSignTransaction: $transaction") - FirebaseAnalyticsHandler.logWcEvent( - FirebaseAnalyticsHandler.WcAnalyticsEvent.Action( - FirebaseAnalyticsHandler.WcAction.SignTransaction + store.state.globalState.analyticsHandlers?.logWcEvent( + Analytics.WcAnalyticsEvent.Action( + Analytics.WcAction.SignTransaction ) ) sessions[client.session]?.toWalletConnectSession()?.let { sessionData -> @@ -362,9 +352,9 @@ class WalletConnectManager { } client.onEthSign = { id: Long, message: WCEthereumSignMessage -> Timber.d("onEthSign: $message") - FirebaseAnalyticsHandler.logWcEvent( - FirebaseAnalyticsHandler.WcAnalyticsEvent.Action( - FirebaseAnalyticsHandler.WcAction.PersonalSign + store.state.globalState.analyticsHandlers?.logWcEvent( + Analytics.WcAnalyticsEvent.Action( + Analytics.WcAction.PersonalSign ) ) sessions[client.session]?.toWalletConnectSession()?.let { sessionData -> @@ -417,9 +407,9 @@ class WalletConnectManager { val message = EthSignHelper.tryToParseEthTypedMessage(data) if (message != null) { Timber.d("onEthSign_v4: $message") - FirebaseAnalyticsHandler.logWcEvent( - FirebaseAnalyticsHandler.WcAnalyticsEvent.Action( - FirebaseAnalyticsHandler.WcAction.PersonalSign + store.state.globalState.analyticsHandlers?.logWcEvent( + Analytics.WcAnalyticsEvent.Action( + Analytics.WcAction.PersonalSign ) ) sessions[client.session]?.toWalletConnectSession()?.let { sessionData -> diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt index ad8b9084be..630176edc6 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt @@ -13,7 +13,7 @@ import com.tangem.common.extensions.hexToBytes import com.tangem.common.extensions.toHexString import com.tangem.crypto.CryptoUtils import com.tangem.operations.sign.SignHashCommand -import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler +import com.tangem.tap.common.analytics.Analytics import com.tangem.tap.common.extensions.toFormattedString import com.tangem.tap.features.details.redux.walletconnect.* import com.tangem.tap.features.details.ui.walletconnect.dialogs.PersonalSignDialogData @@ -136,9 +136,9 @@ class WalletConnectSdkHelper { } is SimpleResult.Failure -> { (result.error as? TangemSdkError)?.let { error -> - FirebaseAnalyticsHandler.logCardSdkError( + store.state.globalState.analyticsHandlers?.logCardSdkError( error, - FirebaseAnalyticsHandler.ActionToLog.WalletConnectTransaction, + Analytics.ActionToLog.WalletConnectTransaction, ) } Timber.e(result.error) @@ -167,9 +167,9 @@ class WalletConnectSdkHelper { } is CompletionResult.Failure -> { (result.error as? TangemSdkError)?.let { error -> - FirebaseAnalyticsHandler.logCardSdkError( + store.state.globalState.analyticsHandlers?.logCardSdkError( error, - FirebaseAnalyticsHandler.ActionToLog.WalletConnectSign, + Analytics.ActionToLog.WalletConnectSign, ) } Timber.e(result.error.customMessage) @@ -196,9 +196,9 @@ class WalletConnectSdkHelper { } is CompletionResult.Failure -> { (result.error as? TangemSdkError)?.let { error -> - FirebaseAnalyticsHandler.logCardSdkError( + store.state.globalState.analyticsHandlers?.logCardSdkError( error, - FirebaseAnalyticsHandler.ActionToLog.WalletConnectTransaction, + Analytics.ActionToLog.WalletConnectTransaction, ) } Timber.e(result.error.customMessage) @@ -273,9 +273,9 @@ class WalletConnectSdkHelper { } is CompletionResult.Failure -> { (result.error as? TangemSdkError)?.let { error -> - FirebaseAnalyticsHandler.logCardSdkError( + store.state.globalState.analyticsHandlers?.logCardSdkError( error, - FirebaseAnalyticsHandler.ActionToLog.WalletConnectSign, + Analytics.ActionToLog.WalletConnectSign, ) } Timber.e(result.error.customMessage) diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt index 2a6d764f4c..dfcf2c03f3 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt @@ -5,7 +5,8 @@ import com.tangem.common.card.FirmwareVersion import com.tangem.common.core.TangemSdkError import com.tangem.common.services.Result import com.tangem.operations.pins.CheckUserCodesResponse -import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler +import com.tangem.tap.common.analytics.Analytics +import com.tangem.tap.common.analytics.AnalyticsParam import com.tangem.tap.common.extensions.dispatchNotification import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.redux.AppState @@ -118,9 +119,9 @@ class DetailsMiddleware { } is CompletionResult.Failure -> { (result.error as? TangemSdkError)?.let { error -> - FirebaseAnalyticsHandler.logCardSdkError( + store.state.globalState.analyticsHandlers?.logCardSdkError( error, - FirebaseAnalyticsHandler.ActionToLog.PurgeWallet, + Analytics.ActionToLog.PurgeWallet, card = store.state.detailsState.scanResponse?.card ) } @@ -206,11 +207,11 @@ class DetailsMiddleware { } is CompletionResult.Failure -> { (result.error as? TangemSdkError)?.let { error -> - FirebaseAnalyticsHandler.logCardSdkError( + store.state.globalState.analyticsHandlers?.logCardSdkError( error = error, - actionToLog = FirebaseAnalyticsHandler.ActionToLog.ChangeSecOptions, + actionToLog = Analytics.ActionToLog.ChangeSecOptions, parameters = mapOf( - FirebaseAnalyticsHandler.AnalyticsParam.NEW_SECURITY_OPTION to + AnalyticsParam.NEW_SECURITY_OPTION to (selectedOption?.name ?: "") ), card = store.state.detailsState.scanResponse?.card diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt index 341aa3e64b..467929a848 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt @@ -1,5 +1,8 @@ package com.tangem.tap.features.home.redux +import com.tangem.tap.common.analytics.AnalyticsEvent +import com.tangem.tap.common.analytics.AnalyticsParam +import com.tangem.tap.common.analytics.GetCardSourceParams import com.tangem.tap.common.entities.IndeterminateProgressButton import com.tangem.tap.common.extensions.dispatchOpenUrl import com.tangem.tap.common.extensions.onCardScanned @@ -46,7 +49,13 @@ private val homeMiddleware: Middleware = { dispatch, state -> } } is HomeAction.ReadCard -> handleReadCard() - is HomeAction.GoToShop -> store.dispatchOpenUrl(HomeMiddleware.CARD_SHOP_URI) + is HomeAction.GoToShop -> { + store.dispatchOpenUrl(HomeMiddleware.CARD_SHOP_URI) + store.state.globalState.analyticsHandlers?.triggerEvent( + event = AnalyticsEvent.GET_CARD, + params = mapOf(AnalyticsParam.SOURCE.param to GetCardSourceParams.WELCOME.param) + ) + } } next(action) } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt index f69e59381f..f32decfddd 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt @@ -130,7 +130,11 @@ private fun handle(action: Action, dispatch: DispatchFunction) { } } is TwinCardsAction.Wallet.LaunchFirstStep -> { - val manager = TwinCardsManager(getScanResponse().card, action.reader) + val manager = TwinCardsManager( + card = getScanResponse().card, + assetReader = action.reader, + analyticsHandler = store.state.globalState.analyticsHandlers + ) store.dispatch(TwinCardsAction.CardsManager.Set(manager)) scope.launch { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt index cdccf7265a..2f58bb300b 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt @@ -4,6 +4,9 @@ import com.tangem.common.CompletionResult import com.tangem.common.card.Card import com.tangem.operations.backup.BackupService import com.tangem.tap.* +import com.tangem.tap.common.analytics.AnalyticsEvent +import com.tangem.tap.common.analytics.AnalyticsParam +import com.tangem.tap.common.analytics.GetCardSourceParams import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.dispatchOpenUrl import com.tangem.tap.common.extensions.withMainContext @@ -212,7 +215,13 @@ private fun handleBackupAction(action: BackupAction) { } } is BackupAction.GoToShop -> { - store.dispatchOpenUrl(BUY_WALLET_URL) + backupState.buyAdditionalCardsUrl?.let { url -> + store.dispatchOpenUrl(url) + store.state.globalState.analyticsHandlers?.triggerEvent( + event = AnalyticsEvent.GET_CARD, + params = mapOf(AnalyticsParam.SOURCE.param to GetCardSourceParams.ONBOARDING.param) + ) + } } is BackupAction.FinishAddingBackupCards -> { if (backupService.addedBackupCardsCount == 1) { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt index b550b82a5b..481110f343 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt @@ -12,7 +12,6 @@ import com.google.android.material.tabs.TabLayoutMediator import com.squareup.picasso.Picasso import com.tangem.common.CardIdFormatter import com.tangem.common.core.CardIdDisplayFormat -import com.tangem.tap.common.extensions.dispatchOpenUrl import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.show import com.tangem.tap.common.leapfrogWidget.LeapfrogWidget @@ -20,7 +19,6 @@ import com.tangem.tap.common.postUi import com.tangem.tap.features.FragmentOnBackPressedHandler import com.tangem.tap.features.addBackPressHandler import com.tangem.tap.features.onboarding.products.wallet.redux.* -import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletMiddleware.Companion.BUY_WALLET_URL import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.AccessCodeDialog import com.tangem.tap.store import com.tangem.wallet.R @@ -377,7 +375,7 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet), override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { R.id.shop_menu -> { - store.dispatchOpenUrl(BUY_WALLET_URL) + store.dispatch(BackupAction.GoToShop) true } else -> super.onOptionsItemSelected(item) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt index ff45a74e19..9243d5953e 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt @@ -9,8 +9,9 @@ import com.tangem.blockchain.extensions.SimpleResult import com.tangem.common.card.Card import com.tangem.common.core.TangemSdkError import com.tangem.common.services.Result +import com.tangem.tap.common.analytics.Analytics import com.tangem.tap.common.analytics.AnalyticsEvent -import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler +import com.tangem.tap.common.analytics.AnalyticsParam import com.tangem.tap.common.extensions.* import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction @@ -180,7 +181,7 @@ private fun sendTransaction( tangemSdk.config.linkedTerminal = isLinkedTerminal when (sendResult) { is SimpleResult.Success -> { - FirebaseAnalyticsHandler.triggerEvent( + store.state.globalState.analyticsHandlers?.triggerEvent( event = AnalyticsEvent.TRANSACTION_IS_SENT, card = card, blockchain = walletManager.wallet.blockchain.currency @@ -239,11 +240,11 @@ private fun sendTransaction( } else -> { (sendResult.error as? TangemSdkError)?.let { error -> - FirebaseAnalyticsHandler.logCardSdkError( + store.state.globalState.analyticsHandlers?.logCardSdkError( error, - FirebaseAnalyticsHandler.ActionToLog.SendTransaction, + Analytics.ActionToLog.SendTransaction, mapOf( - FirebaseAnalyticsHandler.AnalyticsParam.BLOCKCHAIN + AnalyticsParam.BLOCKCHAIN to walletManager.wallet.blockchain.currency), card = card, ) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt index a33d9dbb41..37b71f1c2a 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt @@ -10,7 +10,7 @@ import com.tangem.common.extensions.isZero import com.tangem.common.services.Result import com.tangem.operations.attestation.OnlineCardVerifier import com.tangem.tap.* -import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler +import com.tangem.tap.common.analytics.Analytics import com.tangem.tap.common.extensions.* import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.common.redux.AppState @@ -104,13 +104,13 @@ class WalletMiddleware { when (result) { is CompletionResult.Success -> { val scanNoteResponse = globalState.scanResponse?.copy(card = result.data) - scanNoteResponse?.let { store.onCardScanned(scanNoteResponse, false) } + scanNoteResponse?.let { store.onCardScanned(scanNoteResponse) } } is CompletionResult.Failure -> { (result.error as? TangemSdkError)?.let { error -> - FirebaseAnalyticsHandler.logCardSdkError( + store.state.globalState.analyticsHandlers?.logCardSdkError( error, - FirebaseAnalyticsHandler.ActionToLog.CreateWallet, + Analytics.ActionToLog.CreateWallet, card = store.state.detailsState.scanResponse?.card ) } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt index 7dacae94b1..408f0ef8a3 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt @@ -7,7 +7,6 @@ import com.tangem.blockchain.extensions.SimpleResult import com.tangem.common.card.Card import com.tangem.common.card.FirmwareVersion import com.tangem.tap.common.analytics.AnalyticsEvent -import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler import com.tangem.tap.common.extensions.isGreaterThan import com.tangem.tap.common.redux.global.GlobalState import com.tangem.tap.domain.TapWorkarounds.isTestCard @@ -75,7 +74,7 @@ class WarningsMiddleware { preferencesStorage.appRatingLaunchObserver.foundWalletWithFunds() } if (preferencesStorage.appRatingLaunchObserver.isReadyToShow()) { - FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.APP_RATING_DISPLAYED) + store.state.globalState.analyticsHandlers?.triggerEvent(AnalyticsEvent.APP_RATING_DISPLAYED) addWarningMessage(WarningMessagesManager.appRatingWarning(), true) } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt index 2b793466c3..22c2d56cf3 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt @@ -10,7 +10,6 @@ import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.ItemDecoration import com.google.android.play.core.review.ReviewManagerFactory import com.tangem.tap.common.analytics.AnalyticsEvent -import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler import com.tangem.tap.common.extensions.getActivity import com.tangem.tap.common.extensions.getString import com.tangem.tap.common.extensions.hide @@ -102,13 +101,14 @@ class WarningMessageVH(val view: View) : RecyclerView.ViewHolder(view) { WarningMessage.Type.AppRating -> { view.group_controls_temporary.hide() view.group_controls_rating.show() + val analyticsHandler = store.state.globalState.analyticsHandlers view.btn_close.setOnClickListener { - FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.APP_RATING_DISMISS) + analyticsHandler?.triggerEvent(AnalyticsEvent.APP_RATING_DISMISS) store.dispatch(GlobalAction.HideWarningMessage(warning)) store.dispatch(WalletAction.Warnings.AppRating.RemindLater) } view.btn_can_be_better.setOnClickListener { - FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.APP_RATING_NEGATIVE) + analyticsHandler?.triggerEvent(AnalyticsEvent.APP_RATING_NEGATIVE) store.dispatch(WalletAction.Warnings.AppRating.SetNeverToShow) store.dispatch(GlobalAction.HideWarningMessage(warning)) store.dispatch(GlobalAction.SendFeedback(RateCanBeBetterEmail())) @@ -116,7 +116,7 @@ class WarningMessageVH(val view: View) : RecyclerView.ViewHolder(view) { view.btn_really_cool.setOnClickListener { val activity = view.context.getActivity() ?: return@setOnClickListener - FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.APP_RATING_POSITIVE) + analyticsHandler?.triggerEvent(AnalyticsEvent.APP_RATING_POSITIVE) store.dispatch(WalletAction.Warnings.AppRating.SetNeverToShow) val reviewManager = ReviewManagerFactory.create(activity) val task = reviewManager.requestReviewFlow() diff --git a/dependencies.gradle b/dependencies.gradle index 43b263845b..8004a23445 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -1,4 +1,4 @@ ext.versions = [ - kotlin : '1.5.30', - build_gradle: '7.0.2', + kotlin : '1.6.10', + build_gradle: '7.0.4', ] From a370de2bcb7b663a3eb15050690354b505f2de9f Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 26 Jan 2022 20:25:32 +0300 Subject: [PATCH 07/20] Updated on 2026-08-14 --- app/build.gradle | 6 +- app/src/main/assets/tangem-app-config | 2 +- .../tap/common/analytics/TangemSdkError.kt | 1 + .../tangem/tap/common/extensions/ByteArray.kt | 17 -- .../tap/common/leapfrogWidget/Animations.kt | 147 ----------- .../tap/common/leapfrogWidget/LeapView.kt | 144 ---------- .../common/leapfrogWidget/LeapfrogWidget.kt | 249 ------------------ .../leapfrogWidget/TestLeapfrogFragment.kt | 2 + .../com/tangem/tap/domain/TangemSdkManager.kt | 8 +- .../domain/extensions/WalletManagerFactory.kt | 18 +- .../tangem/tap/domain/tasks/DerivationTask.kt | 105 ++++---- .../tasks/product/CreateProductWalletTask.kt | 10 +- .../domain/tasks/product/ScanProductTask.kt | 12 +- .../tap/domain/twins/TwinCardsWidget.kt | 7 +- .../twins/WriteProtectedIssuerDataTask.kt | 2 +- .../products/twins/ui/TwinsCardsFragment.kt | 2 +- .../products/wallet/ui/BackupCardsWidget.kt | 6 +- .../wallet/ui/OnboardingWalletFragment.kt | 2 +- .../features/tokens/redux/TokensMiddleware.kt | 18 +- 19 files changed, 110 insertions(+), 648 deletions(-) delete mode 100644 app/src/main/java/com/tangem/tap/common/extensions/ByteArray.kt delete mode 100644 app/src/main/java/com/tangem/tap/common/leapfrogWidget/Animations.kt delete mode 100644 app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapView.kt delete mode 100644 app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapfrogWidget.kt diff --git a/app/build.gradle b/app/build.gradle index b35d59c395..7029ff8a83 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,9 +77,9 @@ dependencies { implementation 'com.google.android.play:core-ktx:1.8.1' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' - implementation 'com.tangem:blockchain:develop-53' - implementation 'com.tangem.tangem-sdk-kotlin:core:develop-115' - implementation 'com.tangem.tangem-sdk-kotlin:android:develop-115' + implementation 'com.tangem:blockchain:BSK-107_update_sdk-54' + implementation 'com.tangem.tangem-sdk-kotlin:core:develop-125' + implementation 'com.tangem.tangem-sdk-kotlin:android:develop-125' // WebView implementation "androidx.browser:browser:1.3.0" diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index e80938f7d9..4280329669 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit e80938f7d9ba378a91ac544a9170597e64afa696 +Subproject commit 42803296690ad6914ec2c59c5fdc3f0fb30f8afc diff --git a/app/src/main/java/com/tangem/tap/common/analytics/TangemSdkError.kt b/app/src/main/java/com/tangem/tap/common/analytics/TangemSdkError.kt index 233fa3702d..9a9f5c62b1 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/TangemSdkError.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/TangemSdkError.kt @@ -105,6 +105,7 @@ object TangemSdk { is TangemSdkError.WrongInteractionMode -> TangemSdkError.WrongInteractionMode() is TangemSdkError.IssuerSignatureLoadingFailed -> TangemSdkError.IssuerSignatureLoadingFailed() is TangemSdkError.BackupFailedFirmware -> TangemSdkError.BackupFailedFirmware() + is TangemSdkError.UserForgotTheCode -> TangemSdkError.UserForgotTheCode() } } diff --git a/app/src/main/java/com/tangem/tap/common/extensions/ByteArray.kt b/app/src/main/java/com/tangem/tap/common/extensions/ByteArray.kt deleted file mode 100644 index 376b56f399..0000000000 --- a/app/src/main/java/com/tangem/tap/common/extensions/ByteArray.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.tangem.tap.common.extensions - -/** -[REDACTED_AUTHOR] - */ - -data class ByteArrayKey(val bytes: ByteArray) { - override fun equals(other: Any?): Boolean { - return this === other || other is ByteArrayKey && this.bytes.contentEquals(other.bytes) - } - - override fun hashCode(): Int = bytes.contentHashCode() -} - -fun ByteArray.toMapKey(): ByteArrayKey = ByteArrayKey(this) - -fun ByteArrayKey.bytes(): ByteArray = this.bytes \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/Animations.kt b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/Animations.kt deleted file mode 100644 index a95488d44f..0000000000 --- a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/Animations.kt +++ /dev/null @@ -1,147 +0,0 @@ -package com.tangem.tap.common.leapfrogWidget - -import android.animation.Animator -import android.animation.AnimatorSet -import android.animation.ObjectAnimator -import android.animation.ValueAnimator -import android.view.View -import android.view.animation.DecelerateInterpolator -import android.view.animation.LinearInterpolator - -/** -[REDACTED_AUTHOR] - */ -//fun View.setForeground(hasForegroundAtEnd: Boolean) { -// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { -// foreground = getForegroundDrawable() -// } -//} - -//fun View.getForegroundDrawable(): Drawable? { -// return AppCompatResources.getDrawable(context, R.drawable.shape_rectangle_rounded_8_op_20)?.also { -// it.alpha = 60 -// } -//} - -typealias ProgressListener = (Int) -> Unit - -fun createProgressListener(duration: Long, listener: ProgressListener?): Animator { - val valueAnimator = ValueAnimator.ofInt(0, 100) - valueAnimator.duration = duration - valueAnimator.addUpdateListener { listener?.invoke((it.animatedValue as? Int) ?: 0) } - return valueAnimator -} - -fun View.unfoldAnimation(animDuration: Long, properties: LeapViewProperties): AnimatorSet { - val translate = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation) - translate.duration = animDuration - return AnimatorSet().apply { playTogether(translate) } -} - -fun View.foldAnimation(animDuration: Long, properties: LeapViewProperties): AnimatorSet { - val translate = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation) - translate.duration = animDuration - - return AnimatorSet().apply { - playSequentially(translate) - } -} - -fun View.leapAnimation(animDuration: Long, properties: LeapViewProperties, overLift: Float): AnimatorSet { - val halfDuration = animDuration / 2 - - val upTo = (height.toFloat() + overLift) * -1 - val translateUp = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, upTo) - translateUp.duration = halfDuration - translateUp.interpolator = LinearInterpolator() - - val scaleDuration = halfDuration - halfDuration / 5 - val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X, properties.scale) - val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scale) - val scale = AnimatorSet().apply { - startDelay = scaleDuration - duration = scaleDuration - interpolator = LinearInterpolator() - playTogether(scaleX, scaleY) - } - - val elevation = elevationAnimator(properties.elevationStart, properties.elevationEnd, halfDuration, 100) - val translateDown = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation) - translateDown.startDelay = halfDuration - translateDown.duration = halfDuration - translateDown.interpolator = LinearInterpolator() - - return AnimatorSet().apply { - playTogether(translateUp, scale, elevation, translateDown) - } -} - -fun View.leapBackAnimation(animDuration: Long, properties: LeapViewProperties, calculator: PropertyCalculator): AnimatorSet { - val halfDuration = animDuration / 2 - - val upTo = ((height).toFloat() - calculator.yTranslationFactor) * -1 - val translateUp = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, upTo) - translateUp.duration = halfDuration - translateUp.interpolator = LinearInterpolator() - - val scaleDuration = halfDuration - halfDuration / 5 - val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X, properties.scale) - val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scale) - val scale = AnimatorSet().apply { - startDelay = scaleDuration - duration = scaleDuration - interpolator = LinearInterpolator() - playTogether(scaleX, scaleY) - } - - val elevation = elevationAnimator(properties.elevationStart, properties.elevationEnd, halfDuration, 100) - val translateDown = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation) - translateDown.startDelay = halfDuration - translateDown.duration = halfDuration - - return AnimatorSet().apply { - playTogether(translateUp, scale, elevation, translateDown) - } -} - -fun View.pullUpAnimation(leapDuration: Long, properties: LeapViewProperties): AnimatorSet { - val pullDuration = leapDuration / 2 - val delayDuration = leapDuration / 2 - - val translateUp = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation) - val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X, properties.scale) - val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scale) - val elevation = elevationAnimator(properties.elevationStart, properties.elevationEnd) - - return AnimatorSet().apply { - startDelay = delayDuration - duration = pullDuration - interpolator = DecelerateInterpolator() - playTogether(translateUp, scaleX, scaleY, elevation) - } -} - -fun View.pullDownAnimation(leapDuration: Long, properties: LeapViewProperties): AnimatorSet { - val pullDuration = leapDuration / 2 - val delayDuration = leapDuration / 4 - - val translate = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation) - val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X, properties.scale) - val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scale) - val elevation = elevationAnimator(properties.elevationStart, properties.elevationEnd) - - return AnimatorSet().apply { - startDelay = delayDuration - duration = pullDuration - interpolator = DecelerateInterpolator() - playTogether(translate, scaleX, scaleY, elevation) - } -} - -fun View.elevationAnimator(start: Float, end: Float, startDelay: Long? = null, duration: Long? = null): Animator { - val elevationAnimator = ValueAnimator.ofFloat(start, end) - elevationAnimator.addUpdateListener { this.elevation = (it.animatedValue as? Float) ?: start } - startDelay?.let { elevationAnimator.startDelay = it } - duration?.let { elevationAnimator.duration = it } - return elevationAnimator -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapView.kt b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapView.kt deleted file mode 100644 index be2d63b664..0000000000 --- a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapView.kt +++ /dev/null @@ -1,144 +0,0 @@ -package com.tangem.tap.common.leapfrogWidget - -import android.view.View - -/** -[REDACTED_AUTHOR] - */ -class LeapView( - val view: View, - val index: Int, - position: Int, - private val maximalPosition: Int, - private val calculator: PropertyCalculator, -) { - var state: LeapViewState - private set - - val initialState: LeapViewState - - init { - val initialProperties = createInitialProperty(position) - state = LeapViewState(index, position, position, initialProperties) - initialState = state.copy() - } - - fun initView() { - applyState(initialState) - } - - fun leap(): LeapFrogAnimation { - val previousPosition = state.currentPosition - val currentPosition = when (previousPosition) { - 0 -> maximalPosition - else -> state.currentPosition - 1 - } - - state = state.copy( - currentPosition = currentPosition, - previousPosition = previousPosition, - properties = createLeapAnimationProperty(previousPosition, currentPosition) - ) - - return when { - previousPosition == 0 && currentPosition == maximalPosition -> LeapFrogAnimation.LEAP - else -> LeapFrogAnimation.PULL - } - } - - fun leapBack(): LeapFrogAnimation { - val previousPosition = state.currentPosition - val currentPosition = when (previousPosition) { - maximalPosition -> 0 - else -> state.currentPosition + 1 - } - state = state.copy( - currentPosition = currentPosition, - previousPosition = previousPosition, - properties = createLeapAnimationProperty(previousPosition, currentPosition) - ) - - return when { - previousPosition == maximalPosition && currentPosition == 0 -> LeapFrogAnimation.LEAP - else -> LeapFrogAnimation.PULL - } - } - - fun fold() { - state = state.copy(properties = state.properties.toFold() - ) - } - - fun unfold() { - state = state.copy(properties = state.properties.toUnfold(calculator)) - } - - fun applyState(state: LeapViewState) { - this.state = state - setProperties(this.state.properties) - } - - private fun setProperties(properties: LeapViewProperties) { - view.elevation = properties.elevationEnd - view.translationY = properties.yTranslation - view.scaleX = properties.scale - view.scaleY = properties.scale -// initialProperties.hasForeground - } - - private fun createInitialProperty(endPosition: Int): LeapViewProperties { - return LeapViewProperties( - endPosition, - endPosition, - calculator.scale(endPosition), - calculator.elevation(endPosition, maximalPosition + 1), - calculator.elevation(endPosition, maximalPosition + 1), - calculator.yTranslation(0), - calculator.hasForeground(endPosition), - ) - } - - private fun createLeapAnimationProperty(startPosition: Int, endPosition: Int): LeapViewProperties { - return LeapViewProperties( - startPosition, - endPosition, - calculator.scale(endPosition), - calculator.elevation(startPosition, maximalPosition + 1), - calculator.elevation(endPosition, maximalPosition + 1), - calculator.yTranslation(endPosition), - calculator.hasForeground(endPosition), - ) - } - - private fun LeapViewProperties.toUnfold(calculator: PropertyCalculator): LeapViewProperties { - return this.copy(yTranslation = calculator.yTranslation(this.positionEnd)) - } - - private fun LeapViewProperties.toFold(): LeapViewProperties { - return this.copy(yTranslation = 0f) - } - - override fun toString(): String = "index: $index, position: ${state.currentPosition}, code: ${view.hashCode()}" - -} - -data class LeapViewState( - val index: Int, - val currentPosition: Int, - val previousPosition: Int, - val properties: LeapViewProperties -) - -enum class LeapFrogAnimation { - LEAP, PULL -} - -data class LeapViewProperties( - val positionStart: Int, - val positionEnd: Int, - val scale: Float, - val elevationStart: Float, - val elevationEnd: Float, - val yTranslation: Float, - val hasForeground: Boolean, -) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapfrogWidget.kt b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapfrogWidget.kt deleted file mode 100644 index e83f7b8b92..0000000000 --- a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapfrogWidget.kt +++ /dev/null @@ -1,249 +0,0 @@ -package com.tangem.tap.common.leapfrogWidget - -import android.animation.Animator -import android.animation.AnimatorSet -import android.view.animation.AccelerateDecelerateInterpolator -import android.widget.FrameLayout -import androidx.core.animation.doOnEnd -import androidx.core.view.children -import com.tangem.common.extensions.VoidCallback -import com.tangem.tangem_sdk_new.extensions.dpToPx - -/** -[REDACTED_AUTHOR] - */ -class LeapfrogWidget( - private val parentContainer: FrameLayout, - private val calculator: PropertyCalculator = PropertyCalculator(), -) { - private val foldAnimationDuration = 300L - private val leapAnimationDuration = 500L - private val leapBackAnimationDuration = 500L - - private val leapViews = mutableListOf() - - private var foldUnfoldProgress: Int = 100 - private var leapProgress: Int = 100 - private var leapBackProgress: Int = 100 - - init { - calculator.setTranslationConverter { parentContainer.dpToPx(it) } - if (viewIsFullFledged()) { - val maxPosition = parentContainer.childCount - 1 - parentContainer.children.forEachIndexed { index, view -> - val initialPosition = maxPosition - index - leapViews.add(LeapView(view, index, initialPosition, maxPosition, calculator)) - } - } - } - - /** - * Apply initial properties to the views - */ - fun initViews() { - leapViews.forEach { it.initView() } - } - - fun fold(animate: Boolean = true, onEnd: VoidCallback = {}) { - val animator = foldAnimator(animate) - if (animator == null) { - onEnd() - } else { - animator.doOnEnd { onEnd() } - animator.start() - } - } - - fun unfold(animate: Boolean = true, onEnd: VoidCallback = {}) { - val animator = unfoldAnimator(animate) - if (animator == null) { - onEnd() - } else { - animator.doOnEnd { onEnd() } - animator.start() - } - } - - fun foldAnimator(animate: Boolean = true): Animator? { - if (!canFoldUnfold()) return null - - return createFoldUnfoldAnimator(true, animate, foldAnimationDuration) - } - - fun unfoldAnimator(animate: Boolean = true): Animator? { - if (!canFoldUnfold()) return null - - return createFoldUnfoldAnimator(false, animate, foldAnimationDuration) - } - - private fun createFoldUnfoldAnimator(isFoldAnimation: Boolean, animate: Boolean, duration: Long): Animator { - val animatorsList = mutableListOf() - val animateDuration = if (animate) duration else 0 - leapViews.forEach { - if (isFoldAnimation) { - it.fold() - animatorsList.add(it.view.foldAnimation(animateDuration, it.state.properties)) - } else { - it.unfold() - animatorsList.add(it.view.unfoldAnimation(animateDuration, it.state.properties)) - } - } - animatorsList.add(createProgressListener(animateDuration) { foldUnfoldProgress = it }) - - val animator = AnimatorSet() - animator.interpolator = AccelerateDecelerateInterpolator() - animator.playTogether(animatorsList.toList()) - return animator - } - - fun leap(animate: Boolean = true, onEnd: VoidCallback = {}) { - if (!canLeap()) { - onEnd() - return - } - - val duration = if (animate) leapAnimationDuration else 0 - val animatorsList = mutableListOf() - leapViews.forEach { - when (it.leap()) { - LeapFrogAnimation.LEAP -> { - val overLift = calculator.overLift(leapViews.size) - animatorsList.add(it.view.leapAnimation(duration, it.state.properties, overLift)) - } - LeapFrogAnimation.PULL -> { - animatorsList.add(it.view.pullUpAnimation(duration, it.state.properties)) - } - } - } - animatorsList.add(createProgressListener(duration) { leapProgress = it }) - - val animator = AnimatorSet() - animator.playTogether(animatorsList.toList()) - animator.doOnEnd { onEnd() } - animator.start() - } - - fun leapBack(animate: Boolean = true, onEnd: VoidCallback = {}) { - if (!canLeapBack()) { - onEnd() - return - } - - val duration = if (animate) leapBackAnimationDuration else 0 - val animatorsList = mutableListOf() - leapViews.forEach { - when (it.leapBack()) { - LeapFrogAnimation.LEAP -> { - animatorsList.add(it.view.leapBackAnimation(duration, it.state.properties, calculator)) - } - LeapFrogAnimation.PULL -> { - animatorsList.add(it.view.pullDownAnimation(duration, it.state.properties)) - } - } - } - animatorsList.add(createProgressListener(duration) { leapBackProgress = it }) - - val animator = AnimatorSet() - animator.playTogether(animatorsList.toList()) - animator.doOnEnd { onEnd() } - animator.start() - } - - fun getViewsCount(): Int = parentContainer.childCount - - fun getViewPositionByIndex(index: Int): Int = leapViews.first { it.index == index }.state.currentPosition - - fun getViewByPosition(position: Int): LeapView = leapViews.first { it.state.currentPosition == position } - - fun getState(): LeapfrogWidgetState = LeapfrogWidgetState(leapViews.map { it.state }) - - fun applyState(state: LeapfrogWidgetState) { - state.leapViewStates.forEach { viewState -> - leapViews.firstOrNull { it.index == viewState.index }?.applyState(viewState) - } - } - - private fun viewIsFullFledged(): Boolean = parentContainer.childCount > 1 - - private fun canFoldUnfold(): Boolean { - return when { - !viewIsFullFledged() -> false - foldUnfoldInProgress() -> false - leapInProgress() || leapBackInProgress() -> false - else -> true - } - } - - private fun canLeap(): Boolean { - return when { - !viewIsFullFledged() -> false - leapBackInProgress() -> false - leapProgress < 70 -> false - else -> true - } - } - - private fun canLeapBack(): Boolean { - return when { - !viewIsFullFledged() -> false - leapInProgress() -> false - leapBackProgress < 70 -> false - else -> true - } - } - - private fun foldUnfoldInProgress(): Boolean = foldUnfoldProgress != 100 - private fun leapInProgress(): Boolean = leapProgress != 100 - private fun leapBackInProgress(): Boolean = leapBackProgress != 100 -} - -data class LeapfrogWidgetState( - val leapViewStates: List -) - -class PropertyCalculator( - val elevationFactor: Float = 1f, - val decreaseScaleFactor: Float = 0.15f, - val yTranslationFactor: Float = 35f, - val decreaseOverLiftingFactor: Float = 0.85f, -) { - // 0 view it is view on the top of stack, but it is last in parent.children - - private var translationValueConverter: ((Float) -> Float)? = null - fun setTranslationConverter(converter: (Float) -> Float) { - translationValueConverter = converter - } - - fun scale(position: Int): Float { - return 1f - decreaseScaleFactor * position - } - - fun elevation(position: Int, count: Int): Float { - return elevationFactor * (count - 1 - position) - } - - fun yTranslation(position: Int): Float { - val yTranslation = yTranslationFactor * position - return convertTranslation(yTranslation) - } - - fun hasForeground(position: Int): Boolean { - return position != 0 - } - - fun overLift(viewCount: Int): Float { - val initialOverLift = when { - yTranslationFactor < 0f -> yTranslationFactor * decreaseOverLiftingFactor * viewCount * -1 - else -> 10f - } - val scaleOverLift = when { - decreaseScaleFactor == 0f -> initialOverLift - else -> initialOverLift * decreaseOverLiftingFactor - initialOverLift * decreaseScaleFactor - } - return convertTranslation(scaleOverLift) - } - - private fun convertTranslation(value: Float): Float { - return translationValueConverter?.invoke(value) ?: value - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/TestLeapfrogFragment.kt b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/TestLeapfrogFragment.kt index 1a41ff35be..36e92a0ebe 100644 --- a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/TestLeapfrogFragment.kt +++ b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/TestLeapfrogFragment.kt @@ -5,6 +5,8 @@ import android.view.View import android.widget.FrameLayout import androidx.fragment.app.Fragment import androidx.transition.TransitionInflater +import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidget +import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidgetState import com.tangem.tap.domain.twins.TwinsCardWidget import com.tangem.wallet.R import kotlinx.android.synthetic.main.test_leapfrog_fragment.* diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt index 4c3f0c78d4..d8207f9808 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -14,18 +14,18 @@ import com.tangem.common.core.CardIdDisplayFormat import com.tangem.common.core.CardSessionRunnable import com.tangem.common.core.Config import com.tangem.common.core.TangemSdkError +import com.tangem.common.extensions.ByteArrayKey import com.tangem.common.hdWallet.DerivationPath import com.tangem.operations.CommandResponse +import com.tangem.operations.derivation.DerivationTaskResponse +import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask import com.tangem.operations.pins.CheckUserCodesCommand import com.tangem.operations.pins.CheckUserCodesResponse import com.tangem.operations.pins.SetUserCodeCommand import com.tangem.tap.common.analytics.AnalyticsEvent import com.tangem.tap.common.analytics.AnalyticsHandler import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler -import com.tangem.tap.common.extensions.ByteArrayKey import com.tangem.tap.domain.tasks.CreateWalletAndRescanTask -import com.tangem.tap.domain.tasks.DerivationTask -import com.tangem.tap.domain.tasks.DerivationTaskResponse import com.tangem.tap.domain.tasks.product.ResetToFactorySettingsTask import com.tangem.tap.domain.tasks.product.ScanProductTask import com.tangem.tap.domain.tasks.product.ScanResponse @@ -81,7 +81,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co cardId: String, derivations: Map> ): CompletionResult { - return runTaskAsyncReturnOnMain(DerivationTask(derivations), cardId) + return runTaskAsyncReturnOnMain(DeriveMultipleWalletPublicKeysTask(derivations), cardId) } suspend fun resetToFactorySettings(card: Card): CompletionResult { diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt b/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt index b505619739..3b58ec418e 100644 --- a/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt +++ b/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt @@ -6,7 +6,7 @@ import com.tangem.blockchain.common.WalletManagerFactory import com.tangem.common.card.CardWallet import com.tangem.common.card.EllipticCurve import com.tangem.common.extensions.hexToBytes -import com.tangem.tap.common.extensions.toMapKey +import com.tangem.common.extensions.toMapKey import com.tangem.tap.domain.TapWorkarounds.isTestCard import com.tangem.tap.domain.tasks.product.ScanResponse @@ -33,13 +33,23 @@ fun WalletManagerFactory.makeWalletManagerForApp( } seedKey != null -> { val derivedKeys = scanResponse.derivedKeys[wallet.publicKey.toMapKey()] - val derivedKey = derivedKeys?.firstOrNull { it.derivationPath == blockchain.derivationPath() } + val derivedKey = derivedKeys?.get(blockchain.derivationPath()) ?: return null - makeWalletManager(card.cardId, environmentBlockchain, seedKey, derivedKey) + makeWalletManager( + cardId = card.cardId, + blockchain = environmentBlockchain, + seedKey = wallet.publicKey, + derivedKey = derivedKey + ) } else -> { - makeWalletManager(card.cardId, environmentBlockchain, wallet.publicKey, wallet.curve) + makeWalletManager( + cardId = card.cardId, + blockchain = environmentBlockchain, + walletPublicKey = wallet.publicKey, + curve = wallet.curve + ) } } } diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/DerivationTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/DerivationTask.kt index 8f03e068b5..3c471f4bc4 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/DerivationTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/DerivationTask.kt @@ -1,50 +1,55 @@ -package com.tangem.tap.domain.tasks - -import com.tangem.common.CompletionResult -import com.tangem.common.core.CardSession -import com.tangem.common.core.CardSessionRunnable -import com.tangem.common.core.CompletionCallback -import com.tangem.common.hdWallet.DerivationPath -import com.tangem.operations.CommandResponse -import com.tangem.operations.derivation.DeriveWalletPublicKeysTask -import com.tangem.operations.derivation.ExtendedPublicKeyList -import com.tangem.tap.common.extensions.ByteArrayKey - -class DerivationTaskResponse( - val entries: Map -): CommandResponse - -class DerivationTask( - private val derivations: Map> -) : CardSessionRunnable { - - val response: MutableMap = mutableMapOf() - - override fun run(session: CardSession, callback: CompletionCallback) { - derive(keys = derivations.keys.toList(), index = 0, session = session, callback = callback) - } - - private fun derive( - keys: List, - index: Int, - session: CardSession, - callback: CompletionCallback - ) { - if (index == keys.count()) { - callback(CompletionResult.Success(DerivationTaskResponse(response.toMap()))) - return - } - - val key = keys[index] - val paths = derivations[key]!! - DeriveWalletPublicKeysTask(key.bytes, paths).run(session) { result -> - when (result) { - is CompletionResult.Success -> { - response[key] = result.data - derive(keys = keys, index = index + 1, session = session, callback = callback) - } - is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) - } - } - } -} \ No newline at end of file +//package com.tangem.tap.domain.tasks +// +//import com.tangem.common.CompletionResult +//import com.tangem.common.core.CardSession +//import com.tangem.common.core.CardSessionRunnable +//import com.tangem.common.core.CompletionCallback +//import com.tangem.common.hdWallet.DerivationPath +//import com.tangem.common.hdWallet.ExtendedPublicKey +//import com.tangem.operations.CommandResponse +//import com.tangem.operations.derivation.DeriveWalletPublicKeysTask +//import com.tangem.tap.common.extensions.ByteArrayKey +// +//class ExtendedPublicKeyList( +// items: Collection +//): ArrayList(items), CommandResponse +// +// +//class DerivationTaskResponse( +// val entries: Map +//): CommandResponse +// +//class DerivationTask( +// private val derivations: Map> +//) : CardSessionRunnable { +// +// val response: MutableMap = mutableMapOf() +// +// override fun run(session: CardSession, callback: CompletionCallback) { +// derive(keys = derivations.keys.toList(), index = 0, session = session, callback = callback) +// } +// +// private fun derive( +// keys: List, +// index: Int, +// session: CardSession, +// callback: CompletionCallback +// ) { +// if (index == keys.count()) { +// callback(CompletionResult.Success(DerivationTaskResponse(response.toMap()))) +// return +// } +// +// val key = keys[index] +// val paths = derivations[key]!! +// DeriveWalletPublicKeysTask(key.bytes, paths).run(session) { result -> +// when (result) { +// is CompletionResult.Success -> { +// response[key] = result.data[key] +// derive(keys = keys, index = index + 1, session = session, callback = callback) +// } +// is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) +// } +// } +// } +//} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt index 89bdb12dd2..e75da7ca72 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt @@ -6,16 +6,16 @@ import com.tangem.common.core.CardSession import com.tangem.common.core.CardSessionRunnable import com.tangem.common.core.TangemSdkError import com.tangem.common.extensions.guard -import com.tangem.common.hdWallet.ExtendedPublicKey +import com.tangem.common.extensions.toMapKey import com.tangem.operations.CommandResponse import com.tangem.operations.backup.PrimaryCard import com.tangem.operations.backup.StartPrimaryCardLinkingTask +import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask +import com.tangem.operations.derivation.ExtendedPublicKeysMap import com.tangem.operations.wallet.CreateWalletResponse import com.tangem.operations.wallet.CreateWalletTask -import com.tangem.tap.common.extensions.toMapKey import com.tangem.tap.domain.ProductType import com.tangem.tap.domain.TapWorkarounds.getTangemNoteBlockchain -import com.tangem.tap.domain.tasks.DerivationTask import com.tangem.tap.domain.tasks.product.CreateWalletsTask import com.tangem.tap.domain.tasks.product.KeyWalletPublicKey import com.tangem.tap.domain.tasks.product.ProductCommandProcessor @@ -24,7 +24,7 @@ import com.tangem.tap.domain.tasks.product.getCurvesForNonCreatedWallets data class CreateProductWalletTaskResponse( val card: Card, - val derivedKeys: Map> = mapOf(), + val derivedKeys: Map = mapOf(), val primaryCard: PrimaryCard? = null ) : CommandResponse @@ -170,7 +170,7 @@ private class CreateWalletTangemWallet : ProductCommandProcessor when (result) { is CompletionResult.Success -> { diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt index 506f5aa9c3..05ab2ed34b 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt @@ -12,19 +12,20 @@ import com.tangem.common.core.CardSession import com.tangem.common.core.CardSessionRunnable import com.tangem.common.core.TangemError import com.tangem.common.core.TangemSdkError +import com.tangem.common.extensions.ByteArrayKey import com.tangem.common.extensions.guard import com.tangem.common.extensions.toHexString +import com.tangem.common.extensions.toMapKey import com.tangem.common.hdWallet.DerivationPath -import com.tangem.common.hdWallet.ExtendedPublicKey import com.tangem.operations.CommandResponse import com.tangem.operations.PreflightReadMode import com.tangem.operations.PreflightReadTask import com.tangem.operations.ScanTask import com.tangem.operations.backup.PrimaryCard import com.tangem.operations.backup.StartPrimaryCardLinkingTask +import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask +import com.tangem.operations.derivation.ExtendedPublicKeysMap import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand -import com.tangem.tap.common.extensions.ByteArrayKey -import com.tangem.tap.common.extensions.toMapKey import com.tangem.tap.domain.ProductType import com.tangem.tap.domain.TapSdkError import com.tangem.tap.domain.TapWorkarounds @@ -33,7 +34,6 @@ import com.tangem.tap.domain.TapWorkarounds.isExcluded import com.tangem.tap.domain.TapWorkarounds.isNotSupportedInThatRelease import com.tangem.tap.domain.extensions.getPrimaryCurve import com.tangem.tap.domain.extensions.getSingleWallet -import com.tangem.tap.domain.tasks.DerivationTask import com.tangem.tap.domain.tokens.CurrenciesRepository import com.tangem.tap.domain.twins.TwinsHelper import com.tangem.tap.preferencesStorage @@ -43,7 +43,7 @@ data class ScanResponse( val productType: ProductType, val walletData: WalletData?, val secondTwinPublicKey: String? = null, - val derivedKeys: Map> = mapOf(), + val derivedKeys: Map = mapOf(), val primaryCard: PrimaryCard? = null ) : CommandResponse { @@ -253,7 +253,7 @@ private class ScanWalletProcessor( return } - DerivationTask(derivations).run(session) { result -> + DeriveMultipleWalletPublicKeysTask(derivations).run(session) { result -> when (result) { is CompletionResult.Success -> { val response = ScanResponse( diff --git a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsWidget.kt b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsWidget.kt index c8f3c02952..becb529e80 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsWidget.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsWidget.kt @@ -6,9 +6,10 @@ import android.animation.PropertyValuesHolder import android.view.View import androidx.core.animation.doOnEnd import com.tangem.common.extensions.VoidCallback -import com.tangem.tap.common.leapfrogWidget.LeapView -import com.tangem.tap.common.leapfrogWidget.LeapViewState -import com.tangem.tap.common.leapfrogWidget.LeapfrogWidget +import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapView +import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapViewState +import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidget + /** [REDACTED_AUTHOR] diff --git a/app/src/main/java/com/tangem/tap/domain/twins/WriteProtectedIssuerDataTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/WriteProtectedIssuerDataTask.kt index 94ce765f12..37f9715e19 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/WriteProtectedIssuerDataTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/WriteProtectedIssuerDataTask.kt @@ -8,7 +8,7 @@ import com.tangem.common.core.CardSessionRunnable import com.tangem.common.core.TangemSdkError import com.tangem.common.extensions.calculateSha256 import com.tangem.common.extensions.guard -import com.tangem.common.files.FileHashHelper +import com.tangem.operations.files.FileHashHelper import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand import com.tangem.operations.issuerAndUserData.ReadIssuerDataResponse import com.tangem.operations.issuerAndUserData.WriteIssuerDataCommand diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/TwinsCardsFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/TwinsCardsFragment.kt index 33eaf7a983..12e87f2a70 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/TwinsCardsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/TwinsCardsFragment.kt @@ -12,8 +12,8 @@ import com.squareup.picasso.Picasso import com.tangem.Message import com.tangem.blockchain.common.Blockchain import com.tangem.common.extensions.VoidCallback +import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidget import com.tangem.tap.common.extensions.* -import com.tangem.tap.common.leapfrogWidget.LeapfrogWidget import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.common.redux.navigation.ShareElement diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/BackupCardsWidget.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/BackupCardsWidget.kt index e4de53c9a4..1b4ecba2a7 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/BackupCardsWidget.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/BackupCardsWidget.kt @@ -6,9 +6,9 @@ import android.animation.PropertyValuesHolder import android.view.View import android.widget.ImageView import androidx.core.animation.doOnEnd -import com.tangem.tap.common.leapfrogWidget.LeapView -import com.tangem.tap.common.leapfrogWidget.LeapViewState -import com.tangem.tap.common.leapfrogWidget.LeapfrogWidget +import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapView +import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapViewState +import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidget class BackupCardsWidget( val leapfrogWidget: LeapfrogWidget, diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt index b550b82a5b..e238fa639e 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt @@ -12,10 +12,10 @@ import com.google.android.material.tabs.TabLayoutMediator import com.squareup.picasso.Picasso import com.tangem.common.CardIdFormatter import com.tangem.common.core.CardIdDisplayFormat +import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidget import com.tangem.tap.common.extensions.dispatchOpenUrl import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.show -import com.tangem.tap.common.leapfrogWidget.LeapfrogWidget import com.tangem.tap.common.postUi import com.tangem.tap.features.FragmentOnBackPressedHandler import com.tangem.tap.features.addBackPressHandler diff --git a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt index 30db14cfbe..636638df13 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt @@ -4,11 +4,11 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token import com.tangem.common.CompletionResult import com.tangem.common.card.EllipticCurve +import com.tangem.common.extensions.ByteArrayKey +import com.tangem.common.extensions.toMapKey import com.tangem.common.hdWallet.DerivationPath -import com.tangem.common.hdWallet.ExtendedPublicKey -import com.tangem.tap.common.extensions.ByteArrayKey +import com.tangem.operations.derivation.ExtendedPublicKeysMap import com.tangem.tap.common.extensions.dispatchErrorNotification -import com.tangem.tap.common.extensions.toMapKey import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.NavigationAction @@ -94,14 +94,14 @@ class TokensMiddleware { is CompletionResult.Success -> { val newDerivedKeys = result.data.entries val updatedDerivedKeys = - mutableMapOf>() + mutableMapOf() newDerivedKeys.forEach { entry -> val derivationData = derivationDataList.find { it.mapKeyOfWalletPublicKey == entry.key } ?: return@forEach updatedDerivedKeys[entry.key] = - derivationData.alreadyDerivedKeys + entry.value.toList() + ExtendedPublicKeysMap(derivationData.alreadyDerivedKeys + entry.value) } val updatedScanResponse = scanResponse.copy( @@ -133,9 +133,9 @@ class TokensMiddleware { .mapNotNull { it.derivationPath() } val mapKeyOfWalletPublicKey = wallet.publicKey.toMapKey() - val alreadyDerivedKeys = - scanResponse.derivedKeys[mapKeyOfWalletPublicKey]?.toMutableList() ?: mutableListOf() - val alreadyDerivedPaths = alreadyDerivedKeys.map { it.derivationPath } + val alreadyDerivedKeys: ExtendedPublicKeysMap = + scanResponse.derivedKeys[mapKeyOfWalletPublicKey] ?: ExtendedPublicKeysMap(emptyMap()) + val alreadyDerivedPaths = alreadyDerivedKeys.keys.toList() val toDerive = derivationPathsCandidates.filterNot { alreadyDerivedPaths.contains(it) } return DerivationData( @@ -147,7 +147,7 @@ class TokensMiddleware { private class DerivationData( val derivations: Pair>, - val alreadyDerivedKeys: List, + val alreadyDerivedKeys: ExtendedPublicKeysMap, val mapKeyOfWalletPublicKey: ByteArrayKey ) From 65cc9102807f6c7098575478b39483430857e845 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 27 Jan 2022 10:42:47 +0300 Subject: [PATCH 08/20] Updated on 2026-08-14 --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index d6f5fc9ab6..0a29927a55 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,7 +77,7 @@ dependencies { implementation 'com.google.android.play:core-ktx:1.8.1' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' - implementation 'com.tangem:blockchain:BSK-107_update_sdk-54' + implementation 'com.tangem:blockchain:develop-55' implementation 'com.tangem.tangem-sdk-kotlin:core:develop-125' implementation 'com.tangem.tangem-sdk-kotlin:android:develop-125' From 28ccab43f9029597854abedbd9700b5f78123708 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 27 Jan 2022 11:05:55 +0300 Subject: [PATCH 09/20] Updated on 2026-08-14 --- app/src/main/res/layout/fragment_onboarding_wallet.xml | 1 + app/src/main/res/layout/layout_backup_access_code_submit.xml | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/layout/fragment_onboarding_wallet.xml b/app/src/main/res/layout/fragment_onboarding_wallet.xml index e29397b2bb..5df3b30845 100644 --- a/app/src/main/res/layout/fragment_onboarding_wallet.xml +++ b/app/src/main/res/layout/fragment_onboarding_wallet.xml @@ -192,6 +192,7 @@ app:tabBackground="@drawable/selector_tabs_dots" app:tabIndicator="@null" app:tabPaddingStart="8dp" + android:layout_marginBottom="16dp" app:tabPaddingEnd="8dp" app:tabGravity="center"/> diff --git a/app/src/main/res/layout/layout_backup_access_code_submit.xml b/app/src/main/res/layout/layout_backup_access_code_submit.xml index eca2c05bbd..c9416e28f1 100644 --- a/app/src/main/res/layout/layout_backup_access_code_submit.xml +++ b/app/src/main/res/layout/layout_backup_access_code_submit.xml @@ -27,7 +27,7 @@ android:layout_marginTop="44dp" android:layout_marginEnd="16dp" android:layout_marginBottom="40dp" - android:hint="Access code" + android:hint="@string/onboarding_wallet_info_title_third" app:boxStrokeColor="@color/selector_edit_text" app:boxStrokeWidth="1dp" app:endIconDrawable="@drawable/selector_password_toggle" @@ -53,6 +53,9 @@ android:layout_marginStart="78dp" android:layout_marginEnd="78dp" android:layout_marginBottom="92dp" + android:layout_marginTop="32dp" + app:layout_constraintTop_toTopOf="@id/til_access_code" + app:layout_constraintVertical_bias="1" android:text="@string/common_create" android:textSize="16sp" app:layout_constraintBottom_toBottomOf="parent" /> From ef66e372e851abcf9665a823542d3b9964aca2ef Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 27 Jan 2022 11:17:12 +0300 Subject: [PATCH 10/20] Updated on 2026-08-14 --- app/src/main/res/layout/layout_backup_access_code_info.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/layout/layout_backup_access_code_info.xml b/app/src/main/res/layout/layout_backup_access_code_info.xml index fab2ddf46d..ac5a98171f 100644 --- a/app/src/main/res/layout/layout_backup_access_code_info.xml +++ b/app/src/main/res/layout/layout_backup_access_code_info.xml @@ -122,7 +122,7 @@ android:layout_marginStart="78dp" android:layout_marginEnd="78dp" android:layout_marginBottom="92dp" - android:text="@string/common_create" + android:text="@string/common_continue" android:textSize="16sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" From af1d83e20a09daa645c4826f230a2527692a653b Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 27 Jan 2022 11:19:09 +0300 Subject: [PATCH 11/20] Updated on 2026-08-14 --- .../onboarding/products/wallet/ui/OnboardingWalletFragment.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt index 2bfddfc8ae..30dc5affba 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt @@ -13,7 +13,6 @@ import com.squareup.picasso.Picasso import com.tangem.common.CardIdFormatter import com.tangem.common.core.CardIdDisplayFormat import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidget -import com.tangem.tap.common.extensions.dispatchOpenUrl import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.show import com.tangem.tap.common.postUi @@ -326,7 +325,7 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet), tv_header.text = getString(R.string.onboarding_title_backup_card_format, cardNumber) tv_body.text = getString( - R.string.onboarding_subtitle_scan_primary_card_format, + R.string.onboarding_subtitle_scan_backup_card_format, cardIdFormatter.getFormattedCardId(state.backupCardIds[cardNumber - 1]) ) btn_main_action.text = getString( From b971e1a746459107a9ad74b903c49bb3afbd37a5 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 28 Jan 2022 12:21:14 +0300 Subject: [PATCH 12/20] Updated on 2026-08-14 --- .../com/tangem/tap/features/details/ui/DetailsFragment.kt | 5 ++++- .../send/ui/stateSubscribers/SendStateSubscriber.kt | 5 +++++ .../tangem/tap/features/tokens/redux/TokensMiddleware.kt | 6 +++--- app/src/main/res/values/strings_untranslated.xml | 2 +- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt index 74d3f40fd9..acb76789ba 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt @@ -73,7 +73,10 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber { action -> when (action) { - is TokensAction.LoadCurrencies -> handleLoadCurrencies(action) + is TokensAction.LoadCurrencies -> handleLoadCurrencies() is TokensAction.SaveChanges -> handleSaveChanges(action) } next(action) @@ -40,7 +40,7 @@ class TokensMiddleware { } } - private fun handleLoadCurrencies(action: TokensAction.LoadCurrencies) { + private fun handleLoadCurrencies() { val scanResponse = store.state.globalState.scanResponse ?: return val isTestcard = scanResponse.card.isTestCard @@ -48,7 +48,7 @@ class TokensMiddleware { val blockchains = currenciesRepository.getBlockchains( cardFirmware = scanResponse.card.firmwareVersion, isTestNet = isTestcard - ) + ).sortedBy { it.fullName } val currencies = CurrencyListItem.createListOfCurrencies(blockchains, tokens).toMutableList() store.dispatch(TokensAction.LoadCurrencies.Success(currencies)) diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml index ce001133db..32f666e20c 100644 --- a/app/src/main/res/values/strings_untranslated.xml +++ b/app/src/main/res/values/strings_untranslated.xml @@ -27,7 +27,7 @@ Preparing card Creating wallet The wallet creation procedure consists of three steps. You must complete it to the end, otherwise you will have to start from the beginning. - Tap the card #%s + Scan the card #%s You have already started the process of recreating twin wallet. If you interrupt it, you won\'t be able to use your twin cards until you start it again and complete recreating the wallet. You\'ve tapped the same card. To create twin wallet you need to tap a second twin card. From 507a3f46f6d4160ec0330675af59e539f9c90c4b Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 28 Jan 2022 12:22:21 +0300 Subject: [PATCH 13/20] Updated on 2026-08-14 --- .../products/wallet/redux/OnboardingWalletMiddleware.kt | 2 ++ .../products/wallet/redux/OnboardingWalletReducer.kt | 2 +- .../products/wallet/ui/OnboardingWalletFragment.kt | 7 +------ 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt index 2f58bb300b..29cdd561b2 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt @@ -237,11 +237,13 @@ private fun handleBackupAction(action: BackupAction) { AccessCodeError.CodeTooShort )) } else { + store.dispatch(BackupAction.SetAccessCodeError(null)) store.dispatch(BackupAction.SaveFirstAccessCode(action.accessCode)) } } is BackupAction.SaveAccessCodeConfirmation -> { if (action.accessCodeConfirmation == backupState.accessCode) { + store.dispatch(BackupAction.SetAccessCodeError(null)) backupService.setAccessCode(action.accessCodeConfirmation) store.dispatch(BackupAction.PrepareToWritePrimaryCard) } else { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt index ce0c07b3c6..f46c1bf0ff 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt @@ -60,7 +60,7 @@ class BackupReducer { state.copy(backupStep = BackupStep.SetAccessCode) } BackupAction.ShowEnterAccessCodeScreen -> { - state.copy(backupStep = BackupStep.EnterAccessCode) + state.copy(backupStep = BackupStep.EnterAccessCode, accessCodeError = null) } is BackupAction.SaveFirstAccessCode -> { state.copy( diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt index 30dc5affba..27fe4c8eb7 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt @@ -15,7 +15,6 @@ import com.tangem.common.core.CardIdDisplayFormat import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidget import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.show -import com.tangem.tap.common.postUi import com.tangem.tap.features.FragmentOnBackPressedHandler import com.tangem.tap.features.addBackPressHandler import com.tangem.tap.features.onboarding.products.wallet.redux.* @@ -253,15 +252,11 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet), private fun showEnterAccessCode(state: BackupState) { accessCodeDialog?.showEnterAccessCode() accessCodeDialog?.showError(state.accessCodeError) - postUi(2000) { store.dispatch(BackupAction.SetAccessCodeError(null)) } - } private fun showReenterAccessCode(state: BackupState) { accessCodeDialog?.showReenterAccessCode() accessCodeDialog?.showError(state.accessCodeError) - postUi(2000) { store.dispatch(BackupAction.SetAccessCodeError(null)) } - } private fun showWritePrimaryCard(state: BackupState) { @@ -350,7 +345,7 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet), tab_layout_backup_info.hide() tv_body.text = text - btn_main_action.text = getText(R.string.common_continue) + btn_main_action.text = getText(R.string.onboarding_button_continue_wallet) btn_alternative_action.hide() btn_main_action.setOnClickListener { lav_confetti.cancelAnimation() From 79a7e7263da3c8706b3da6dbe23c74bf1f1adb85 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 1 Feb 2022 18:26:33 +0300 Subject: [PATCH 14/20] Updated on 2026-08-14 --- app/build.gradle | 6 +++--- .../com/tangem/tap/common/extensions/WalletManager.kt | 4 ++-- .../java/com/tangem/tap/domain/extensions/Blockchain.kt | 8 -------- .../tap/features/send/redux/middlewares/SendMiddleware.kt | 6 +++--- 4 files changed, 8 insertions(+), 16 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 0a29927a55..310b18fa9f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,9 +77,9 @@ dependencies { implementation 'com.google.android.play:core-ktx:1.8.1' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' - implementation 'com.tangem:blockchain:develop-55' - implementation 'com.tangem.tangem-sdk-kotlin:core:develop-125' - implementation 'com.tangem.tangem-sdk-kotlin:android:develop-125' + implementation 'com.tangem:blockchain:develop-57' + implementation 'com.tangem.tangem-sdk-kotlin:core:develop-127' + implementation 'com.tangem.tangem-sdk-kotlin:android:develop-127' // WebView implementation "androidx.browser:browser:1.3.0" diff --git a/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt b/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt index 70a4fcc286..3d33ceeaa8 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt @@ -1,11 +1,11 @@ package com.tangem.tap.common.extensions +import com.tangem.blockchain.common.BlockchainSdkError import com.tangem.blockchain.common.Wallet import com.tangem.blockchain.common.WalletManager import com.tangem.common.services.Result import com.tangem.tap.domain.TapError import com.tangem.tap.domain.extensions.amountToCreateAccount -import com.tangem.tap.domain.extensions.isNoAccountError import com.tangem.tap.domain.getFirstToken import com.tangem.tap.domain.topup.TradeCryptoHelper import com.tangem.tap.features.wallet.redux.AddressData @@ -26,7 +26,7 @@ suspend fun WalletManager.safeUpdate(): Result = try { val blockchain = wallet.blockchain val amountToCreateAccount = blockchain.amountToCreateAccount(wallet.getFirstToken()) - if (blockchain.isNoAccountError(exception) && amountToCreateAccount != null) { + if (exception is BlockchainSdkError.AccountNotFound && amountToCreateAccount != null) { Result.Failure(TapError.WalletManagerUpdate.NoAccountError(amountToCreateAccount.toString())) } else { val message = exception.localizedMessage ?: "An error has occurred. Try later" diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/Blockchain.kt b/app/src/main/java/com/tangem/tap/domain/extensions/Blockchain.kt index 0563eb8482..673ef1787b 100644 --- a/app/src/main/java/com/tangem/tap/domain/extensions/Blockchain.kt +++ b/app/src/main/java/com/tangem/tap/domain/extensions/Blockchain.kt @@ -6,14 +6,6 @@ import com.tangem.common.card.EllipticCurve import java.math.BigDecimal -fun Blockchain.isNoAccountError(exception: Throwable): Boolean { - return when (this) { - Blockchain.Stellar, Blockchain.XRP -> - exception.message?.contains("Account not found") == true - else -> false - } -} - fun Blockchain.amountToCreateAccount(token: Token? = null): BigDecimal? { return when (this) { Blockchain.Stellar -> if (token?.symbol == NODL) BigDecimal(1.5) else BigDecimal.ONE diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt index 9243d5953e..572658b793 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt @@ -205,13 +205,13 @@ private fun sendTransaction( } is SimpleResult.Failure -> { when (sendResult.error) { - is CreateAccountUnderfunded -> { - val error = sendResult.error as CreateAccountUnderfunded + is BlockchainSdkError.CreateAccountUnderfunded -> { + val error = sendResult.error as BlockchainSdkError.CreateAccountUnderfunded val reserve = error.minReserve.value?.stripZeroPlainString() ?: "0" val symbol = error.minReserve.currencySymbol dispatch(SendAction.SendError(TapError.CreateAccountUnderfunded(listOf(reserve, symbol)))) } - is SendException -> { + is BlockchainSdkError.SendException -> { sendResult.error?.let { FirebaseCrashlytics.getInstance().recordException(it) } } is Throwable -> { From 0a608c8d904887eca8fc643b0e2b3c15f9d9de85 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 1 Feb 2022 18:26:55 +0300 Subject: [PATCH 15/20] Updated on 2026-08-14 --- app/src/main/assets/solana_tokens.json | 519 ++++++++++++++++++ .../main/assets/solana_tokens_testnet.json | 14 + 2 files changed, 533 insertions(+) create mode 100644 app/src/main/assets/solana_tokens.json create mode 100644 app/src/main/assets/solana_tokens_testnet.json diff --git a/app/src/main/assets/solana_tokens.json b/app/src/main/assets/solana_tokens.json new file mode 100644 index 0000000000..7290017dfd --- /dev/null +++ b/app/src/main/assets/solana_tokens.json @@ -0,0 +1,519 @@ +[ + { + "decimalCount": 9, + "symbol": "SOLAPE", + "name": "SolAPE Finance", + "contractAddress": "GHvFFSZ9BctWsEc5nujR1MTmmJWY7tgQz2AXE6WVFtGN", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 1, + "symbol": "FLOOF", + "name": "FLOOF", + "contractAddress": "3jzdrXXKxwkBk82u2eCWASZLCKoZs1LQTg87HBEAmBJw", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 9, + "symbol": "MNDE", + "name": "Marinade", + "contractAddress": "MNDEFzGvMt87ueuHvVU9VcTqsAP5b3fTGPsHuuPA5ey", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 6, + "symbol": "BTC", + "name": "Wrapped BTC", + "contractAddress": "9n4nbM75f5Ui33ZbPYXn59EwSgE8CGsHtAeTH5YFeJ9E", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 8, + "symbol": "TWT", + "name": "Trust Wallet", + "contractAddress": "HZNpqL7RT9gxf9eWoWsWzC5DfjzQ41XTQgEA7p3VzaaD", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 9, + "symbol": "CCAI", + "name": "Cryptocurrencies.Ai", + "contractAddress": "E5ndSkaB17Dm7CsD22dvcjfrYSDLCxFcMd6z8ddCk5wp", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 6, + "symbol": "ETH", + "name": "Wrapped ETH", + "contractAddress": "2FPyTwcZLUg1MDrwsyoP4D6s1tM7hAkHYRjkNb5w6Pxk", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 6, + "symbol": "XRP", + "name": "Wrapped XRP", + "contractAddress": "Ga2AXHpfAF6mv2ekZwcsJFqu7wB4NV331qNH7fW9Nst8", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 5, + "symbol": "KIN", + "name": "Kin", + "contractAddress": "kinXdEcpDQeHPEuQnqmUgtYykqKGVFq6CeVX5iAHJq6", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 9, + "symbol": "mSOL", + "name": "Marinade staked", + "contractAddress": "mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 9, + "symbol": "GENE", + "name": "Genopets", + "contractAddress": "GENEtH5amGSi8kHAtQoezp1XEXwZJ8vcuePYnXdKrMYz", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 9, + "symbol": "MM", + "name": "Million", + "contractAddress": "FiCiuX9DetEE89PgRAU1hmoptnem8b1fkpEq8PGYTYkd", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 9, + "symbol": "DFL", + "name": "DeFi Land", + "contractAddress": "DFL1zNkaGPWm1BqAVqRjCZvHmwTFrEaJtbzJWgseoNJh", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 6, + "symbol": "CAVE", + "name": "Crypto Cavemen", + "contractAddress": "4SZjjNABoqhbd4hnapbvoEPEqT8mnNkfbEoAwALf1V8t", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 6, + "symbol": "FTT", + "name": "Wrapped FTX", + "contractAddress": "AGFEad2et2ZJif9jaGpdMixQqvW5i81aBdvKe7PHNfz3", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 9, + "symbol": "SOL", + "name": "Wrapped SOL", + "contractAddress": "So11111111111111111111111111111111111111112", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 8, + "symbol": "ATLAS", + "name": "Star Atlas", + "contractAddress": "ATLASXmbPQxBUYbxPsV97usA3fPQYEqzQBUHgiFCUsXx", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 4, + "symbol": "BRZ", + "name": "Brazilian Digital Token", + "contractAddress": "FtgGSFADXBtroxq8VCausXRr2of47QBf5AS1NtZCu4GD", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 6, + "symbol": "USDC", + "name": "USD Coin", + "contractAddress": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 6, + "symbol": "SLIM", + "name": "Solanium", + "contractAddress": "xxxxa1sKNGwFtw2kFn8XauW9xq8hBZ5kVtcSesTT9fW", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 6, + "symbol": "USDT", + "name": "Wrapped USDT", + "contractAddress": "BQcdHdAQW1hczDbBi9hiegXAR7A98Q9jx3X3iBBBDiq4", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 9, + "symbol": "HOLD", + "name": "Holdana", + "contractAddress": "8GQsW3f7mdwfjqJon2myADcBsSsRjpXmxHYDG8q1pvV6", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 6, + "symbol": "MAPS", + "name": "Maps.me", + "contractAddress": "MAPS41MDahZ9QdKXhVa4dWB9RuyfV4XqhyAZ8XcYepb", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 6, + "symbol": "SRM", + "name": "Serum", + "contractAddress": "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 9, + "symbol": "JET", + "name": "Jet Protocol", + "contractAddress": "JET6zMJWkCN9tpRT2v2jfAmm5VnQFDpUBCyaKojmGtz", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 9, + "symbol": "STNK", + "name": "Stonks", + "contractAddress": "43VWkd99HjqkhFTZbWBpMpRhjG469nWa7x7uEsgSH7We", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 9, + "symbol": "SKEM", + "name": "SKEM", + "contractAddress": "3x7UeXDF4imKSKnizK9mYyx1M5bTNzpeALfPeB8S6XT9", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 6, + "symbol": "MEDIA", + "name": "Media Network", + "contractAddress": "ETAtLmCmsoiEEKfNrHKJ2kYy3MoABhU6NQvpSfij5tDs", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 9, + "symbol": "ROPE", + "name": "Rope Token", + "contractAddress": "8PMHT4swUMtBzgHnh5U564N5sjPSiUz2cjEQzFnnP1Fo", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 8, + "symbol": "POLIS", + "name": "Star Atlas DAO", + "contractAddress": "poLisWXnNRwC6oBu1vHiuKQzFjGL4XDSu4g9qjz9qVk", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 6, + "symbol": "RAY", + "name": "Raydium", + "contractAddress": "4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 6, + "symbol": "Wrapped FRONT", + "name": "Wrapped FRONT", + "contractAddress": "9S4t2NEAiJVMvPdRYKVrfJpBafPBLtvbvyS3DecojQHw", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 6, + "symbol": "USDC", + "name": "Wrapped USDC", + "contractAddress": "BXXkv6z8ykpG1yuvUDPgh732wzVHB69RnB9YgSYh3itW", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 6, + "symbol": "USDT", + "name": "USDT", + "contractAddress": "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 6, + "symbol": "ORCA", + "name": "Orca", + "contractAddress": "orcaEKTdK7LKz57vaAYr9QeNsVEPfiu6QeMU1kektZE", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 6, + "symbol": "LIQ", + "name": "LIQ Protocol", + "contractAddress": "4wjPQJ6PrkC4dHhYghwJzGBVP78DkBzA2U3kHoFNBuhj", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 6, + "symbol": "WOOF", + "name": "WOOFENOMICS", + "contractAddress": "9nEqaUcb16sQ3Tn1psbkWqyhPdLmfHWjKGymREjsAgTE", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 9, + "symbol": "STEP", + "name": "Step Finance", + "contractAddress": "StepAscQoEioFxxWGnh2sLBDFp9d8rvKz2Yp39iDpyT", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 9, + "symbol": "FOUR", + "name": "The 4th Pillar Token", + "contractAddress": "DAtU322C23YpoZyWBm8szk12QyqHa9rUQe1EYXzbm1JE", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 6, + "symbol": "OXY", + "name": "Oxygen Protocol", + "contractAddress": "z3dn17yLaGMKffVogeFHQ9zWVcXgqgf3PQnDsNs2g6M", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 7, + "symbol": "ISLAMI", + "name": "ISLAMICOIN", + "contractAddress": "HG2FnifX1WPY6UvnkXnMx2kAt4E4Ssu6DZ2aiB5RmLHo", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 9, + "symbol": "AURY", + "name": "Aurory", + "contractAddress": "AURYydfxJib1ZkTir1Jn1J9ECYUtjb6rKQVmtYaixWPP", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 6, + "symbol": "COPE", + "name": "COPE", + "contractAddress": "8HGyAAB1yoM1ttS7pXjHMa3dukTFGQggnFFH3hJZgzQh", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 6, + "symbol": "FIDA", + "name": "Bonfida", + "contractAddress": "EchesyfXePKdLtoiZSL8pBe8Myagyy8ZRqsACNCFGnvp", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 6, + "symbol": "wCCX", + "name": "Wrapped Conceal", + "contractAddress": "CzqJTjTp3R9opDHmzF5Gy1G2NKDHEyMufa4Keiju4PLu", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 0, + "symbol": "XCOPE", + "name": "XCOPE", + "contractAddress": "3K6rftdAaQYMPunrtNRHgnK2UAtjm2JwyT2oCiTDouYE", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + }, + { + "decimalCount": 6, + "symbol": "MNGO", + "name": "Mango Markets", + "contractAddress": "MangoCzJ36AjZyKwVj3VnYU4GTonjfVEnJmvvWaxLac", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": false + } + } +] \ No newline at end of file diff --git a/app/src/main/assets/solana_tokens_testnet.json b/app/src/main/assets/solana_tokens_testnet.json new file mode 100644 index 0000000000..07f315f75f --- /dev/null +++ b/app/src/main/assets/solana_tokens_testnet.json @@ -0,0 +1,14 @@ +[ + { + "decimalCount" : 9, + "symbol" : "TCA", + "name" : "Tangem Coin A", + "contractAddress" : "22PTNbX31Zuztd6nD82fC8nQdT2hUfWv9XWXKuDkrFqR" + }, + { + "decimalCount" : 9, + "symbol" : "TCB", + "name" : "Tangem Coin B", + "contractAddress" : "HmSghNPg6KCk711YJA92aPejt8auyFkvmaED6jbHfUs4" + } +] \ No newline at end of file From 7571527c23e3e0abea86bec0950bef8bf7ff1a1c Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 1 Feb 2022 18:27:50 +0300 Subject: [PATCH 16/20] Updated on 2026-08-14 --- .../tap/features/wallet/redux/WalletAction.kt | 1 + .../tap/features/wallet/redux/WalletState.kt | 8 ++- .../middlewares/MultiWalletMiddleware.kt | 23 ++++++++ .../wallet/redux/reducers/WalletReducer.kt | 11 ++++ .../wallet/ui/WalletDetailsFragment.kt | 18 +++++- .../res/layout/fragment_details_confirm.xml | 4 +- .../res/layout/fragment_wallet_details.xml | 57 +++++++++++-------- .../res/layout/layout_wallet_details_rent.xml | 47 +++++++++++++++ .../main/res/values/strings_untranslated.xml | 2 + 9 files changed, 143 insertions(+), 28 deletions(-) create mode 100644 app/src/main/res/layout/layout_wallet_details_rent.xml diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt index 1081a3303c..029b6b88b5 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt @@ -146,4 +146,5 @@ sealed class WalletAction : Action { data class ChangeSelectedAddress(val type: AddressType) : WalletAction() + data class SetWalletRent(val blockchain: Blockchain, val minRent: String, val rentExempt: String): WalletAction() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt index 96044f0e1c..4dd980e98c 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt @@ -246,7 +246,8 @@ data class WalletData( val fiatRateString: String? = null, val fiatRate: BigDecimal? = null, val mainButton: WalletMainButton = WalletMainButton.SendButton(false), - val currency: Currency + val currency: Currency, + val walletRent: WalletRent? = null, ) { fun shouldShowMultipleAddress(): Boolean { val listOfAddresses = walletAddresses?.list ?: return false @@ -254,6 +255,11 @@ data class WalletData( } } +data class WalletRent( + val minRentValue: String, + val rentExemptValue: String +) + sealed interface Currency { val blockchain: com.tangem.blockchain.common.Blockchain diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt index b7444dcb16..df4c9120f7 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt @@ -1,10 +1,12 @@ package com.tangem.tap.features.wallet.redux.middlewares +import com.tangem.blockchain.blockchains.solana.RentProvider import com.tangem.blockchain.common.* import com.tangem.blockchain.extensions.Result import com.tangem.common.extensions.isZero import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.safeUpdate +import com.tangem.tap.common.extensions.stripZeroPlainString import com.tangem.tap.common.redux.global.GlobalState import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction @@ -31,6 +33,7 @@ class MultiWalletMiddleware { when (action) { is WalletAction.MultiWallet.AddWalletManagers -> { globalState.feedbackManager?.infoHolder?.setWalletsInfo(action.walletManagers) + action.walletManagers.forEach { checkForRentWarning(it) } } is WalletAction.MultiWallet.SelectWallet -> { if (action.walletData != null) { @@ -197,6 +200,26 @@ class MultiWalletMiddleware { } } + private fun checkForRentWarning(walletManager: WalletManager) { + val rentProvider = walletManager as? RentProvider ?: return + + scope.launch { + when (val result = rentProvider.minimalBalanceForRentExemption()) { + is Result.Success -> { + val currency = walletManager.wallet.blockchain.currency + val minRent = ("${rentProvider.rentAmount().stripZeroPlainString()} $currency") + val rentExempt = ("${result.data.stripZeroPlainString()} $currency") + store.dispatchOnMain(WalletAction.SetWalletRent( + blockchain = walletManager.wallet.blockchain, + minRent = minRent, + rentExempt = rentExempt + )) + } + is Result.Failure -> {} + } + } + } + private fun addTokens( tokens: List, walletState: WalletState?, diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt index 097fbf28e3..7005235044 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt @@ -245,6 +245,17 @@ private fun internalReduce(action: Action, state: AppState): WalletState { ) newState = newState.copy(wallets = wallets) } + is WalletAction.SetWalletRent -> { + var walletData = newState.getWalletData(action.blockchain) + if (walletData == null) { + newState + } else { + walletData = walletData.copy(walletRent = WalletRent(action.minRent, action.rentExempt)) + newState = newState.copy( + wallets = newState.replaceSomeWallets(listOf(walletData)) + ) + } + } } return newState } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt index 41a3dc9258..bfd6057d73 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt @@ -10,6 +10,7 @@ import androidx.fragment.app.Fragment import androidx.recyclerview.widget.LinearLayoutManager import androidx.transition.TransitionInflater import com.squareup.picasso.Picasso +import com.tangem.common.extensions.guard import com.tangem.tap.common.SnackbarHandler import com.tangem.tap.common.extensions.* import com.tangem.tap.common.redux.StateDialog @@ -27,6 +28,8 @@ import kotlinx.android.synthetic.main.item_popular_token.view.* import kotlinx.android.synthetic.main.layout_balance_error.* import kotlinx.android.synthetic.main.layout_balance_wallet_details.* import kotlinx.android.synthetic.main.layout_wallet_details.* +import kotlinx.android.synthetic.main.layout_wallet_details.card_balance +import kotlinx.android.synthetic.main.layout_wallet_details_rent.* import org.rekotlin.StoreSubscriber class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreSubscriber { @@ -124,11 +127,12 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS handleDialogs(state.walletDialog) handleCurrencyIcon(selectedWallet) + handleWalletRent(selectedWallet.walletRent) srl_wallet_details.setOnRefreshListener { if (selectedWallet.currencyData.status != BalanceStatus.Loading) { store.dispatch(WalletAction.LoadWallet( - blockchain = selectedWallet.currency?.blockchain + blockchain = selectedWallet.currency.blockchain )) } } @@ -141,6 +145,18 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS btn_sell.show(selectedWallet.tradeCryptoState.sellingAllowed) } + private fun handleWalletRent(rent: WalletRent?) { + val rent = rent.guard { + l_rent_warning.hide() + return + } + + val warningMessage = requireContext().getString( + R.string.solana_rent_warning, rent.minRentValue, rent.rentExemptValue) + tv_rent_warning_message.text = warningMessage + l_rent_warning.show() + } + private fun handleCurrencyIcon(wallet: WalletData) { Picasso.get().loadCurrenciesIcon( imageView = iv_currency, diff --git a/app/src/main/res/layout/fragment_details_confirm.xml b/app/src/main/res/layout/fragment_details_confirm.xml index 2a6933b594..5d992196b0 100644 --- a/app/src/main/res/layout/fragment_details_confirm.xml +++ b/app/src/main/res/layout/fragment_details_confirm.xml @@ -42,7 +42,7 @@ app:layout_constraintTop_toTopOf="parent" /> diff --git a/app/src/main/res/layout/fragment_wallet_details.xml b/app/src/main/res/layout/fragment_wallet_details.xml index 27da2ac50f..0f6ef2b42d 100644 --- a/app/src/main/res/layout/fragment_wallet_details.xml +++ b/app/src/main/res/layout/fragment_wallet_details.xml @@ -21,9 +21,9 @@ android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" + android:theme="@style/ThemeOverlay.MyTheme.Toolbar" app:menu="@menu/wallet_details" app:navigationIcon="@drawable/ic_baseline_arrow_back_24" - android:theme="@style/ThemeOverlay.MyTheme.Toolbar" app:title=" " /> @@ -46,7 +46,6 @@ android:layout_width="match_parent" android:layout_height="wrap_content"> - - + - - + + diff --git a/app/src/main/res/layout/layout_wallet_details_rent.xml b/app/src/main/res/layout/layout_wallet_details_rent.xml new file mode 100644 index 0000000000..1f44d10ea7 --- /dev/null +++ b/app/src/main/res/layout/layout_wallet_details_rent.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml index 32f666e20c..40634c9ea5 100644 --- a/app/src/main/res/values/strings_untranslated.xml +++ b/app/src/main/res/values/strings_untranslated.xml @@ -311,4 +311,6 @@ Price: %s\n Amount to receive: %s\n Amount to pay: %s + Solana network charges a rent of %s every 2 days. Accounts that can’t afford the rent are purged from the network. Deposit your account with more than %s to use it for free. + From a1e86cb6fce7cd78bdbe6ede86c3b625bfa29589 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 2 Feb 2022 13:17:36 +0300 Subject: [PATCH 17/20] Updated on 2026-08-14 --- app/build.gradle | 2 +- app/src/main/assets/avalanche_tokens.json | 1637 +++++++++++++++++ .../main/assets/avalanche_tokens_testnet.json | 24 + .../main/assets/binance_tokens_testnet.json | 2 +- .../main/assets/solana_tokens_testnet.json | 26 +- .../tap/domain/tokens/CurrenciesRepository.kt | 37 +- .../features/tokens/redux/CurrencyListItem.kt | 36 +- .../main/res/values/strings_untranslated.xml | 1 + 8 files changed, 1731 insertions(+), 34 deletions(-) create mode 100644 app/src/main/assets/avalanche_tokens.json create mode 100644 app/src/main/assets/avalanche_tokens_testnet.json diff --git a/app/build.gradle b/app/build.gradle index 310b18fa9f..d4e92b6555 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,7 +77,7 @@ dependencies { implementation 'com.google.android.play:core-ktx:1.8.1' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' - implementation 'com.tangem:blockchain:develop-57' + implementation 'com.tangem:blockchain:develop-59' implementation 'com.tangem.tangem-sdk-kotlin:core:develop-127' implementation 'com.tangem.tangem-sdk-kotlin:android:develop-127' diff --git a/app/src/main/assets/avalanche_tokens.json b/app/src/main/assets/avalanche_tokens.json new file mode 100644 index 0000000000..c9a1b2e5d9 --- /dev/null +++ b/app/src/main/assets/avalanche_tokens.json @@ -0,0 +1,1637 @@ +[ + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/ava-labs/bridge-tokens/main/avalanche-tokens/0x1ECd47FF4d9598f89721A2866BFEb99505a413Ed/logo.png", + "symbol": "AVME", + "name": "AV Me", + "contractAddress": "0x1ECd47FF4d9598f89721A2866BFEb99505a413Ed", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x9F87C6c30F4b23C7B51Aa7465A9e0A836514700D/logo.png", + "symbol": "TERRA", + "name": "AVATerra", + "contractAddress": "0x9F87C6c30F4b23C7B51Aa7465A9e0A836514700D", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/truemiller/tokens/main/assets/0x4Aca0ad6357b918e3d06BB1a0BCC403619177523/logo.png", + "symbol": "SONIC", + "name": "AVAXRush.com - Sonic Token", + "contractAddress": "0x4Aca0ad6357b918e3d06BB1a0BCC403619177523", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x9E037dE681CaFA6E661e6108eD9c2bd1AA567Ecd/logo.png", + "symbol": "WALBT", + "name": "AllianceBlock Token", + "contractAddress": "0x9E037dE681CaFA6E661e6108eD9c2bd1AA567Ecd", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x2147EFFF675e4A4eE1C2f918d181cDBd7a8E208f/logo.png", + "symbol": "ALPHA.e", + "name": "Alpha Finance Lab Token", + "contractAddress": "0x2147EFFF675e4A4eE1C2f918d181cDBd7a8E208f", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 9.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x027dbcA046ca156De9622cD1e2D907d375e53aa7/logo.png", + "symbol": "AMPL", + "name": "Ampleforth", + "contractAddress": "0x027dbcA046ca156De9622cD1e2D907d375e53aa7", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xE6B9d092223f39013656702A40dbE6B7DeCc5746/logo.png", + "symbol": "ANGLE", + "name": "Angle", + "contractAddress": "0xE6B9d092223f39013656702A40dbE6B7DeCc5746", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x6C6f910A79639dcC94b4feEF59Ff507c2E843929/logo.png", + "symbol": "aAVAXb", + "name": "Ankr Avalanche Reward Earning Bond", + "contractAddress": "0x6C6f910A79639dcC94b4feEF59Ff507c2E843929", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x938FE3788222A74924E062120E7BFac829c719Fb/logo.png", + "symbol": "APEIN", + "name": "Ape In", + "contractAddress": "0x938FE3788222A74924E062120E7BFac829c719Fb", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 9.0, + "symbol": "APE-X", + "name": "Ape-X", + "contractAddress": "0xd039C9079ca7F2a87D632A9C0d7cEa0137bAcFB5", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/avalaunch-app/xava-protocol/master/logo.png", + "symbol": "XAVA", + "name": "Avalaunch", + "contractAddress": "0xd1c3f94DE7e5B45fa4eDBBA472491a9f4B166FC4", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x78ea17559B3D2CF85a7F9C2C704eda119Db5E6dE/logo.png", + "symbol": "AVE", + "name": "Avaware", + "contractAddress": "0x78ea17559B3D2CF85a7F9C2C704eda119Db5E6dE", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 6.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x397bBd6A0E41bdF4C3F971731E180Db8Ad06eBc1/logo.png", + "symbol": "AVXT", + "name": "Avaxtars Token", + "contractAddress": "0x397bBd6A0E41bdF4C3F971731E180Db8Ad06eBc1", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xee0f5c0c2c8cFB1473d441868C174de3F3736F70/logo.png", + "symbol": "BPNG", + "name": "BABYPANGOLIN", + "contractAddress": "0xee0f5c0c2c8cFB1473d441868C174de3F3736F70", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 9.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x62a4f3280C02C8Cc3E9ff984e4aaD94f8F7fEA26/logo.png", + "symbol": "BABYPangolin", + "name": "BABYPangolin", + "contractAddress": "0x62a4f3280C02C8Cc3E9ff984e4aaD94f8F7fEA26", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x8729438EB15e2C8B576fCc6AeCdA6A148776C0F5/logo.png", + "symbol": "QI", + "name": "BENQI", + "contractAddress": "0x8729438EB15e2C8B576fCc6AeCdA6A148776C0F5", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x8cCF617Aa653463157c124229844EB688C4f2A04/logo.png", + "symbol": "BUX", + "name": "BUX Token", + "contractAddress": "0x8cCF617Aa653463157c124229844EB688C4f2A04", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/ava-labs/bridge-tokens/main/avalanche-tokens/0xd6070ae98b8069de6B494332d1A1a81B6179D960/logo.png", + "symbol": "BIFI", + "name": "Beefy Finance", + "contractAddress": "0xd6070ae98b8069de6B494332d1A1a81B6179D960", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/ava-labs/bridge-tokens/main/avalanche-tokens/0x264c1383EA520f73dd837F915ef3a732e204a493/logo.png", + "symbol": "BNB", + "name": "Binance", + "contractAddress": "0x264c1383EA520f73dd837F915ef3a732e204a493", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x0f34919404a290e71fc6A510cB4a6aCb8D764b24/logo.png", + "symbol": "BLZZ", + "name": "Blizz Finance Token", + "contractAddress": "0x0f34919404a290e71fc6A510cB4a6aCb8D764b24", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xB147656604217a03Fe2c73c4838770DF8d9D21B8/logo.png", + "symbol": "BLIZZ", + "name": "Blizzard", + "contractAddress": "0xB147656604217a03Fe2c73c4838770DF8d9D21B8", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 8.0, + "customIconUrl": "https://github.com/blocknetdx/documentation/blob/master/docs/img/icons/Blocknet_symbol_dark_RGB.png", + "symbol": "aaBLOCK", + "name": "Blocknet", + "contractAddress": "0xC931f61B1534EB21D8c11B24f3f5Ab2471d4aB50", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xc12e249FaBe1c5Eb7C558E5F50D187687a244E31/logo.png", + "symbol": "BLUE", + "name": "Blue Token", + "contractAddress": "0xc12e249FaBe1c5Eb7C558E5F50D187687a244E31", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/BooFinance/assets/main/logo.png", + "symbol": "BOOFI", + "name": "BooFinance", + "contractAddress": "0xB00F1ad977a949a3CCc389Ca1D1282A2946963b0", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xF2CfC11093edB5A2DC7F49e70A3a3a9cd4F4FeE4/logo.png", + "symbol": "BRIG", + "name": "Brig Finance Token", + "contractAddress": "0xF2CfC11093edB5A2DC7F49e70A3a3a9cd4F4FeE4", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/complusnetwork/default-token-list/master/src/ava/0x3711c397B6c8F7173391361e27e67d72F252cAad/logo.png", + "symbol": "COM", + "name": "COMPLUS", + "contractAddress": "0x3711c397B6c8F7173391361e27e67d72F252cAad", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x8aE8be25C23833e0A01Aa200403e826F611f9CD2/logo.png", + "symbol": "CRAFT", + "name": "CRAFT", + "contractAddress": "0x8aE8be25C23833e0A01Aa200403e826F611f9CD2", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/qualitycabbage/token/main/logo.png", + "symbol": "CABAG", + "name": "Cabbage.Cash", + "contractAddress": "0xdb333724fAE72b4253FC3d44c8270CBBC86d147b", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/ava-labs/bridge-tokens/main/avalanche-tokens/0x8D88e48465F30Acfb8daC0b3E35c9D6D7d36abaf/logo.png", + "symbol": "CNR", + "name": "Canary", + "contractAddress": "0x8D88e48465F30Acfb8daC0b3E35c9D6D7d36abaf", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/ava-labs/bridge-tokens/main/avalanche-tokens/0x76076880e1EBBcE597e6E15c47386cd34de4930F/logo.png", + "symbol": "OPUS", + "name": "Canopus", + "contractAddress": "0x76076880e1EBBcE597e6E15c47386cd34de4930F", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x7761E2338B35bCEB6BdA6ce477EF012bde7aE611/logo.png", + "symbol": "EGG", + "name": "Chikn Egg", + "contractAddress": "0x7761E2338B35bCEB6BdA6ce477EF012bde7aE611", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xab592d197ACc575D16C3346f4EB70C703F308D1E/logo.png", + "symbol": "FEED", + "name": "Chikn Feed", + "contractAddress": "0xab592d197ACc575D16C3346f4EB70C703F308D1E", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xec3492a2508DDf4FDc0cD76F31f340b30d1793e6/logo.png", + "symbol": "CLY", + "name": "Colony Token", + "contractAddress": "0xec3492a2508DDf4FDc0cD76F31f340b30d1793e6", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x2923E182A6e68C0fE4006736a33B32f805703683/logo.png", + "symbol": "COMET", + "name": "Comet Nodes", + "contractAddress": "0x2923E182A6e68C0fE4006736a33B32f805703683", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x637afeff75ca669fF92e4570B14D6399A658902f/logo.png", + "symbol": "COOK", + "name": "Cook Finance", + "contractAddress": "0x637afeff75ca669fF92e4570B14D6399A658902f", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xA32608e873F9DdEF944B24798db69d80Bbb4d1ed/logo.png", + "symbol": "CRA", + "name": "Crabada", + "contractAddress": "0xA32608e873F9DdEF944B24798db69d80Bbb4d1ed", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 9.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x4939B3313E73ae8546b90e53E998E82274afDbDB/logo.png", + "symbol": "CCC", + "name": "Cross Chain Capital V2", + "contractAddress": "0x4939B3313E73ae8546b90e53E998E82274afDbDB", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/CycleProtocol/assets/master/cycle-logo-round-small.png", + "symbol": "CYCLE", + "name": "Cycle Protocol", + "contractAddress": "0x81440C939f2C1E34fc7048E518a637205A632a74", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/ava-labs/bridge-tokens/main/avalanche-tokens/0x961C8c0B1aaD0c0b10a51FeF6a867E3091BCef17/logo.png", + "symbol": "DYP", + "name": "DeFi Yield Protocol", + "contractAddress": "0x961C8c0B1aaD0c0b10a51FeF6a867E3091BCef17", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x47EB6F7525C1aA999FBC9ee92715F5231eB1241D/logo.png", + "symbol": "MELT", + "name": "Defrost Finance Token", + "contractAddress": "0x47EB6F7525C1aA999FBC9ee92715F5231eB1241D", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x1DB749847C4abB991d8B6032102383e6BfD9B1c7/logo.png", + "symbol": "DON", + "name": "Dogeon Token", + "contractAddress": "0x1DB749847C4abB991d8B6032102383e6BfD9B1c7", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x100Cc3a819Dd3e8573fD2E46D1E66ee866068f30/logo.png", + "symbol": "DCAU", + "name": "Dragon Crypto Aurum", + "contractAddress": "0x100Cc3a819Dd3e8573fD2E46D1E66ee866068f30", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xf57b80A574297892B64E9a6c997662889b04a73a/logo.png", + "symbol": "EXP", + "name": "EXP Token", + "contractAddress": "0xf57b80A574297892B64E9a6c997662889b04a73a", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xAcD7B3D9c10e97d0efA418903C0c7669E702E4C0/logo.png", + "symbol": "ELE", + "name": "Eleven.finance", + "contractAddress": "0xAcD7B3D9c10e97d0efA418903C0c7669E702E4C0", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xE1C110E1B1b4A1deD0cAf3E42BfBdbB7b5d7cE1C/logo.png", + "symbol": "ELK", + "name": "Elk", + "contractAddress": "0xE1C110E1B1b4A1deD0cAf3E42BfBdbB7b5d7cE1C", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x3ACa5545e76746A3Fe13eA66B24BC0eBcC51E6b4/logo.png", + "symbol": "EVRT", + "name": "Everest Token", + "contractAddress": "0x3ACa5545e76746A3Fe13eA66B24BC0eBcC51E6b4", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 9.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xf6d46849DB378AE01D93732585BEc2C4480D1fD5/logo.png", + "symbol": "FORT", + "name": "Fortress", + "contractAddress": "0xf6d46849DB378AE01D93732585BEc2C4480D1fD5", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x214DB107654fF987AD859F34125307783fC8e387/logo.png", + "symbol": "FXS", + "name": "Frax Share", + "contractAddress": "0x214DB107654fF987AD859F34125307783fC8e387", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xD67de0e0a0Fd7b15dC8348Bb9BE742F3c5850454/logo.png", + "symbol": "FXS", + "name": "Frax Share - Anyswap", + "contractAddress": "0xD67de0e0a0Fd7b15dC8348Bb9BE742F3c5850454", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/FreeBitcoins-com/tokens/main/assets/0xCa7730D239f9fCA6d9683B0a62ed1A6c660D7CbA/logo.png", + "symbol": "FREE", + "name": "Freebitcoins", + "contractAddress": "0xCa7730D239f9fCA6d9683B0a62ed1A6c660D7CbA", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 6.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xcA220F1e486A8e35d6F1DCD62073Ad8DD04659ED/logo.png", + "symbol": "GLDB", + "name": "GLDB Token", + "contractAddress": "0xcA220F1e486A8e35d6F1DCD62073Ad8DD04659ED", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x595c8481c48894771CE8FaDE54ac6Bf59093F9E8/logo.png", + "symbol": "GAJ", + "name": "Gaj Finance", + "contractAddress": "0x595c8481c48894771CE8FaDE54ac6Bf59093F9E8", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 9.0, + "symbol": "GG", + "name": "Galaxy Goggle", + "contractAddress": "0xF2F7CE610a091B94d41D69f4fF1129434a82E2f0", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x999c891262ce01f1C1AFD1D46260E4c1E508B243/logo.png", + "symbol": "GIVE", + "name": "Give Token", + "contractAddress": "0x999c891262ce01f1C1AFD1D46260E4c1E508B243", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/gondola-finance/frontend-v2/master/src/assets/icons/colour.png", + "symbol": "GDL", + "name": "Gondola", + "contractAddress": "0xD606199557c8Ab6F4Cc70bD03FaCc96ca576f142", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 9.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x90842eb834cFD2A1DB0b1512B254a18E4D396215/logo.png", + "symbol": "GB", + "name": "Good Bridging", + "contractAddress": "0x90842eb834cFD2A1DB0b1512B254a18E4D396215", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x321E7092a180BB43555132ec53AaA65a5bF84251/logo.png", + "symbol": "gOHM", + "name": "Governance OHM", + "contractAddress": "0x321E7092a180BB43555132ec53AaA65a5bF84251", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xB15f02F9Da8CD1f99E9dd375F21dc96D25ddd82C/logo.png", + "symbol": "HERMES", + "name": "HERMES", + "contractAddress": "0xB15f02F9Da8CD1f99E9dd375F21dc96D25ddd82C", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xfa4B6db72A650601E7Bd50a0A9f537c9E98311B2/logo.png", + "symbol": "HSHARE", + "name": "HSHARE", + "contractAddress": "0xfa4B6db72A650601E7Bd50a0A9f537c9E98311B2", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xC7f4debC8072e23fe9259A5C0398326d8EfB7f5c/logo.png", + "symbol": "HeC", + "name": "HeroesChained", + "contractAddress": "0xC7f4debC8072e23fe9259A5C0398326d8EfB7f5c", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x9C8E99eb130AED653Ef90fED709D9C3E9cC8b269/logo.png", + "symbol": "HTZ", + "name": "Hertz Token", + "contractAddress": "0x9C8E99eb130AED653Ef90fED709D9C3E9cC8b269", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xce0052745A182fff07aCB9dd834041E5F29319b2/logo.png", + "symbol": "HIGGS", + "name": "Higgs", + "contractAddress": "0xce0052745A182fff07aCB9dd834041E5F29319b2", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x0fEc6d8A84A85b79A1FFE0E28c1902E08b653EFE/logo.png", + "symbol": "HOOP", + "name": "Hoopoe Ventures", + "contractAddress": "0x0fEc6d8A84A85b79A1FFE0E28c1902E08b653EFE", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x45C13620B55C35A5f539d26E88247011Eb10fDbd/logo.png", + "symbol": "HCT", + "name": "Hurricane Token", + "contractAddress": "0x45C13620B55C35A5f539d26E88247011Eb10fDbd", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/safepoint-be/project-husky/main/img/logo.png", + "symbol": "HUSKY", + "name": "Husky Avalanche", + "contractAddress": "0x65378b697853568dA9ff8EaB60C13E1Ee9f4a654", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 9.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xBf9D7eCC6D5f09B9a10b8a39921E6f9cbA3c5D48/logo.png", + "symbol": "ICEDOGE", + "name": "ICEDOGE", + "contractAddress": "0xBf9D7eCC6D5f09B9a10b8a39921E6f9cbA3c5D48", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xeA6887e4a9CdA1B77E70129E5Fba830CdB5cdDef/logo.png", + "symbol": "IMX", + "name": "IMX", + "contractAddress": "0xeA6887e4a9CdA1B77E70129E5Fba830CdB5cdDef", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xF891214fdcF9cDaa5fdC42369eE4F27F226AdaD6/logo.png", + "symbol": "IME", + "name": "Imperium Empires Token", + "contractAddress": "0xF891214fdcF9cDaa5fdC42369eE4F27F226AdaD6", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x544c42fBB96B39B21DF61cf322b5EDC285EE7429/logo.png", + "symbol": "INSUR", + "name": "InsurAce", + "contractAddress": "0x544c42fBB96B39B21DF61cf322b5EDC285EE7429", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xfC108f21931576a21D0b4b301935DAc80d9E5086/logo.png", + "symbol": "IronICE", + "name": "Iron Finance ICE", + "contractAddress": "0xfC108f21931576a21D0b4b301935DAc80d9E5086", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x3EeFb18003D033661f84e48360eBeCD181A84709/logo.png", + "symbol": "ISA", + "name": "Islander", + "contractAddress": "0x3EeFb18003D033661f84e48360eBeCD181A84709", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xdE61d83F83950fDe967BD2C68B1a9f64694F85Ff/logo.png", + "symbol": "JENKEM", + "name": "JENKEM", + "contractAddress": "0xdE61d83F83950fDe967BD2C68B1a9f64694F85Ff", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x4f60a160D8C2DDdaAfe16FCC57566dB84D674BD6/logo.png", + "symbol": "JEWEL", + "name": "Jewels", + "contractAddress": "0x4f60a160D8C2DDdaAfe16FCC57566dB84D674BD6", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x6e84a6216eA6dACC71eE8E6b0a5B7322EEbC0fDd/logo.png", + "symbol": "JOE", + "name": "Joe Token", + "contractAddress": "0x6e84a6216eA6dACC71eE8E6b0a5B7322EEbC0fDd", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x45CdaF3Fd17BD31d9830Fa977159162DD2431683/logo.png", + "symbol": "KIOO", + "name": "KIOO", + "contractAddress": "0x45CdaF3Fd17BD31d9830Fa977159162DD2431683", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xb27c8941a7Df8958A1778c0259f76D1F8B711C35/logo.png", + "symbol": "KLO", + "name": "Kalao Token", + "contractAddress": "0xb27c8941a7Df8958A1778c0259f76D1F8B711C35", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xbca7f1998Dc9FFB70b086543a808960a460aBcA7/logo.png", + "symbol": "KITTY", + "name": "Kitty Token", + "contractAddress": "0xbca7f1998Dc9FFB70b086543a808960a460aBcA7", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x5BcDE17c50652aD6148C970F10Bf06647137A7E4/logo.png", + "symbol": "LOA", + "name": "LOAlexandria", + "contractAddress": "0x5BcDE17c50652aD6148C970F10Bf06647137A7E4", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 9.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x5684a087C739A2e845F4AaAaBf4FBd261edc2bE8/logo.png", + "symbol": "LF", + "name": "Life DAO Token", + "contractAddress": "0x5684a087C739A2e845F4AaAaBf4FBd261edc2bE8", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xff579d6259dEDcc80488c9b89d2820bCb5609160/logo.png", + "symbol": "LVT", + "name": "Louverture", + "contractAddress": "0xff579d6259dEDcc80488c9b89d2820bCb5609160", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/ava-labs/bridge-tokens/main/avalanche-tokens/0x4C9B4E1AC6F24CdE3660D5E4Ef1eBF77C710C084/logo.png", + "symbol": "LYD", + "name": "Lydia Finance", + "contractAddress": "0x4C9B4E1AC6F24CdE3660D5E4Ef1eBF77C710C084", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x1C59e9e44b7983931E0aBA8dd0b8571C5f34cC87/logo.png", + "symbol": "METL", + "name": "METL Token", + "contractAddress": "0x1C59e9e44b7983931E0aBA8dd0b8571C5f34cC87", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x9Fda7cEeC4c18008096C2fE2B85F05dc300F94d0/logo.png", + "symbol": "MFI", + "name": "MarginSwap", + "contractAddress": "0x9Fda7cEeC4c18008096C2fE2B85F05dc300F94d0", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 9.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x7C08413cbf02202a1c13643dB173f2694e0F73f0/logo.png", + "symbol": "MAXI", + "name": "Maximizer", + "contractAddress": "0x7C08413cbf02202a1c13643dB173f2694e0F73f0", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x885d748C00A279B67A7749EC6b03301700dd0455/logo.png", + "symbol": "MAXI", + "name": "Maximus", + "contractAddress": "0x885d748C00A279B67A7749EC6b03301700dd0455", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x921f99719Eb6C01b4B8f0BA7973A7C24891e740A/logo.png", + "symbol": "MAGE", + "name": "MetaBrands", + "contractAddress": "0x921f99719Eb6C01b4B8f0BA7973A7C24891e740A", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 9.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xed46443C18E38064523180Fc364C6180b35803d3/logo.png", + "symbol": "CROWN", + "name": "Midas DAO Token", + "contractAddress": "0xed46443C18E38064523180Fc364C6180b35803d3", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x721C299E6BF7D6a430d9bEA3364Ea197314bcE09/logo.png", + "symbol": "MILK2", + "name": "MilkyWay Token by SpaceSwap v2", + "contractAddress": "0x721C299E6BF7D6a430d9bEA3364Ea197314bcE09", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 12.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xdDAaAD7366B455AfF8E7c82940C43CEB5829B604/logo.png", + "symbol": "mYAK", + "name": "MiniYAK", + "contractAddress": "0xdDAaAD7366B455AfF8E7c82940C43CEB5829B604", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 6.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xf69c2fcd9128d49DfA22348C69177f9380438eB8/logo.png", + "symbol": "NFSG", + "name": "NFT Soccer Games", + "contractAddress": "0xf69c2fcd9128d49DfA22348C69177f9380438eB8", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x9E3Ca00f2d4A9E5d4f0add0900de5f15050812cF/logo.png", + "symbol": "NFTD", + "name": "NFTrade Token", + "contractAddress": "0x9E3Ca00f2d4A9E5d4f0add0900de5f15050812cF", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x937E077aBaEA52d3abf879c9b9d3f2eBd15BAA21/logo.png", + "symbol": "OH", + "name": "Oh Finance", + "contractAddress": "0x937E077aBaEA52d3abf879c9b9d3f2eBd15BAA21", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x0ebd9537A25f56713E34c45b38F421A1e7191469/logo.png", + "symbol": "OOE", + "name": "OpenOcean", + "contractAddress": "0x0ebd9537A25f56713E34c45b38F421A1e7191469", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x340fE1D898ECCAad394e2ba0fC1F93d27c7b717A/logo.png", + "symbol": "ORBS", + "name": "Orbs", + "contractAddress": "0x340fE1D898ECCAad394e2ba0fC1F93d27c7b717A", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x8B1d98A91F853218ddbb066F20b8c63E782e2430/logo.png", + "symbol": "ORCA", + "name": "OrcaDAO", + "contractAddress": "0x8B1d98A91F853218ddbb066F20b8c63E782e2430", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "symbol": "PNG", + "name": "Pangolin", + "contractAddress": "0x60781C2586D68229fde47564546784ab3fACA982", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x69A61f38Df59CBB51962E69C54D39184E21C27Ec/logo.png", + "symbol": "PARTY", + "name": "Partyswap", + "contractAddress": "0x69A61f38Df59CBB51962E69C54D39184E21C27Ec", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xfB98B335551a418cD0737375a2ea0ded62Ea213b/logo.png", + "symbol": "PENDLE", + "name": "Pendle Finance Token", + "contractAddress": "0xfB98B335551a418cD0737375a2ea0ded62Ea213b", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/Penguin-Finance/png-files/main/pefiv2_250x250.png", + "symbol": "PEFI", + "name": "Penguin Finance", + "contractAddress": "0xe896CDeaAC9615145c0cA09C8Cd5C25bced6384c", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x22d4002028f537599bE9f666d1c4Fa138522f9c8/logo.png", + "symbol": "PTP", + "name": "Platypus Token", + "contractAddress": "0x22d4002028f537599bE9f666d1c4Fa138522f9c8", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x7f041ce89A2079873693207653b24C15B5e6A293/logo.png", + "symbol": "LOOT", + "name": "Police and Thief Game", + "contractAddress": "0x7f041ce89A2079873693207653b24C15B5e6A293", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x7b2B702706D9b361dfE3f00bD138C0CFDA7FB2Cf/logo.png", + "symbol": "PLN", + "name": "Pollen", + "contractAddress": "0x7b2B702706D9b361dfE3f00bD138C0CFDA7FB2Cf", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xe0Ce60AF0850bF54072635e66E79Df17082A1109/logo.png", + "symbol": "ICE", + "name": "Popsicle Finance Token", + "contractAddress": "0xe0Ce60AF0850bF54072635e66E79Df17082A1109", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xb2a85C5ECea99187A977aC34303b80AcbDdFa208/logo.png", + "symbol": "ROCO", + "name": "ROCO", + "contractAddress": "0xb2a85C5ECea99187A977aC34303b80AcbDdFa208", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x7086e045b78E1e72F741F25231c08d238812CF8a/logo.png", + "symbol": "RACEX", + "name": "RaceX Token", + "contractAddress": "0x7086e045b78E1e72F741F25231c08d238812CF8a", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x97Cd1CFE2ed5712660bb6c14053C0EcB031Bff7d/logo.png", + "symbol": "RAI", + "name": "Rai Reflex Index", + "contractAddress": "0x97Cd1CFE2ed5712660bb6c14053C0EcB031Bff7d", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xC1d02E488a9Ce2481BFdcd797d5373Dd2E70a9C2/logo.png", + "symbol": "SHAKE", + "name": "SHAKE token by SpaceSwap v2", + "contractAddress": "0xC1d02E488a9Ce2481BFdcd797d5373Dd2E70a9C2", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x440aBbf18c54b2782A4917b80a1746d3A2c2Cce1/logo.png", + "symbol": "SHIBX", + "name": "SHIBAVAX", + "contractAddress": "0x440aBbf18c54b2782A4917b80a1746d3A2c2Cce1", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x6AFD5A1ea4b793CC1526d6Dc7e99A608b356eF7b/logo.png", + "symbol": "STORM", + "name": "STORM Token", + "contractAddress": "0x6AFD5A1ea4b793CC1526d6Dc7e99A608b356eF7b", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/ava-labs/bridge-tokens/main/avalanche-tokens/0xa5E59761eBD4436fa4d20E1A27cBa29FB2471Fc6/logo.png", + "symbol": "SHERPA", + "name": "Sherpa", + "contractAddress": "0xa5E59761eBD4436fa4d20E1A27cBa29FB2471Fc6", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x483416eB3aFA601B9C6385f63CeC0C82B6aBf1fb/logo.png", + "symbol": "SKILL", + "name": "Skill Token", + "contractAddress": "0x483416eB3aFA601B9C6385f63CeC0C82B6aBf1fb", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x2d7f2f30b66b3A754F9B7Ca53a82b073Dd2Cdca2/logo.png", + "symbol": "SLIME", + "name": "Slime", + "contractAddress": "0x2d7f2f30b66b3A754F9B7Ca53a82b073Dd2Cdca2", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x6D923f688C7FF287dc3A5943CAeefc994F97b290/logo.png", + "symbol": "SMRTR", + "name": "SmarterCoin", + "contractAddress": "0x6D923f688C7FF287dc3A5943CAeefc994F97b290", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/Snowball-Finance/Assets/main/Uphill%20snowball.png", + "symbol": "SNOB", + "name": "Snowball", + "contractAddress": "0xC38f41A296A4493Ff429F1238e030924A1542e50", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 9.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x7d1232B90D3F809A54eeaeeBC639C62dF8a8942f/logo.png", + "symbol": "SB", + "name": "Snowbank", + "contractAddress": "0x7d1232B90D3F809A54eeaeeBC639C62dF8a8942f", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xCE1bFFBD5374Dac86a2893119683F4911a2F7814/logo.png", + "symbol": "SPELL", + "name": "Spell Token", + "contractAddress": "0xCE1bFFBD5374Dac86a2893119683F4911a2F7814", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/SphereSwap/SphereSwapData/main/logo/logo.png", + "symbol": "SPHERE.e", + "name": "SphereSwap Token", + "contractAddress": "0x8A9B36393633aD77ceb8aebC7768815627B93557", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xbD83010eB60F12112908774998F65761cf9f6f9a/logo.png", + "symbol": "BOO", + "name": "Spookyswap Token", + "contractAddress": "0xbD83010eB60F12112908774998F65761cf9f6f9a", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 9.0, + "customIconUrl": "https://raw.githubusercontent.com/sporeproject/Spore-frontend/master/src/utils/SPORE.png", + "symbol": "SPORE", + "name": "Spore", + "contractAddress": "0x6e7f5C0b9f4432716bDd0a77a3601291b9D9e985", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x37d87e316CB4e35163881fDb6c6Bc0CdBa91dc0A/logo.png", + "symbol": "SET", + "name": "Stabilize Token", + "contractAddress": "0x37d87e316CB4e35163881fDb6c6Bc0CdBa91dc0A", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xF44Fb887334Fa17d2c5c0F970B5D320ab53eD557/logo.png", + "symbol": "START", + "name": "Starter", + "contractAddress": "0xF44Fb887334Fa17d2c5c0F970B5D320ab53eD557", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 12.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x2bC8834Bc217f15DF898fcab896e2b3E3673faf6/logo.png", + "symbol": "SXI", + "name": "SwapXI", + "contractAddress": "0x2bC8834Bc217f15DF898fcab896e2b3E3673faf6", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x1f1E7c893855525b303f99bDF5c3c05Be09ca251/logo.png", + "symbol": "SYN", + "name": "Synapse", + "contractAddress": "0x1f1E7c893855525b303f99bDF5c3c05Be09ca251", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 8.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xae9d2385Ff2E2951Dd4fA061e74c4d3deDD24347/logo.png", + "symbol": "TOK", + "name": "TOK", + "contractAddress": "0xae9d2385Ff2E2951Dd4fA061e74c4d3deDD24347", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x094bd7B2D99711A1486FB94d4395801C6d0fdDcC/logo.png", + "symbol": "TEDDY", + "name": "Teddy Cash Token", + "contractAddress": "0x094bd7B2D99711A1486FB94d4395801C6d0fdDcC", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x8F47416CaE600bccF9530E9F3aeaA06bdD1Caa79/logo.png", + "symbol": "THOR", + "name": "Thor v2", + "contractAddress": "0x8F47416CaE600bccF9530E9F3aeaA06bdD1Caa79", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 8.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x245C2591403e182e41d7A851eab53B01854844CE/logo.png", + "symbol": "MEAD", + "name": "Thors Mead", + "contractAddress": "0x245C2591403e182e41d7A851eab53B01854844CE", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 9.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xb54f16fB19478766A268F172C9480f8da1a7c9C3/logo.png", + "symbol": "TIME", + "name": "Time", + "contractAddress": "0xb54f16fB19478766A268F172C9480f8da1a7c9C3", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 9.0, + "customIconUrl": "https://raw.githubusercontent.com/sled-finance/tjoma-media/master/logo.png", + "symbol": "TJOMA", + "name": "Tjoma.Finance", + "contractAddress": "0x8d4A8522f2FB040d4755757C05d99976C0D29fCE", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xb84527D59b6Ecb96F433029ECc890D4492C5dCe1/logo.png", + "symbol": "TOMB", + "name": "Tomb Token", + "contractAddress": "0xb84527D59b6Ecb96F433029ECc890D4492C5dCe1", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xf693248F96Fe03422FEa95aC0aFbBBc4a8FdD172/logo.png", + "symbol": "TUS", + "name": "Treasure Under Sea", + "contractAddress": "0xf693248F96Fe03422FEa95aC0aFbBBc4a8FdD172", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 6.0, + "customIconUrl": "https://raw.githubusercontent.com/TrueReflect-Lead-Design/Logo/main/logo.png", + "symbol": "TRT", + "name": "True Reflect Token", + "contractAddress": "0x70B907f3F7231bE122100dac51C1995e6804E346", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xc7B5D72C836e718cDA8888eaf03707fAef675079/logo.png", + "symbol": "SWAP.e", + "name": "TrustSwap Token", + "contractAddress": "0xc7B5D72C836e718cDA8888eaf03707fAef675079", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x21c5402C3B7d40C89Cc472C9dF5dD7E51BbAb1b1/logo.png", + "symbol": "TUNDRA", + "name": "Tundra Token", + "contractAddress": "0x21c5402C3B7d40C89Cc472C9dF5dD7E51BbAb1b1", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x3709E8615E02C15B096f8a9B460ccb8cA8194e86/logo.png", + "symbol": "VEE", + "name": "Vee Finance", + "contractAddress": "0x3709E8615E02C15B096f8a9B460ccb8cA8194e86", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/VersoOfficial/pr/master/icon_blue.png", + "symbol": "VSO", + "name": "Verso", + "contractAddress": "0x846D50248BAf8b7ceAA9d9B53BFd12d7D7FBB25a", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xA384Bc7Cdc0A93e686da9E7B8C0807cD040F4E0b/logo.png", + "symbol": "WOW", + "name": "WOWSwap", + "contractAddress": "0xA384Bc7Cdc0A93e686da9E7B8C0807cD040F4E0b", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xB1466d4cf0DCfC0bCdDcf3500F473cdACb88b56D/logo.png", + "symbol": "WET", + "name": "Weble Ecosystem Token", + "contractAddress": "0xB1466d4cf0DCfC0bCdDcf3500F473cdACb88b56D", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xbA5dC7e77d150816b758E9826fCad2D74820e379/logo.png", + "symbol": "WOWX", + "name": "WowneroX", + "contractAddress": "0xbA5dC7e77d150816b758E9826fCad2D74820e379", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x2CF51e73C3516f3d86E9C0B4De0971Dbf0766Fd4/logo.png", + "symbol": "XIO", + "name": "XIO Network Token", + "contractAddress": "0x2CF51e73C3516f3d86E9C0B4De0971Dbf0766Fd4", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x01C2086faCFD7aA38f69A6Bd8C91BEF3BB5adFCa/logo.png", + "symbol": "YAY", + "name": "YAY Games", + "contractAddress": "0x01C2086faCFD7aA38f69A6Bd8C91BEF3BB5adFCa", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/YetiSwap/yetiswap.app/master/src/assets/image/YTSCoin.png", + "symbol": "YTS", + "name": "YetiSwap", + "contractAddress": "0x488F73cddDA1DE3664775fFd91623637383D6404", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x59414b3089ce2AF0010e7523Dea7E2b35d776ec7/logo.png", + "symbol": "YAK", + "name": "Yield Yak", + "contractAddress": "0x59414b3089ce2AF0010e7523Dea7E2b35d776ec7", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/zabufinance/Assets/main/Zabu300x300.png", + "symbol": "ZABU", + "name": "ZABU", + "contractAddress": "0xDd453dBD253fA4E5e745047d93667Ce9DA93bbCF", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xbe6D6323eA233fD1DBe1fF66c5252170c69fb6c7/logo.png", + "symbol": "ZUBAX", + "name": "ZUBAX", + "contractAddress": "0xbe6D6323eA233fD1DBe1fF66c5252170c69fb6c7", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "symbol": "ZERO", + "name": "Zero.Exchange Token", + "contractAddress": "0x008E26068B3EB40B443d3Ea88c1fF99B789c10F7", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xBD100d061E120b2c67A24453CF6368E63f1Be056/logo.png", + "symbol": "iDYP", + "name": "iDYP", + "contractAddress": "0xBD100d061E120b2c67A24453CF6368E63f1Be056", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 9.0, + "customIconUrl": "https://raw.githubusercontent.com/sled-finance/media/main/sled_token_fl.png", + "symbol": "SFI", + "name": "sled.finance", + "contractAddress": "0x1F1FE1eF06ab30a791d6357FdF0a7361B39b1537", + "blockchain": { + "key": "avalanche", + "testnet": false + } + }, + { + "decimalCount": 18.0, + "customIconUrl": "https://raw.githubusercontent.com/xDollar-Finance/logo/master/xdo-128x128.png", + "symbol": "XDO", + "name": "xDollar", + "contractAddress": "0x9eF758aC000a354479e538B8b2f01b917b8e89e7", + "blockchain": { + "key": "avalanche", + "testnet": false + } + } +] \ No newline at end of file diff --git a/app/src/main/assets/avalanche_tokens_testnet.json b/app/src/main/assets/avalanche_tokens_testnet.json new file mode 100644 index 0000000000..415f0582bb --- /dev/null +++ b/app/src/main/assets/avalanche_tokens_testnet.json @@ -0,0 +1,24 @@ +[ + { + "decimalCount": 6, + "customIconUrl": "https://i.imgur.com/LPZDXoT.png", + "symbol": "FUJISTABLE", + "name": "The Fuji stablecoin", + "contractAddress": "0x2058ec2791dD28b6f67DB836ddf87534F4Bbdf22", + "blockchain": { + "key": "avalanche", + "testnet": true + } + }, + { + "decimalCount": 9, + "customIconUrl": "https://i.imgur.com/jNTqyQA.png", + "symbol": "FUJIMOON", + "name": "To the Moon", + "contractAddress": "0x97132C109c6816525F7f338DCb7435E1412A7668", + "blockchain": { + "key": "avalanche", + "testnet": true + } + } +] \ No newline at end of file diff --git a/app/src/main/assets/binance_tokens_testnet.json b/app/src/main/assets/binance_tokens_testnet.json index 7d5590d293..5abd0fd139 100644 --- a/app/src/main/assets/binance_tokens_testnet.json +++ b/app/src/main/assets/binance_tokens_testnet.json @@ -6,7 +6,7 @@ "blockchain" : { "key" : "binance", "curve" : "secp256k1", - "testnet" : false + "testnet" : true }, "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/MATIC-84A/logo.png", "name" : "Hemster - 452" diff --git a/app/src/main/assets/solana_tokens_testnet.json b/app/src/main/assets/solana_tokens_testnet.json index 07f315f75f..1cdd983a74 100644 --- a/app/src/main/assets/solana_tokens_testnet.json +++ b/app/src/main/assets/solana_tokens_testnet.json @@ -1,14 +1,24 @@ [ { - "decimalCount" : 9, - "symbol" : "TCA", - "name" : "Tangem Coin A", - "contractAddress" : "22PTNbX31Zuztd6nD82fC8nQdT2hUfWv9XWXKuDkrFqR" + "decimalCount": 9, + "symbol": "TCA", + "name": "Tangem Coin A", + "contractAddress": "22PTNbX31Zuztd6nD82fC8nQdT2hUfWv9XWXKuDkrFqR", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": true + } }, { - "decimalCount" : 9, - "symbol" : "TCB", - "name" : "Tangem Coin B", - "contractAddress" : "HmSghNPg6KCk711YJA92aPejt8auyFkvmaED6jbHfUs4" + "decimalCount": 9, + "symbol": "TCB", + "name": "Tangem Coin B", + "contractAddress": "HmSghNPg6KCk711YJA92aPejt8auyFkvmaED6jbHfUs4", + "blockchain": { + "key": "solana", + "curve": "ed25519", + "testnet": true + } } ] \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt b/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt index fc1ae2bcb2..ad57bd6f51 100644 --- a/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt @@ -9,6 +9,7 @@ import com.squareup.moshi.Types import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token import com.tangem.common.card.FirmwareVersion +import com.tangem.common.json.MoshiJsonConverter import com.tangem.tap.common.extensions.readJsonFileToString import com.tangem.tap.network.createMoshi @@ -113,24 +114,28 @@ class CurrenciesRepository(val context: Application) { if (isTestNet) BSC_TESTNET_TOKENS_FILE_NAME else BSC_TOKENS_FILE_NAME val binanceTokensFileName = if (isTestNet) BINANCE_TESTNET_TOKENS_FILE_NAME else BINANCE_TOKENS_FILE_NAME + val avalancheTokensFileName = + if (isTestNet) AVALANCHE_TESTNET_TOKENS_FILE_NAME else AVALANCHE_TOKENS_FILE_NAME val ethereumTokensJson = context.assets.readJsonFileToString(ethereumTokensFileName) val bscTokensJson = context.assets.readJsonFileToString(bscTokensFileName) val binanceTokensJson = context.assets.readJsonFileToString(binanceTokensFileName) + val avalancheTokensJson = context.assets.readJsonFileToString(avalancheTokensFileName) return tokensAdapter.fromJson(ethereumTokensJson)!!.map { it.toToken() } + - tokensAdapter.fromJson(bscTokensJson)!!.map { it.toToken() } + - tokensAdapter.fromJson(binanceTokensJson)!!.mapNotNull { - // temporary exclude Binance BEP-8 tokens - if (it.type != null && it.type == BINANCE_TOKEN_TYPE_BEP8) null else it.toToken() - } + tokensAdapter.fromJson(bscTokensJson)!!.map { it.toToken() } + + tokensAdapter.fromJson(binanceTokensJson)!!.mapNotNull { + // temporary exclude Binance BEP-8 tokens + if (it.type != null && it.type == BINANCE_TOKEN_TYPE_BEP8) null else it.toToken() + } + + tokensAdapter.fromJson(avalancheTokensJson)!!.map { it.toToken() } } fun getBlockchains( - cardFirmware: FirmwareVersion?, + cardFirmware: FirmwareVersion, isTestNet: Boolean = false ): List { - return if (cardFirmware == null || cardFirmware < FirmwareVersion.MultiWalletAvailable) { + return if (cardFirmware < FirmwareVersion.MultiWalletAvailable) { Blockchain.secp256k1Blockchains(isTestNet) } else { Blockchain.secp256k1Blockchains(isTestNet) + Blockchain.ed25519OnlyBlockchains(isTestNet) @@ -144,6 +149,9 @@ class CurrenciesRepository(val context: Application) { private const val BSC_TESTNET_TOKENS_FILE_NAME = "bsc_tokens_testnet" private const val BINANCE_TOKENS_FILE_NAME = "binance_tokens" private const val BINANCE_TESTNET_TOKENS_FILE_NAME = "binance_tokens_testnet" + private const val AVALANCHE_TOKENS_FILE_NAME = "avalanche_tokens" + private const val AVALANCHE_TESTNET_TOKENS_FILE_NAME = "avalanche_tokens_testnet" + private const val FILE_NAME_PREFIX_TOKENS = "tokens" private const val FILE_NAME_PREFIX_BLOCKCHAINS = "blockchains" @@ -152,6 +160,18 @@ class CurrenciesRepository(val context: Application) { fun getFileNameForTokens(cardId: String): String = "${FILE_NAME_PREFIX_TOKENS}_$cardId" fun getFileNameForBlockchains(cardId: String): String = "${FILE_NAME_PREFIX_BLOCKCHAINS}_$cardId" + + fun injectDaoBlockchain(context: Context, tokenFileName: String, blockchain: Blockchain): String? { + val tokenJson = context.assets.readJsonFileToString(tokenFileName) + val converter = MoshiJsonConverter.default() + val rawTokensList = converter.fromJson>>(tokenJson) ?: return null + + rawTokensList.forEach { + it["blockchain"] = BlockchainDao.fromBlockchain(blockchain) + } + + return converter.toJson(rawTokensList) + } } } @@ -205,8 +225,7 @@ data class BlockchainDao( companion object { fun fromBlockchain(blockchain: Blockchain): BlockchainDao { val name = blockchain.name.removeSuffix("Testnet").lowercase() - val isTestnet = blockchain.name.endsWith("Testnet") - return BlockchainDao(name, isTestnet) + return BlockchainDao(name, blockchain.isTestnet()) } } } diff --git a/app/src/main/java/com/tangem/tap/features/tokens/redux/CurrencyListItem.kt b/app/src/main/java/com/tangem/tap/features/tokens/redux/CurrencyListItem.kt index 53acef169a..f9749a8b8a 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/redux/CurrencyListItem.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/redux/CurrencyListItem.kt @@ -23,22 +23,28 @@ sealed class CurrencyListItem { blockchains: List, tokens: List, ): List { - val blockchainsTitle = R.string.add_tokens_subtitle_blockchains - val ethereumTokensTitle = R.string.add_tokens_subtitle_ethereum_tokens - val bscTokensTitle = R.string.add_tokens_subtitle_bsc_tokens - val binanceTokensTitle = R.string.add_tokens_subtitle_binance_tokens + return createBlockchainList(blockchains) + + createTokensList(R.string.add_tokens_subtitle_ethereum_tokens, Blockchain.Ethereum, tokens) + + createTokensList(R.string.add_tokens_subtitle_bsc_tokens, Blockchain.BSC, tokens) + + createTokensList(R.string.add_tokens_subtitle_binance_tokens, Blockchain.Binance, tokens) + + createTokensList(R.string.add_tokens_subtitle_avalanche_tokens, Blockchain.Avalanche, tokens) + } - val ethereumTokens = tokens.filter { it.blockchain == Blockchain.Ethereum } - val bscTokens = tokens.filter { it.blockchain == Blockchain.BSC } - val binanceTokens = tokens.filter { it.blockchain == Blockchain.Binance } - return listOf(TitleListItem(blockchainsTitle)) + - blockchains.map { BlockchainListItem(it) } + - listOf(TitleListItem(ethereumTokensTitle, blockchain = Blockchain.Ethereum)) + - ethereumTokens.map { TokenListItem(it) } + - listOf(TitleListItem(bscTokensTitle, blockchain = Blockchain.BSC)) + - bscTokens.map { TokenListItem(it) } + - listOf(TitleListItem(binanceTokensTitle, blockchain = Blockchain.Binance)) + - binanceTokens.map { TokenListItem(it) } + private fun createBlockchainList(blockchains: List): List { + val blockchainsTitle = R.string.add_tokens_subtitle_blockchains + return listOf(TitleListItem(blockchainsTitle)) + blockchains.map { BlockchainListItem(it) } + } + + private fun createTokensList( + @StringRes titleResId: Int, + blockchain: Blockchain, + tokens: List + ): List { + val filteredTokens = tokens.filter { + it.blockchain == blockchain || it.blockchain == blockchain.getTestnetVersion() + } + val tokensListItem = filteredTokens.map { TokenListItem(it) } + return listOf(TitleListItem(titleResId, blockchain = blockchain)) + tokensListItem } } } diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml index 40634c9ea5..e77a33cb2b 100644 --- a/app/src/main/res/values/strings_untranslated.xml +++ b/app/src/main/res/values/strings_untranslated.xml @@ -101,6 +101,7 @@ Ethereum tokens Binance Smart Chain tokens Binance Chain tokens + Avalanche C-Chain tokens Copy Share + Add tokens From 53ed62b84d75e093fd652dbf2b82925acfce0168 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 2 Feb 2022 13:21:30 +0300 Subject: [PATCH 18/20] Updated on 2026-08-14 --- .../com/tangem/tap/common/extensions/Picasso.kt | 4 +++- .../com/tangem/tap/domain/extensions/Token.kt | 17 +++++++++++++++++ .../tap/domain/tokens/CurrenciesRepository.kt | 10 ++++++++-- 3 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/domain/extensions/Token.kt diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Picasso.kt b/app/src/main/java/com/tangem/tap/common/extensions/Picasso.kt index 60c8a1ec9d..f34b5e9ecf 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Picasso.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Picasso.kt @@ -1,6 +1,7 @@ package com.tangem.tap.common.extensions import android.graphics.PorterDuff +import android.net.Uri import android.widget.ImageView import android.widget.TextView import androidx.core.content.ContextCompat @@ -9,6 +10,7 @@ import com.squareup.picasso.Picasso import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.IconsUtil import com.tangem.blockchain.common.Token +import com.tangem.tap.domain.extensions.getCustomIconUrl import com.tangem.wallet.R fun Picasso.loadCurrenciesIcon( @@ -19,7 +21,7 @@ fun Picasso.loadCurrenciesIcon( ) { val url = if (token != null) { - IconsUtil.getTokenIconUri(blockchain, token) + token.getCustomIconUrl()?.let { Uri.parse(it) } ?: IconsUtil.getTokenIconUri(blockchain, token) } else { IconsUtil.getBlockchainIconUri(blockchain) } diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/Token.kt b/app/src/main/java/com/tangem/tap/domain/extensions/Token.kt new file mode 100644 index 0000000000..6963ed3682 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/extensions/Token.kt @@ -0,0 +1,17 @@ +package com.tangem.tap.domain.extensions + +import com.tangem.blockchain.common.Token + +/** +[REDACTED_AUTHOR] + */ + +private val tokenCustomIconUrls = mutableMapOf() + +fun Token.getCustomIconUrl(): String? { + return tokenCustomIconUrls[this.contractAddress] +} + +fun Token.setCustomIconUrl(url: String) { + tokenCustomIconUrls[this.contractAddress] = url +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt b/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt index ad57bd6f51..02d42e5869 100644 --- a/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt @@ -11,6 +11,8 @@ import com.tangem.blockchain.common.Token import com.tangem.common.card.FirmwareVersion import com.tangem.common.json.MoshiJsonConverter import com.tangem.tap.common.extensions.readJsonFileToString +import com.tangem.tap.domain.extensions.getCustomIconUrl +import com.tangem.tap.domain.extensions.setCustomIconUrl import com.tangem.tap.network.createMoshi class CurrenciesRepository(val context: Application) { @@ -183,6 +185,7 @@ data class TokenDao( val decimalCount: Int, @Json(name = "blockchain") val blockchainDao: BlockchainDao, + val customIconUrl: String? = null, val type: String? = null ) { fun toToken(): Token { @@ -192,7 +195,9 @@ data class TokenDao( contractAddress = contractAddress, decimals = decimalCount, blockchain = blockchainDao.toBlockchain() - ) + ).apply { + customIconUrl?.let { this.setCustomIconUrl(it) } + } } companion object { @@ -202,7 +207,8 @@ data class TokenDao( symbol = token.symbol, contractAddress = token.contractAddress, decimalCount = token.decimals, - blockchainDao = BlockchainDao.fromBlockchain(token.blockchain) + blockchainDao = BlockchainDao.fromBlockchain(token.blockchain), + customIconUrl = token.getCustomIconUrl() ) } } From 62ca26e98894c999bc8f698aa432f98386009b96 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 2 Feb 2022 13:28:39 +0300 Subject: [PATCH 19/20] Updated on 2026-08-14 --- app/src/main/res/layout/fragment_details_confirm.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/layout/fragment_details_confirm.xml b/app/src/main/res/layout/fragment_details_confirm.xml index 5d992196b0..2a6933b594 100644 --- a/app/src/main/res/layout/fragment_details_confirm.xml +++ b/app/src/main/res/layout/fragment_details_confirm.xml @@ -42,7 +42,7 @@ app:layout_constraintTop_toTopOf="parent" /> From 7dc92a9beab7017ba27652b52062dd3369d84fb1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 2 Feb 2022 14:24:01 +0300 Subject: [PATCH 20/20] Updated on 2026-08-14 --- .../features/tokens/redux/CurrencyListItem.kt | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/tokens/redux/CurrencyListItem.kt b/app/src/main/java/com/tangem/tap/features/tokens/redux/CurrencyListItem.kt index f9749a8b8a..032f9d82ef 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/redux/CurrencyListItem.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/redux/CurrencyListItem.kt @@ -51,7 +51,7 @@ sealed class CurrencyListItem { fun List.removeTokensForBlockchain(blockchain: Blockchain): List { return filterNot { - it is CurrencyListItem.TokenListItem && it.token.blockchain == blockchain + it is CurrencyListItem.TokenListItem && it.isTheSameOrTestnetBlockchain(blockchain) } } @@ -59,18 +59,31 @@ fun List.addTokensForBlockchain( blockchain: Blockchain, fullCurrenciesList: List, ): List { val tokensToAdd = fullCurrenciesList.filter { - it is CurrencyListItem.TokenListItem && it.token.blockchain == blockchain + it is CurrencyListItem.TokenListItem && it.isTheSameOrTestnetBlockchain(blockchain) } val indexToInsert = indexOfFirst { - it is CurrencyListItem.TitleListItem && it.blockchain == blockchain + it is CurrencyListItem.TitleListItem && it.isTheSameOrTestnetBlockchain(blockchain) } + 1 return this.toMutableList().apply { addAll(indexToInsert, tokensToAdd) } } fun List.toggleHeaderContentShownValue(blockchain: Blockchain) { map { - if (it is CurrencyListItem.TitleListItem && it.blockchain == blockchain) { + if (it is CurrencyListItem.TitleListItem && it.isTheSameOrTestnetBlockchain(blockchain)) { it.isContentShown = !it.isContentShown } } +} + +// if you do not check the testNet blockchains, it will not collapse +private fun CurrencyListItem.isTheSameOrTestnetBlockchain(blockchain: Blockchain): Boolean { + return when (this) { + is CurrencyListItem.TitleListItem -> this.blockchain?.isTheSameOrTestnetBlockchain(blockchain) ?: false + is CurrencyListItem.BlockchainListItem -> this.blockchain.isTheSameOrTestnetBlockchain(blockchain) + is CurrencyListItem.TokenListItem -> this.token.blockchain.isTheSameOrTestnetBlockchain(blockchain) + } +} + +private fun Blockchain.isTheSameOrTestnetBlockchain(blockchain: Blockchain): Boolean { + return this == blockchain || this == blockchain.getTestnetVersion() } \ No newline at end of file