From cc8e3809619416114a28aaaa2dc455629796c5b1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 24 Jun 2021 12:18:31 +0300 Subject: [PATCH 001/189] Updated on 2026-08-14 --- .../com/tangem/tap/common/DialogManager.kt | 7 +++++++ .../walletconnect/WalletConnectManager.kt | 21 ++++++++++++++----- .../walletconnect/WalletConnectAction.kt | 3 ++- .../walletconnect/WalletConnectMiddleware.kt | 4 ++++ .../walletconnect/WalletConnectReducer.kt | 1 + .../redux/walletconnect/WalletConnectState.kt | 8 +------ .../main/res/values/strings_untranslated.xml | 1 + 7 files changed, 32 insertions(+), 13 deletions(-) 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 96992a5d7a..f58e132937 100644 --- a/app/src/main/java/com/tangem/tap/common/DialogManager.kt +++ b/app/src/main/java/com/tangem/tap/common/DialogManager.kt @@ -51,6 +51,13 @@ class DialogManager : StoreSubscriber { context = context ) } + is WalletConnectDialog.SessionTimeout -> { + dialog = SimpleAlertDialog.create( + titleRes = R.string.wallet_connect, + messageRes = R.string.wallet_connect_error_timeout, + context = context + ) + } is WalletConnectDialog.ApproveWcSession -> dialog = ApproveWcSessionDialog.create(state.dialog.session, context) is WalletConnectDialog.ClipboardOrScanQr -> 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 44eece7399..29f1eec9fe 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 @@ -15,6 +15,7 @@ import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage import com.trustwallet.walletconnect.models.ethereum.WCEthereumTransaction import com.trustwallet.walletconnect.models.session.WCSession import com.trustwallet.walletconnect.models.session.WCSessionUpdate +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import okhttp3.Interceptor import okhttp3.OkHttpClient @@ -29,9 +30,9 @@ class WalletConnectManager { private val okHttpClient: OkHttpClient by lazy { OkHttpClient.Builder() - .connectTimeout(30, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) - .writeTimeout(30, TimeUnit.SECONDS) + .connectTimeout(20, TimeUnit.SECONDS) + .readTimeout(20, TimeUnit.SECONDS) + .writeTimeout(20, TimeUnit.SECONDS) .addInterceptor(interceptor) .addInterceptor(RetryInterceptor()) .build() @@ -59,6 +60,18 @@ class WalletConnectManager { client = client, wallet = wallet ) + setupConnectionTimeoutCheck(session) + } + + private fun setupConnectionTimeoutCheck(session: WCSession) { + scope.launch { + delay(20_000) + val data = sessions[session] + if (data != null && data.peerMeta == null) { + disconnect(session) + store.dispatchOnMain(WalletConnectAction.OpeningSessionTimeout(session)) + } + } } fun restoreSessions() { @@ -184,7 +197,6 @@ class WalletConnectManager { acceptRequest(data.session.session, data.id, hash) sessions[data.session.session] = activeData.copy(transactionData = null) } - } fun handlePersonalSignRequest( @@ -252,7 +264,6 @@ class WalletConnectManager { type = WcTransactionType.EthSendTransaction )) } - } client.onEthSignTransaction = { id: Long, transaction: WCEthereumTransaction -> Timber.d("onEthSignTransaction: $transaction") 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 728a8d0446..16a3f638a4 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 @@ -31,6 +31,8 @@ sealed class WalletConnectAction : Action { object RefuseOpeningSession : WalletConnectAction() + data class OpeningSessionTimeout(val session: WCSession) : WalletConnectAction() + data class AcceptOpeningSession(val session: WalletConnectSession) : WalletConnectAction() data class ApproveSession( @@ -41,7 +43,6 @@ sealed class WalletConnectAction : Action { data class FailureEstablishingSession(val session: WCSession?) : WalletConnectAction() - data class SetSessionsRestored(val sessions: List) : WalletConnectAction() 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 89bec71f1f..a47a1379b2 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 @@ -60,6 +60,10 @@ class WalletConnectMiddleware { handleScanCard(action.wcUri) } + is WalletConnectAction.OpeningSessionTimeout -> { + store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.SessionTimeout)) + } + is WalletConnectAction.FailureEstablishingSession -> { if (action.session != null) { walletConnectManager.disconnect(action.session) 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 df3eec9834..c8b6f3b1b7 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 @@ -28,6 +28,7 @@ class WalletConnectReducer { is WalletConnectAction.UnsupportedCard -> state.copy(loading = false) is WalletConnectAction.RefuseOpeningSession -> state.copy(loading = false) + is WalletConnectAction.OpeningSessionTimeout -> state.copy(loading = false) is WalletConnectAction.FailureEstablishingSession -> state.copy(loading = false) else -> state 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 f5b7ce0c5f..757584cc46 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,9 +1,7 @@ package com.tangem.tap.features.details.redux.walletconnect -import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.common.WalletManager -import com.tangem.common.extensions.hexToBytes import com.tangem.tap.common.redux.global.StateDialog import com.tangem.tap.features.details.ui.walletconnect.dialogs.PersonalSignDialogData import com.tangem.tap.features.details.ui.walletconnect.dialogs.TransactionRequestDialogData @@ -30,11 +28,6 @@ data class WalletForSession( ) { val chainId get() = if (isTestNet) 4 else 1 - - fun getAddress(): String { - val blockchain = if (isTestNet) Blockchain.EthereumTestnet else Blockchain.Ethereum - return blockchain.makeAddresses(walletPublicKey.hexToBytes()).first().value - } } @@ -42,6 +35,7 @@ sealed class WalletConnectDialog : StateDialog { data class ClipboardOrScanQr(val clipboardUri: String) : WalletConnectDialog() object UnsupportedCard : WalletConnectDialog() object OpeningSessionRejected : WalletConnectDialog() + object SessionTimeout : WalletConnectDialog() data class ApproveWcSession(val session: WalletConnectSession) : WalletConnectDialog() data class RequestTransaction(val dialogData: TransactionRequestDialogData) : WalletConnectDialog() diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml index d6c4426536..5f9c771bb9 100644 --- a/app/src/main/res/values/strings_untranslated.xml +++ b/app/src/main/res/values/strings_untranslated.xml @@ -156,4 +156,5 @@ this wallet. This card can’t be used to establish WalletConnect session The operation couldn\'t be completed The operation couldn\'t be completed. \n\nYou have already established a WalletConnect session with this parameters. + Failed to establish WalletConnect session: timeout error. Please, try again later. From b18e293e4f6fc83e7e5069b350a46c10c4b334cd Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 24 Jun 2021 14:19:23 +0300 Subject: [PATCH 002/189] Updated on 2026-08-14 --- .../tap/common/analytics/AnalyticsHandler.kt | 10 ++- .../analytics/FirebaseAnalyticsHandler.kt | 70 ++++++++++++++--- .../tap/common/analytics/TangemSdkError.kt | 77 +++++++++++++++++++ .../com/tangem/tap/common/extensions/Map.kt | 4 + .../com/tangem/tap/domain/TangemSdkManager.kt | 9 ++- .../tap/domain/twins/TwinCardsManager.kt | 35 ++++++++- .../walletconnect/WalletConnectSdkHelper.kt | 20 +++++ .../details/redux/DetailsMiddleware.kt | 25 +++++- .../ui/walletconnect/dialogs/WcDialog.kt | 56 ++++++++++++++ .../send/redux/middlewares/SendMiddleware.kt | 17 +++- .../redux/middlewares/WalletMiddleware.kt | 10 +++ 11 files changed, 316 insertions(+), 17 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/common/analytics/TangemSdkError.kt create mode 100644 app/src/main/java/com/tangem/tap/common/extensions/Map.kt create mode 100644 app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/WcDialog.kt diff --git a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsHandler.kt index 1dc3586808..30bb8d545d 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsHandler.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsHandler.kt @@ -1,8 +1,16 @@ package com.tangem.tap.common.analytics +import com.tangem.TangemSdkError +import com.tangem.blockchain.common.Blockchain import com.tangem.commands.common.card.Card interface AnalyticsHandler { - fun triggerEvent(event: AnalyticsEvent, card: Card? = null) + fun triggerEvent(event: AnalyticsEvent, card: Card? = null, blockchain: Blockchain? = null) + fun logCardSdkError( + error: TangemSdkError, + actionToLog: FirebaseAnalyticsHandler.ActionToLog, + parameters: Map? = null, + card: Card? = null + ) } \ 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 d1d3ce7a38..5a91215e83 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 @@ -3,34 +3,84 @@ package com.tangem.tap.common.analytics import android.os.Bundle import androidx.core.os.bundleOf import com.google.firebase.analytics.ktx.analytics +import com.google.firebase.crashlytics.FirebaseCrashlytics import com.google.firebase.ktx.Firebase +import com.tangem.TangemSdkError +import com.tangem.blockchain.common.Blockchain import com.tangem.commands.common.card.Card +import com.tangem.tap.common.extensions.filterNotNull object FirebaseAnalyticsHandler : AnalyticsHandler { - override fun triggerEvent(event: AnalyticsEvent, card: Card?) { - Firebase.analytics.logEvent(event.event, setCardData(card)) + override fun triggerEvent(event: AnalyticsEvent, card: Card?, blockchain: Blockchain?) { + Firebase.analytics.logEvent(event.event, setData(card, blockchain)) } fun logException(name: String, throwable: Throwable) { Firebase.analytics.logEvent(name, bundleOf( - "message" to (throwable.message ?: "none"), - "cause_message" to (throwable.cause?.message ?: "none"), - "stack_trace" to throwable.stackTraceToString() + "message" to (throwable.message ?: "none"), + "cause_message" to (throwable.cause?.message ?: "none"), + "stack_trace" to throwable.stackTraceToString() )) } - private fun setCardData(card: Card?): Bundle { + override fun logCardSdkError( + error: TangemSdkError, + actionToLog: ActionToLog, + parameters: Map?, + card: Card?, + ) { + if (error is TangemSdkError.UserCancelled) return + + val params = parameters?.toMutableMap() ?: mutableMapOf() + if (card != null) params + getParamsFromCard(card) + params[AnalyticsParam.ACTION] = actionToLog.key + params[AnalyticsParam.ERROR_CODE] = error.code.toString() + params[AnalyticsParam.ERROR_DESCRIPTION] = error.javaClass.simpleName + params[AnalyticsParam.ERROR_KEY] = "TangemSdkError" + + params.forEach { (key, value) -> + FirebaseCrashlytics.getInstance().setCustomKey(key.param, value) + } + val cardError = TangemSdk.map(error) + FirebaseCrashlytics.getInstance().recordException(cardError) + } + + private fun getParamsFromCard(card: Card): Map { + return mapOf( + AnalyticsParam.FIRMWARE to card.firmwareVersion.version, + AnalyticsParam.BATCH_ID to card.cardData?.batchId + ).filterNotNull() + } + + private fun setData(card: Card?, blockchain: Blockchain?): Bundle { if (card == null) return bundleOf() return bundleOf( - AnalyticsParam.BLOCKCHAIN.param to card.cardData?.blockchainName, - AnalyticsParam.BATCH_ID.param to card.cardData?.batchId, - AnalyticsParam.FIRMWARE.param to card.firmwareVersion.version + AnalyticsParam.BLOCKCHAIN.param to (blockchain?.currency ?: card.cardData?.blockchainName), + AnalyticsParam.BATCH_ID.param to card.cardData?.batchId, + AnalyticsParam.FIRMWARE.param to card.firmwareVersion.version ) } - private enum class AnalyticsParam(val param: String) { + 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"), + } + + 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"), } } \ No newline at end of file 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 new file mode 100644 index 0000000000..1c574708e1 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/analytics/TangemSdkError.kt @@ -0,0 +1,77 @@ +package com.tangem.tap.common.analytics + +import com.tangem.TangemSdkError + +object TangemSdk { + + // This mapping is performed to group errors in FirebaseCrashlytics. + // At the moment, the errors in Crashlytics can only be grouped by their place of creation (class and line). + fun map(error: TangemSdkError): TangemSdkError { + return when (error) { + is TangemSdkError.TagLost -> TangemSdkError.TagLost() + is TangemSdkError.ExtendedLengthNotSupported -> TangemSdkError.ExtendedLengthNotSupported() + is TangemSdkError.SerializeCommandError -> TangemSdkError.SerializeCommandError() + is TangemSdkError.DeserializeApduFailed -> TangemSdkError.DeserializeApduFailed() + is TangemSdkError.EncodingFailedTypeMismatch -> TangemSdkError.EncodingFailedTypeMismatch( + error.customMessage) + is TangemSdkError.EncodingFailed -> TangemSdkError.EncodingFailed(error.customMessage) + is TangemSdkError.DecodingFailedMissingTag -> TangemSdkError.DecodingFailedMissingTag( + error.customMessage) + is TangemSdkError.DecodingFailedTypeMismatch -> TangemSdkError.DecodingFailedTypeMismatch( + error.customMessage) + is TangemSdkError.DecodingFailed -> TangemSdkError.DecodingFailed(error.customMessage) + is TangemSdkError.InvalidResponse -> TangemSdkError.InvalidResponse() + is TangemSdkError.UnknownStatus -> TangemSdkError.UnknownStatus(error.statusWord) + is TangemSdkError.ErrorProcessingCommand -> TangemSdkError.ErrorProcessingCommand() + is TangemSdkError.InvalidState -> TangemSdkError.InvalidState() + is TangemSdkError.InsNotSupported -> TangemSdkError.InsNotSupported() + is TangemSdkError.InvalidParams -> TangemSdkError.InvalidParams() + is TangemSdkError.NeedEncryption -> TangemSdkError.NeedEncryption() + is TangemSdkError.FileNotFound -> TangemSdkError.FileNotFound() + is TangemSdkError.WalletNotFound -> TangemSdkError.WalletNotFound() + is TangemSdkError.AlreadyPersonalized -> TangemSdkError.AlreadyPersonalized() + is TangemSdkError.CannotBeDepersonalized -> TangemSdkError.CannotBeDepersonalized() + is TangemSdkError.Pin1Required -> TangemSdkError.Pin1Required() + is TangemSdkError.CardReadWrongWallet -> TangemSdkError.CardReadWrongWallet() + is TangemSdkError.CardWithMaxZeroWallets -> TangemSdkError.CardWithMaxZeroWallets() + is TangemSdkError.AlreadyCreated -> TangemSdkError.AlreadyCreated() + is TangemSdkError.WalletIndexExceedsMaxValue -> TangemSdkError.WalletIndexExceedsMaxValue() + is TangemSdkError.MaxNumberOfWalletsCreated -> TangemSdkError.MaxNumberOfWalletsCreated() + is TangemSdkError.WalletIndexNotCorrect -> TangemSdkError.WalletIndexNotCorrect() + is TangemSdkError.PurgeWalletProhibited -> TangemSdkError.PurgeWalletProhibited() + is TangemSdkError.Pin1CannotBeChanged -> TangemSdkError.Pin1CannotBeChanged() + is TangemSdkError.Pin2CannotBeChanged -> TangemSdkError.Pin2CannotBeChanged() + is TangemSdkError.Pin1CannotBeDefault -> TangemSdkError.Pin1CannotBeDefault() + is TangemSdkError.NoRemainingSignatures -> TangemSdkError.NoRemainingSignatures() + is TangemSdkError.EmptyHashes -> TangemSdkError.EmptyHashes() + is TangemSdkError.HashSizeMustBeEqual -> TangemSdkError.HashSizeMustBeEqual() + is TangemSdkError.WalletIsNotCreated -> TangemSdkError.WalletIsNotCreated() + is TangemSdkError.SignHashesNotAvailable -> TangemSdkError.SignHashesNotAvailable() + is TangemSdkError.TooManyHashesInOneTransaction -> TangemSdkError.TooManyHashesInOneTransaction() + is TangemSdkError.ExtendedDataSizeTooLarge -> TangemSdkError.ExtendedDataSizeTooLarge() + is TangemSdkError.NotPersonalized -> TangemSdkError.NotPersonalized() + is TangemSdkError.NotActivated -> TangemSdkError.NotActivated() + is TangemSdkError.WalletIsPurged -> TangemSdkError.WalletIsPurged() + is TangemSdkError.Pin2OrCvcRequired -> TangemSdkError.Pin2OrCvcRequired() + is TangemSdkError.VerificationFailed -> TangemSdkError.VerificationFailed() + is TangemSdkError.DataSizeTooLarge -> TangemSdkError.DataSizeTooLarge() + is TangemSdkError.MissingCounter -> TangemSdkError.MissingCounter() + is TangemSdkError.OverwritingDataIsProhibited -> TangemSdkError.OverwritingDataIsProhibited() + is TangemSdkError.DataCannotBeWritten -> TangemSdkError.DataCannotBeWritten() + is TangemSdkError.MissingIssuerPubicKey -> TangemSdkError.MissingIssuerPubicKey() + is TangemSdkError.CardVerificationFailed -> TangemSdkError.CardVerificationFailed() + is TangemSdkError.WrongPin1 -> TangemSdkError.WrongPin1() + is TangemSdkError.WrongPin2 -> TangemSdkError.WrongPin2() + is TangemSdkError.UnknownError -> TangemSdkError.UnknownError() + is TangemSdkError.UserCancelled -> TangemSdkError.UserCancelled() + is TangemSdkError.Busy -> TangemSdkError.Busy() + is TangemSdkError.MissingPreflightRead -> TangemSdkError.MissingPreflightRead() + is TangemSdkError.WrongCardNumber -> TangemSdkError.WrongCardNumber() + is TangemSdkError.WrongCardType -> TangemSdkError.WrongCardType() + is TangemSdkError.CardError -> TangemSdkError.CardError() + is TangemSdkError.FirmwareNotSupported -> TangemSdkError.FirmwareNotSupported() + is TangemSdkError.WalletError -> TangemSdkError.WalletError() + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Map.kt b/app/src/main/java/com/tangem/tap/common/extensions/Map.kt new file mode 100644 index 0000000000..e970306219 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/extensions/Map.kt @@ -0,0 +1,4 @@ +package com.tangem.tap.common.extensions + +fun Map.filterNotNull(): Map = + filter { it.key != null && it.value != null } as Map \ No newline at end of file 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 af401f69bc..2a7810f725 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -16,6 +16,7 @@ import com.tangem.common.extensions.calculateSha256 import com.tangem.tangem_sdk_new.extensions.init 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.domain.extensions.getDefaultWalletIndex import com.tangem.tap.domain.tasks.CreateWalletAndRescanTask import com.tangem.tap.domain.tasks.ScanNoteResponse @@ -37,10 +38,16 @@ class TangemSdkManager(val activity: ComponentActivity) { analyticsHandler: AnalyticsHandler, messageRes: Int? = null ): CompletionResult { analyticsHandler.triggerEvent(AnalyticsEvent.READY_TO_SCAN, null) - return runTaskAsyncReturnOnMain(ScanNoteTask(), + val result = runTaskAsyncReturnOnMain(ScanNoteTask(), initialMessage = Message( activity.getString(messageRes ?: R.string.initial_message_scan_header) )) + if (result is CompletionResult.Failure) { + (result.error as? TangemSdkError)?.let { error -> + analyticsHandler.logCardSdkError(error, FirebaseAnalyticsHandler.ActionToLog.Scan) + } + } + return result } suspend fun createWallet(cardId: String?): CompletionResult { 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 43d313f8a6..af088dc421 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 @@ -2,12 +2,14 @@ package com.tangem.tap.domain.twins import com.tangem.KeyPair import com.tangem.Message +import com.tangem.TangemSdkError import com.tangem.blockchain.extensions.Result import com.tangem.blockchain.extensions.SimpleResult import com.tangem.common.CompletionResult import com.tangem.common.extensions.hexToBytes import com.tangem.common.extensions.toHexString import com.tangem.crypto.CryptoUtils +import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler import com.tangem.tap.domain.tasks.ScanNoteResponse import com.tangem.tap.tangemSdkManager @@ -28,7 +30,16 @@ class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse) { currentCardPublicKey = response.data.walletPublicKey.toHexString() return SimpleResult.Success } - is CompletionResult.Failure -> return SimpleResult.failure(response.error) + is CompletionResult.Failure -> { + (response.error as? TangemSdkError)?.let { error -> + FirebaseAnalyticsHandler.logCardSdkError( + error, + FirebaseAnalyticsHandler.ActionToLog.CreateWallet, + card = scanNoteResponse.card + ) + } + return SimpleResult.failure(response.error) + } } } @@ -48,7 +59,16 @@ class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse) { secondCardPublicKey = response.data.walletPublicKey.toHexString() return SimpleResult.Success } - is CompletionResult.Failure -> return SimpleResult.failure(response.error) + is CompletionResult.Failure -> { + (response.error as? TangemSdkError)?.let { error -> + FirebaseAnalyticsHandler.logCardSdkError( + error, + FirebaseAnalyticsHandler.ActionToLog.CreateWallet, + card = scanNoteResponse.card + ) + } + return SimpleResult.failure(response.error) + } } } @@ -60,7 +80,16 @@ class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse) { ) return when (response) { is CompletionResult.Success -> Result.Success(response.data) - is CompletionResult.Failure -> Result.failure(response.error) + is CompletionResult.Failure -> { + (response.error as? TangemSdkError)?.let { error -> + FirebaseAnalyticsHandler.logCardSdkError( + error, + FirebaseAnalyticsHandler.ActionToLog.WriteIssuerData, + card = scanNoteResponse.card + ) + } + Result.failure(response.error) + } } } 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 a78b81d7ce..7cff86a035 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 @@ -1,6 +1,7 @@ package com.tangem.tap.domain.walletconnect import com.tangem.Message +import com.tangem.TangemSdkError import com.tangem.blockchain.blockchains.ethereum.EthereumGasLoader import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionExtras import com.tangem.blockchain.blockchains.ethereum.EthereumUtils @@ -14,6 +15,7 @@ import com.tangem.commands.SignCommand import com.tangem.commands.wallet.WalletIndex import com.tangem.common.CompletionResult import com.tangem.common.extensions.hexToBytes +import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler 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 @@ -128,6 +130,12 @@ class WalletConnectSdkHelper { HEX_PREFIX + data.walletManager.wallet.recentTransactions.last().hash } is SimpleResult.Failure -> { + (result.error as? TangemSdkError)?.let { error -> + FirebaseAnalyticsHandler.logCardSdkError( + error, + FirebaseAnalyticsHandler.ActionToLog.WalletConnectTransaction, + ) + } Timber.e(result.error) null } @@ -152,6 +160,12 @@ class WalletConnectSdkHelper { HEX_PREFIX + result.data } is CompletionResult.Failure -> { + (result.error as? TangemSdkError)?.let { error -> + FirebaseAnalyticsHandler.logCardSdkError( + error, + FirebaseAnalyticsHandler.ActionToLog.WalletConnectSign, + ) + } Timber.e(result.error.customMessage) null } @@ -208,6 +222,12 @@ class WalletConnectSdkHelper { ) } is CompletionResult.Failure -> { + (result.error as? TangemSdkError)?.let { error -> + FirebaseAnalyticsHandler.logCardSdkError( + error, + FirebaseAnalyticsHandler.ActionToLog.WalletConnectSign, + ) + } Timber.e(result.error.customMessage) null } 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 b66aa958e9..90f75c3efe 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 @@ -1,7 +1,9 @@ package com.tangem.tap.features.details.redux +import com.tangem.TangemSdkError import com.tangem.commands.common.network.Result import com.tangem.common.CompletionResult +import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.AppScreen @@ -90,6 +92,15 @@ class DetailsMiddleware { is CompletionResult.Success -> { store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) } + is CompletionResult.Failure -> { + (result.error as? TangemSdkError)?.let { error -> + FirebaseAnalyticsHandler.logCardSdkError( + error, + FirebaseAnalyticsHandler.ActionToLog.PurgeWallet, + card = store.state.detailsState.card + ) + } + } } } } @@ -148,8 +159,20 @@ class DetailsMiddleware { } store.dispatch(DetailsAction.ManageSecurity.SaveChanges.Success) } - is CompletionResult.Failure, null -> + is CompletionResult.Failure -> { + (result.error as? TangemSdkError)?.let { error -> + FirebaseAnalyticsHandler.logCardSdkError( + error = error, + actionToLog = FirebaseAnalyticsHandler.ActionToLog.ChangeSecOptions, + parameters = mapOf( + FirebaseAnalyticsHandler.AnalyticsParam.NEW_SECURITY_OPTION to + (selectedOption?.name ?: "") + ), + card = store.state.detailsState.card + ) + } store.dispatch(DetailsAction.ManageSecurity.SaveChanges.Failure) + } } } } diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/WcDialog.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/WcDialog.kt new file mode 100644 index 0000000000..4bbb63e270 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/WcDialog.kt @@ -0,0 +1,56 @@ +package com.tangem.tap.features.details.ui.walletconnect.dialogs + +import android.content.Context +import androidx.appcompat.app.AlertDialog + +class WcDialog { + companion object { + fun create(dialogData: DialogData, context: Context): AlertDialog { + return AlertDialog.Builder(context).apply { + setTitle(dialogData.title) + setMessage(dialogData.message) + setPositiveButton(dialogData.positiveButton.title) { _, _ -> + dialogData.positiveButton.action() + } + setNegativeButton(dialogData.negativeButton.title) { _, _ -> + dialogData.negativeButton.action() + } + setOnDismissListener { dialogData.onDismissAction() } + }.create() + } + } +} + +data class DialogData( + val title: String, + val message: String, + val positiveButton: ButtonData, + val negativeButton: ButtonData, + val onDismissAction: () -> Unit, +) + +data class ButtonData( + val title: String, + val action: () -> Unit, +) + +//class WcDialogMessageBuilder(val messageRes: Int, val data: DialogMessageData? = null) { +// fun build(context: Context): String { +// when (data) { +// null -> context.getString(messageRes) +// } +// } +//} + +interface DialogMessageData + +data class WcTransactionDialogMessageData( + val cardId: String, + val dAppName: String, + val dAppUrl: String, + val amount: String, + val gasAmount: String, + val totalAmount: String, + val balance: String, + val isEnoughFundsToSend: Boolean, +) \ No newline at end of file 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 7dcececcda..9ca7c9ce1c 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 @@ -1,6 +1,7 @@ package com.tangem.tap.features.send.redux.middlewares import com.google.firebase.crashlytics.FirebaseCrashlytics +import com.tangem.TangemSdkError import com.tangem.blockchain.blockchains.stellar.StellarTransactionExtras import com.tangem.blockchain.blockchains.xrp.XrpTransactionBuilder import com.tangem.blockchain.common.* @@ -127,7 +128,11 @@ private fun sendTransaction( when (result) { is SimpleResult.Success -> { tangemSdk.config.linkedTerminal = isLinkedTerminal - FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.TRANSACTION_IS_SENT, card) + FirebaseAnalyticsHandler.triggerEvent( + event = AnalyticsEvent.TRANSACTION_IS_SENT, + card = card, + blockchain = walletManager.wallet.blockchain + ) dispatch(SendAction.SendSuccess) dispatch(NavigationAction.PopBackTo()) scope.launch(Dispatchers.IO) { @@ -178,6 +183,16 @@ private fun sendTransaction( dispatch(SendAction.SendError(TapError.XmlError.AssetAccountNotCreated)) } else -> { + (result.error as? TangemSdkError)?.let { error -> + FirebaseAnalyticsHandler.logCardSdkError( + error, + FirebaseAnalyticsHandler.ActionToLog.SendTransaction, + mapOf( + FirebaseAnalyticsHandler.AnalyticsParam.BLOCKCHAIN + to walletManager.wallet.blockchain.currency), + card = card, + ) + } Timber.e(throwable) FirebaseCrashlytics.getInstance().recordException(throwable) dispatch(SendAction.SendError(TapError.CustomError(message))) 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 183d55ddda..53ce1c2b2a 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 @@ -3,6 +3,7 @@ package com.tangem.tap.features.wallet.redux.middlewares import android.content.Intent import android.net.Uri import androidx.core.content.ContextCompat +import com.tangem.TangemSdkError import com.tangem.blockchain.common.* import com.tangem.common.CompletionResult import com.tangem.common.extensions.isZero @@ -111,6 +112,15 @@ class WalletMiddleware { globalState.tapWalletManager.onCardScanned(scanNoteResponse) } } + is CompletionResult.Failure -> { + (result.error as? TangemSdkError)?.let { error -> + FirebaseAnalyticsHandler.logCardSdkError( + error, + FirebaseAnalyticsHandler.ActionToLog.CreateWallet, + card = store.state.detailsState.card + ) + } + } } } } From 3df2441d4f2f88dc445c46a8ce9bb1bddc68705e Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 24 Jun 2021 14:32:27 +0300 Subject: [PATCH 003/189] Updated on 2026-08-14 --- app/src/main/assets/erc20_tokens.json | 5682 ++++++++++++++--- .../main/res/layout/item_popular_token.xml | 12 +- 2 files changed, 4768 insertions(+), 926 deletions(-) diff --git a/app/src/main/assets/erc20_tokens.json b/app/src/main/assets/erc20_tokens.json index 22fe8bd802..829096ad47 100644 --- a/app/src/main/assets/erc20_tokens.json +++ b/app/src/main/assets/erc20_tokens.json @@ -1,927 +1,4761 @@ [ - { - "decimalCount" : 18, - "symbol" : "ZRX", - "name" : "0x", - "contractAddress" : "0xE41d2489571d322189246DaFA5ebDe1F4699F498" - }, - { - "decimalCount" : 18, - "symbol" : "AAVE", - "name" : "Aave", - "contractAddress" : "0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9" - }, - { - "decimalCount" : 18, - "symbol" : "LEND", - "name" : "Aave (OLD)", - "contractAddress" : "0x80fB784B7eD66730e8b1DBd9820aFD29931aab03" - }, - { - "decimalCount" : 18, - "symbol" : "ADX", - "name" : "AdEx", - "contractAddress" : "0xADE00C28244d5CE17D72E40330B1c318cD12B7c3" - }, - { - "decimalCount" : 9, - "symbol" : "ADT", - "name" : "adToken", - "contractAddress" : "0xD0D6D6C5Fe4a677D343cC433536BB717bAe167dD" - }, - { - "decimalCount" : 18, - "symbol" : "ELF", - "name" : "Aelf", - "contractAddress" : "0xbf2179859fc6d5bee9bf9158632dc51678a4100e" - }, - { - "decimalCount" : 8, - "symbol" : "ARN", - "name" : "Aeron", - "contractAddress" : "0xBA5F11b16B155792Cf3B2E6880E8706859A8AEB6" - }, - { - "decimalCount" : 18, - "symbol" : "AE", - "name" : "Aeternity", - "contractAddress" : "0x5ca9a71b1d01849c0a95490cc00559717fcf0d1d" - }, - { - "decimalCount" : 8, - "symbol" : "AION", - "name" : "Aion", - "contractAddress" : "0x4CEdA7906a5Ed2179785Cd3A40A69ee8bc99C466" - }, - { - "decimalCount" : 4, - "symbol" : "AST", - "name" : "Airswap", - "contractAddress" : "0x27054b13b1b798b345b591a4d22e6562d47ea75a" - }, - { - "decimalCount" : 18, - "symbol" : "AMB", - "name" : "Amber", - "contractAddress" : "0x4dc3643dbc642b72c158e7f3d2ff232df61cb6ce" - }, - { - "decimalCount" : 18, - "symbol" : "AMLT", - "name" : "AMLT by Coinfirm", - "contractAddress" : "0xca0e7269600d353f70b14ad118a49575455c0f2f" - }, - { - "decimalCount" : 18, - "symbol" : "APPC", - "name" : "AppCoins", - "contractAddress" : "0x1a7a8bd9106f2b8d977e08582dc7d24c723ab0db" - }, - { - "decimalCount" : 18, - "symbol" : "ANT", - "name" : "Aragon", - "contractAddress" : "0xa117000000f279d81a1d3cc75430faa017fa5a2e" - }, - { - "decimalCount" : 18, - "symbol" : "ABT", - "name" : "ArcBlock", - "contractAddress" : "0xb98d4c97425d9908e66e53a6fdf673acca0be986" - }, - { - "decimalCount" : 18, - "symbol" : "ATMI", - "name" : "Atonomi", - "contractAddress" : "0x97aeb5066e1a590e868b511457beb6fe99d329f5" - }, - { - "decimalCount" : 18, - "symbol" : "REP", - "name" : "Augur", - "contractAddress" : "0x1985365e9f78359a9B6AD760e32412f4a445E862" - }, - { - "decimalCount" : 18, - "symbol" : "AVM", - "name" : "AVM Ecosystem", - "contractAddress" : "0x74004a7227615fb52b82d17ffabfa376907d8a4d" - }, - { - "decimalCount" : 18, - "symbol" : "BNT", - "name" : "Bancor", - "contractAddress" : "0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C" - }, - { - "decimalCount" : 18, - "symbol" : "BAT", - "name" : "Basic Attention", - "contractAddress" : "0x0D8775F648430679A709E98d2b0Cb6250d2887EF" - }, - { - "decimalCount" : 18, - "symbol" : "BNB", - "name" : "Binance Coin", - "contractAddress" : "0xb8c77482e45f1f44de1745f52c74426c631bdd52" - }, - { - "decimalCount" : 18, - "symbol" : "BUSD", - "name" : "Binance USD", - "contractAddress" : "0x4fabb145d64652a948d72533023f6e7a623c7c53" - }, - { - "decimalCount" : 18, - "symbol" : "STU", - "name" : "bitJob", - "contractAddress" : "0x0371a82e4a9d0a4312f3ee2ac9c6958512891372" - }, - { - "decimalCount" : 18, - "symbol" : "BIX", - "name" : "BIX Token", - "contractAddress" : "0xb3104b4b9da82025e8b9f8fb28b3553ce2f67069" - }, - { - "decimalCount" : 18, - "symbol" : "VEE", - "name" : "BLOCKv", - "contractAddress" : "0x340d2bde5eb28c1eed91b2f790723e3b160613b7" - }, - { - "decimalCount" : 18, - "symbol" : "BLZ", - "name" : "Bluzelle", - "contractAddress" : "0x5732046a883704404f284ce41ffadd5b007fd668" - }, - { - "decimalCount" : 18, - "symbol" : "BRD", - "name" : "BRD Token", - "contractAddress" : "0x558ec3152e2eb2174905cd19aea4e34a23de9ad6" - }, - { - "decimalCount" : 8, - "symbol" : "BTM", - "name" : "Bytom", - "contractAddress" : "0xcb97e65f07da24d46bcdd078ebebd7c6e6e3d750" - }, - { - "decimalCount" : 18, - "symbol" : "CAS", - "name" : "Cashaa", - "contractAddress" : "0xe8780b48bdb05f928697a5e8155f672ed91462f7" - }, - { - "decimalCount" : 18, - "symbol" : "CSP", - "name" : "Caspian", - "contractAddress" : "0xa6446d655a0c34bc4f05042ee88170d056cbaf45" - }, - { - "decimalCount" : 18, - "symbol" : "LINK", - "name" : "ChainLink", - "contractAddress" : "0x514910771af9ca656af840dff83e8264ecf986ca" - }, - { - "decimalCount" : 8, - "symbol" : "TIME", - "name" : "Chronobank", - "contractAddress" : "0x6531f133e6DeeBe7F2dcE5A0441aA7ef330B4e53" - }, - { - "decimalCount" : 18, - "symbol" : "CND", - "name" : "Cindicator", - "contractAddress" : "0xd4c435f5b09f855c3317c8524cb1f586e42795fa" - }, - { - "decimalCount" : 8, - "symbol" : "CVC", - "name" : "Civic", - "contractAddress" : "0x41e5560054824ea6b0732e656e3ad64e20e94e45" - }, - { - "decimalCount" : 18, - "symbol" : "CNN", - "name" : "CNN Token", - "contractAddress" : "0x8713d26637cf49e1b6b4a7ce57106aabc9325343" - }, - { - "decimalCount" : 18, - "symbol" : "CFI", - "name" : "Cofound.it", - "contractAddress" : "0x12FEF5e57bF45873Cd9B62E9DBd7BFb99e32D73e" - }, - { - "decimalCount" : 18, - "symbol" : "CCC", - "name" : "Container Crypto Coin", - "contractAddress" : "0x9e3359f862b6c7f5c660cfd6d1aa6909b1d9504d" - }, - { - "decimalCount" : 6, - "symbol" : "CS", - "name" : "Credits", - "contractAddress" : "0x46b9ad944d1059450da1163511069c718f699d31" - }, - { - "decimalCount" : 18, - "symbol" : "CRPT", - "name" : "Crypterium", - "contractAddress" : "0x08389495d7456e1951ddf7c3a1314a4bfb646d8b" - }, - { - "decimalCount" : 18, - "symbol" : "AUTO", - "name" : "CUBE", - "contractAddress" : "0x622dFfCc4e83C64ba959530A5a5580687a57581b" - }, - { - "decimalCount" : 18, - "symbol" : "CMT", - "name" : "CyberMiles", - "contractAddress" : "0xf85fEea2FdD81d51177F6b8F35F0e6734Ce45F5F" - }, - { - "decimalCount" : 18, - "symbol" : "DAI", - "name" : "Dai", - "contractAddress" : "0x6b175474e89094c44da98b954eedeac495271d0f" - }, - { - "decimalCount" : 18, - "symbol" : "MANA", - "name" : "Decentraland", - "contractAddress" : "0x0F5D2fB29fb7d3CFeE444a200298f468908cC942" - }, - { - "decimalCount" : 8, - "symbol" : "DENT", - "name" : "DENT", - "contractAddress" : "0x3597bfd533a99c9aa083587b074434e61eb0a258" - }, - { - "decimalCount" : 0, - "symbol" : "XJP", - "name" : "Digital JPY", - "contractAddress" : "0x39689fE671C01fcE173395f6BC45D4C332026666" - }, - { - "decimalCount" : 9, - "symbol" : "DGD", - "name" : "DigixDAO", - "contractAddress" : "0xE0B7927c4aF23765Cb51314A0E0521A9645F0E2A" - }, - { - "decimalCount" : 18, - "symbol" : "DNT", - "name" : "district0x", - "contractAddress" : "0x0abdace70d3790235af448c88547603b945604ea" - }, - { - "decimalCount" : 18, - "symbol" : "DOV", - "name" : "Dovu", - "contractAddress" : "0xac3211a5025414af2866ff09c23fc18bc97e79b1" - }, - { - "decimalCount" : 18, - "symbol" : "DRGN", - "name" : "Dragon", - "contractAddress" : "0x419c4db4b9e25d6db2ad9691ccb832c8d9fda05e" - }, - { - "decimalCount" : 0, - "symbol" : "EDG", - "name" : "Edgeless", - "contractAddress" : "0x08711D3B02C8758F2FB3ab4e80228418a7F8e39c" - }, - { - "decimalCount" : 18, - "symbol" : "EDO", - "name" : "Eidoo", - "contractAddress" : "0xced4e93198734ddaff8492d525bd258d49eb388e" - }, - { - "decimalCount" : 8, - "symbol" : "ENG", - "name" : "Enigma", - "contractAddress" : "0xf0ee6b27b759c9893ce4f094b49ad28fd15a23e4" - }, - { - "decimalCount" : 18, - "symbol" : "ENJ", - "name" : "EnjinCoin", - "contractAddress" : "0xF629cBd94d3791C9250152BD8dfBDF380E2a3B9c" - }, - { - "decimalCount" : 18, - "symbol" : "EOS", - "name" : "EOS", - "contractAddress" : "0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0" - }, - { - "decimalCount" : 2, - "symbol" : "EUR.AVM", - "name" : "EUR AVM", - "contractAddress" : "0xF01Cd2f1c9E42c509d309aC8C5b29B6dA8E64b1a" - }, - { - "decimalCount" : 18, - "symbol" : "XUC", - "name" : "Exchange Union", - "contractAddress" : "0xc324a2f6b05880503444451b8b27e6f9e63287cb" - }, - { - "decimalCount" : 18, - "symbol" : "1ST", - "name" : "FirstBlood", - "contractAddress" : "0xAf30D2a7E90d7DC361c8C4585e9BB7D2F6f15bc7" - }, - { - "decimalCount" : 18, - "symbol" : "FXC", - "name" : "Flexacoin", - "contractAddress" : "0x4a57E687b9126435a9B19E4A802113e266AdeBde" - }, - { - "decimalCount" : 8, - "symbol" : "FUN", - "name" : "FunFair", - "contractAddress" : "0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b" - }, - { - "decimalCount" : 18, - "symbol" : "FSN", - "name" : "Fusion", - "contractAddress" : "0xd0352a019e9ab9d757776f532377aaebd36fd541" - }, - { - "decimalCount" : 2, - "symbol" : "GUSD", - "name" : "Gemini Dollar", - "contractAddress" : "0x056fd409e1d7a124bd7017459dfea2f387b6d5cd" - }, - { - "decimalCount" : 9, - "symbol" : "GNX", - "name" : "Genaro Network", - "contractAddress" : "0x6ec8a24cabdc339a06a172f8223ea557055adaa5" - }, - { - "decimalCount" : 18, - "symbol" : "GVT", - "name" : "Genesis Vision", - "contractAddress" : "0x103c3A209da59d3E7C4A89307e66521e081CFDF0" - }, - { - "decimalCount" : 5, - "symbol" : "GTO", - "name" : "Gifto", - "contractAddress" : "0xc5bbae50781be1669306b9e001eff57a2957b09d" - }, - { - "decimalCount" : 8, - "symbol" : "GBX", - "name" : "Globitex", - "contractAddress" : "0x12fcd6463e66974cf7bbc24ffc4d40d6be458283" - }, - { - "decimalCount" : 18, - "symbol" : "GNO", - "name" : "Gnosis", - "contractAddress" : "0x6810e776880C02933D47DB1b9fc05908e5386b96" - }, - { - "decimalCount" : 18, - "symbol" : "GNT", - "name" : "Golem", - "contractAddress" : "0xa74476443119A942dE498590Fe1f2454d7D4aC0d" - }, - { - "decimalCount" : 18, - "symbol" : "HPB", - "name" : "HPBCoin", - "contractAddress" : "0x38c6a68304cdefb9bec48bbfaaba5c5b47818bb2" - }, - { - "decimalCount" : 18, - "symbol" : "HYDRO", - "name" : "Hydro", - "contractAddress" : "0xebbdf302c940c6bfd49c6b165f457fdb324649bc" - }, - { - "decimalCount" : 18, - "symbol" : "ICX", - "name" : "ICON", - "contractAddress" : "0xb5a5f22694352c15b00323844ad545abb2b11028" - }, - { - "decimalCount" : 18, - "symbol" : "ICN", - "name" : "ICONOMI", - "contractAddress" : "0x888666CA69E0f178DED6D75b5726Cee99A87D698" - }, - { - "decimalCount" : 9, - "symbol" : "RLC", - "name" : "iExec RLC", - "contractAddress" : "0x607F4C5BB672230e8672085532f7e901544a7375" - }, - { - "decimalCount" : 18, - "symbol" : "IOST", - "name" : "IOStoken", - "contractAddress" : "0xfa1a856cfa3409cfa145fa4e20eb270df3eb21ab" - }, - { - "decimalCount" : 18, - "symbol" : "ITC", - "name" : "IOT Chain", - "contractAddress" : "0x5e6b6d9abad9093fdc861ea1600eba1b355cd940" - }, - { - "decimalCount" : 8, - "symbol" : "IXT", - "name" : "iXledger", - "contractAddress" : "0xfca47962d45adfdfd1ab2d972315db4ce7ccf094" - }, - { - "decimalCount" : 18, - "symbol" : "KIN", - "name" : "Kin", - "contractAddress" : "0x818Fc6C2Ec5986bc6E2CBf00939d90556aB12ce5" - }, - { - "decimalCount" : 18, - "symbol" : "KNC", - "name" : "Kyber Network", - "contractAddress" : "0xdd974D5C2e2928deA5F71b9825b8b646686BD200" - }, - { - "decimalCount" : 18, - "symbol" : "LOOM", - "name" : "Loom", - "contractAddress" : "0xa4e8c3ec456107ea67d3075bf9e3df3a75823db0" - }, - { - "decimalCount" : 18, - "symbol" : "LRC", - "name" : "Loopring", - "contractAddress" : "0xbbbbca6a901c926f240b89eacb641d8aec7aeafd" - }, - { - "decimalCount" : 18, - "symbol" : "LUN", - "name" : "Lunyr", - "contractAddress" : "0xfa05A73FfE78ef8f1a739473e462c54bae6567D9" - }, - { - "decimalCount" : 18, - "symbol" : "MKR", - "name" : "Maker", - "contractAddress" : "0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2" - }, - { - "decimalCount" : 3, - "symbol" : "GUP", - "name" : "Matchpool", - "contractAddress" : "0xf7B098298f7C69Fc14610bf71d5e02c60792894C" - }, - { - "decimalCount" : 18, - "symbol" : "MAN", - "name" : "MATRIX AI Network", - "contractAddress" : "0xe25bcec5d3801ce3a794079bf94adf1b8ccd802d" - }, - { - "decimalCount" : 8, - "symbol" : "MTL", - "name" : "Metal", - "contractAddress" : "0xF433089366899D83a9f26A773D59ec7eCF30355e" - }, - { - "decimalCount" : 8, - "symbol" : "MCO", - "name" : "Monaco", - "contractAddress" : "0xB63B606Ac810a52cCa15e44bB630fd42D8d1d83d" - }, - { - "decimalCount" : 8, - "symbol" : "MYST", - "name" : "Mysterium", - "contractAddress" : "0xa645264C5603E96c3b0B078cdab68733794B0A71" - }, - { - "decimalCount" : 18, - "symbol" : "NAS", - "name" : "Nebulas", - "contractAddress" : "0x5d65D971895Edc438f465c17DB6992698a52318D" - }, - { - "decimalCount" : 18, - "symbol" : "NEXO", - "name" : "Nexo", - "contractAddress" : "0xb62132e35a6c13ee1ee0f84dc5d40bad8d815206" - }, - { - "decimalCount" : 18, - "symbol" : "NCASH", - "name" : "Nucleus Vision", - "contractAddress" : "0x809826cceab68c387726af962713b64cb5cb3cca" - }, - { - "decimalCount" : 18, - "symbol" : "NULS", - "name" : "Nuls", - "contractAddress" : "0xb91318f35bdb262e9423bc7c7c2a3a93dd93c92c" - }, - { - "decimalCount" : 18, - "symbol" : "NMR", - "name" : "Numeraire", - "contractAddress" : "0x1776e1F26f98b1A5dF9cD347953a26dd3Cb46671" - }, - { - "decimalCount" : 18, - "symbol" : "OMG", - "name" : "OmiseGO", - "contractAddress" : "0xd26114cd6EE289AccF82350c8d8487fedB8A0C07" - }, - { - "decimalCount" : 18, - "symbol" : "OXT", - "name" : "Orchid", - "contractAddress" : "0x4575f41308ec1483f3d399aa9a2826d74da13deb" - }, - { - "decimalCount" : 18, - "symbol" : "OST", - "name" : "OST", - "contractAddress" : "0x2C4e8f2D746113d0696cE89B35F0d8bF88E0AEcA" - }, - { - "decimalCount" : 18, - "symbol" : "PRL", - "name" : "Oyster Pearl", - "contractAddress" : "0x1844b21593262668b7248d0f57a220caaba46ab9" - }, - { - "decimalCount" : 8, - "symbol" : "PTOY", - "name" : "Patientory", - "contractAddress" : "0x8Ae4BF2C33a8e667de34B54938B0ccD03Eb8CC06" - }, - { - "decimalCount" : 18, - "symbol" : "PAX", - "name" : "Paxos Standard", - "contractAddress" : "0x8e870d67f660d95d5be530380d0ec0bd388289e1" - }, - { - "decimalCount" : 18, - "symbol" : "PMTN", - "name" : "Peer Mountain", - "contractAddress" : "0xe91df2bb9bccd18c45f01843cab88768d64f2d32" - }, - { - "decimalCount" : 8, - "symbol" : "POE", - "name" : "Po.et", - "contractAddress" : "0x0e0989b1f9b8a38983c2ba8053269ca62ec9b195" - }, - { - "decimalCount" : 18, - "symbol" : "PAL", - "name" : "PolicyPal Network", - "contractAddress" : "0xfeDAE5642668f8636A11987Ff386bfd215F942EE" - }, - { - "decimalCount" : 18, - "symbol" : "POLY", - "name" : "Polymath", - "contractAddress" : "0x9992eC3cF6A55b00978cdDF2b27BC6882d88D1eC" - }, - { - "decimalCount" : 8, - "symbol" : "PPT", - "name" : "Populous", - "contractAddress" : "0xd4fa1460F537bb9085d22C7bcCB5DD450Ef28e3a" - }, - { - "decimalCount" : 6, - "symbol" : "POWR", - "name" : "Power Ledger", - "contractAddress" : "0x595832f8fc6bf59c85c527fec3740a1b7a361269" - }, - { - "decimalCount" : 18, - "symbol" : "NPXS", - "name" : "Pundi X Token", - "contractAddress" : "0xa15c7ebe1f07caf6bff097d8a589fb8ac49ae5b3" - }, - { - "decimalCount" : 6, - "symbol" : "QASH", - "name" : "QASH", - "contractAddress" : "0x618e75ac90b12c6049ba3b27f5d5f8651b0037f6" - }, - { - "decimalCount" : 8, - "symbol" : "QCX", - "name" : "QuickX Protocol", - "contractAddress" : "0xf9e5af7b42d31d51677c75bbbd37c1986ec79aee", - "customIcon": "qcx" - }, - { - "decimalCount" : 8, - "symbol" : "QRL", - "name" : "QRL", - "contractAddress" : "0x697beac28B09E122C4332D163985e8a73121b97F" - }, - { - "decimalCount" : 18, - "symbol" : "QSP", - "name" : "Quantstamp", - "contractAddress" : "0x99ea4dB9EE77ACD40B119BD1dC4E33e1C070b80d" - }, - { - "decimalCount" : 18, - "symbol" : "RDN", - "name" : "Raiden Network", - "contractAddress" : "0x255aa6df07540cb5d3d297f0d0d4d84cb52bc8e6" - }, - { - "decimalCount" : 18, - "symbol" : "RCC", - "name" : "Reality Clash", - "contractAddress" : "0x9b6443b0fb9c241a7fdac375595cea13e6b7807a" - }, - { - "decimalCount" : 6, - "symbol" : "RC1U03", - "name" : "RentalCoin 1.0", - "contractAddress" : "0x7384db1aaa06364d1e6e67f5434391d3eea2a038" - }, - { - "decimalCount" : 18, - "symbol" : "REQ", - "name" : "Request", - "contractAddress" : "0x8f8221aFbB33998d8584A2B05749bA73c37a938a" - }, - { - "decimalCount" : 0, - "symbol" : "REV", - "name" : "Revain", - "contractAddress" : "0x48f775efbe4f5ece6e0df2f7b5932df56823b990" - }, - { - "decimalCount" : 18, - "symbol" : "RCN", - "name" : "Ripio Credit", - "contractAddress" : "0xf970b8e36e23f7fc3fd752eea86f8be8d83375a6" - }, - { - "decimalCount" : 18, - "symbol" : "RVT", - "name" : "Rivetz", - "contractAddress" : "0x3d1ba9be9f66b8ee101911bc36d3fb562eac2244" - }, - { - "decimalCount" : 18, - "symbol" : "RUFF", - "name" : "RUFF", - "contractAddress" : "0xf278c1ca969095ffddded020290cf8b5c424ace2" - }, - { - "decimalCount" : 18, - "symbol" : "SAI", - "name" : "Sai", - "contractAddress" : "0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359" - }, - { - "decimalCount" : 8, - "symbol" : "SALT", - "name" : "SALT", - "contractAddress" : "0x4156D3342D5c385a87D264F90653733592000581" - }, - { - "decimalCount" : 18, - "symbol" : "SAN", - "name" : "SAN", - "contractAddress" : "0x7C5A0CE9267ED19B22F8cae653F198e3E8daf098" - }, - { - "decimalCount" : 8, - "symbol" : "SENT", - "name" : "SENTinel", - "contractAddress" : "0xa44e5137293e855b1b7bc7e2c6f8cd796ffcb037" - }, - { - "decimalCount" : 0, - "symbol" : "SNGLS", - "name" : "Singular DTV", - "contractAddress" : "0xaeC2E87E0A235266D9C5ADc9DEb4b2E29b54D009" - }, - { - "decimalCount" : 8, - "symbol" : "AGI", - "name" : "SingularityNET", - "contractAddress" : "0x8eB24319393716668D768dCEC29356ae9CfFe285" - }, - { - "decimalCount" : 18, - "symbol" : "SRN", - "name" : "SIRIN", - "contractAddress" : "0x68d57c9a1C35f63E2c83eE8e49A64e9d70528D25" - }, - { - "decimalCount" : 18, - "symbol" : "SNM", - "name" : "SONM", - "contractAddress" : "0x983F6d60db79ea8cA4eB9968C6aFf8cfA04B3c63" - }, - { - "decimalCount" : 18, - "symbol" : "SNT", - "name" : "Status", - "contractAddress" : "0x744d70FDBE2Ba4CF95131626614a1763DF805B9E" - }, - { - "decimalCount" : 18, - "symbol" : "STK", - "name" : "STK", - "contractAddress" : "0xae73b38d1c9a8b274127ec30160a4927c4d71824" - }, - { - "decimalCount" : 18, - "symbol" : "STQ", - "name" : "Storiqa", - "contractAddress" : "0x5c3a228510d246b78a3765c20221cbf3082b44a4" - }, - { - "decimalCount" : 8, - "symbol" : "STORJ", - "name" : "Storj", - "contractAddress" : "0xB64ef51C888972c908CFacf59B47C1AfBC0Ab8aC" - }, - { - "decimalCount" : 18, - "symbol" : "STORM", - "name" : "Storm", - "contractAddress" : "0xD0a4b8946Cb52f0661273bfbC6fD0E0C75Fc6433" - }, - { - "decimalCount" : 18, - "symbol" : "STMX", - "name" : "StormX", - "contractAddress" : "0xbE9375C6a420D2eEB258962efB95551A5b722803" - }, - { - "decimalCount" : 18, - "symbol" : "STX", - "name" : "Stox", - "contractAddress" : "0x006BeA43Baa3f7A6f765F14f10A1a1b08334EF45" - }, - { - "decimalCount" : 18, - "symbol" : "SUB", - "name" : "Substratum", - "contractAddress" : "0x8D75959f1E61EC2571aa72798237101F084DE63a" - }, - { - "decimalCount" : 18, - "symbol" : "SNC", - "name" : "SunContract", - "contractAddress" : "0xF4134146AF2d511Dd5EA8cDB1C4AC88C57D60404" - }, - { - "decimalCount" : 18, - "symbol" : "SUSHI", - "name" : "SushiSwap", - "contractAddress" : "0x6b3595068778dd592e39a122f4f5a5cf09c90fe2" - }, - { - "decimalCount" : 2, - "symbol" : "TEL", - "name" : "Telcoin", - "contractAddress" : "0x467bccd9d29f223bce8043b84e8c8b282827790f" - }, - { - "decimalCount" : 18, - "symbol" : "PAY", - "name" : "TenX", - "contractAddress" : "0xB97048628DB6B661D4C2aA833e95Dbe1A905B280" - }, - { - "decimalCount" : 6, - "symbol" : "USDT", - "name" : "Tether USD", - "contractAddress" : "0xdac17f958d2ee523a2206206994597c13d831ec7" - }, - { - "decimalCount" : 18, - "symbol" : "THETA", - "name" : "Theta Token", - "contractAddress" : "0x3883f5e181fccaf8410fa61e12b59bad963fb645" - }, - { - "decimalCount" : 6, - "symbol" : "TRX", - "name" : "TRON", - "contractAddress" : "0xf230b790e05390fc8295f4d3f60332c93bed42e2" - }, - { - "decimalCount" : 18, - "symbol" : "TUSD", - "name" : "TrueUSD", - "contractAddress" : "0x0000000000085d4780B73119b644AE5ecd22b376" - }, - { - "decimalCount" : 8, - "symbol" : "UCASH", - "name" : "UCASH", - "contractAddress" : "0x92e52a1a235d9a103d970901066ce910aacefd37" - }, - { - "decimalCount" : 18, - "symbol" : "UNI", - "name" : "Uniswap", - "contractAddress" : "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984" - }, - { - "decimalCount" : 6, - "symbol" : "USDC", - "name" : "USD Coin", - "contractAddress" : "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" - }, - { - "decimalCount" : 18, - "symbol" : "VIB", - "name" : "Viberate", - "contractAddress" : "0x2C974B2d0BA1716E644c1FC59982a89DDD2fF724" - }, - { - "decimalCount" : 8, - "symbol" : "VGX", - "name" : "Voyager", - "contractAddress" : "0x5af2be193a6abca9c8817001f45744777db30756" - }, - { - "decimalCount" : 18, - "symbol" : "WTC", - "name" : "Waltonchain", - "contractAddress" : "0xb7cb1c96db6b22b0d3d9536e0108d062bd488f74" - }, - { - "decimalCount" : 8, - "symbol" : "WAX", - "name" : "WAX", - "contractAddress" : "0x39Bb259F66E1C59d5ABEF88375979b4D20D98022" - }, - { - "decimalCount" : 18, - "symbol" : "WETH", - "name" : "Wrapped Ether", - "contractAddress" : "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" - }, - { - "decimalCount" : 18, - "symbol" : "WPR", - "name" : "WePower", - "contractAddress" : "0x4CF488387F035FF08c371515562CBa712f9015d4" - }, - { - "decimalCount" : 18, - "symbol" : "WINGS", - "name" : "Wings", - "contractAddress" : "0x667088b212ce3d06a1b553a7221E1fD19000d9aF" - }, - { - "decimalCount" : 6, - "symbol" : "WRC", - "name" : "Worldcore", - "contractAddress" : "0x72adadb447784dd7ab1f472467750fc485e4cb2d" - }, - { - "decimalCount" : 18, - "symbol" : "ZLA", - "name" : "Zilla", - "contractAddress" : "0xfd8971d5e8e1740ce2d0a84095fca4de729d0c16" - }, - { - "decimalCount" : 0, - "symbol" : "GD", - "name" : "Zilla GD", - "contractAddress" : "0xb4cebdb66b3a5183fd4764a25cad1fc109535016" - }, - { - "decimalCount" : 12, - "symbol" : "ZIL", - "name" : "Zilliqa", - "contractAddress" : "0x05f4a42e251f2d52b8ed15E9FEdAacFcEF1FAD27" + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7FF4169a6B5122b664c51c95727d87750eC07c84/logo.png", + "symbol": "10SET", + "name": "10Set Token", + "contractAddress": "0x7FF4169a6B5122b664c51c95727d87750eC07c84", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x111111111117dC0aa78b770fA6A738034120C302/logo.png", + "symbol": "1INCH", + "name": "1INCH Token", + "contractAddress": "0x111111111117dC0aa78b770fA6A738034120C302", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xBBc2AE13b23d715c30720F079fcd9B4a74093505/logo.png", + "symbol": "ERN", + "name": "@EthernityChain $ERN Token", + "contractAddress": "0xBBc2AE13b23d715c30720F079fcd9B4a74093505", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "1ST", + "name": "FirstBlood", + "contractAddress": "0xAf30D2a7E90d7DC361c8C4585e9BB7D2F6f15bc7", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9/logo.png", + "symbol": "AAVE", + "name": "Aave Token", + "contractAddress": "0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "ABT", + "name": "ArcBlock", + "contractAddress": "0xb98d4c97425d9908e66e53a6fdf673acca0be986", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xADE00C28244d5CE17D72E40330B1c318cD12B7c3/logo.png", + "symbol": "ADX", + "name": "AdEx Network", + "contractAddress": "0xADE00C28244d5CE17D72E40330B1c318cD12B7c3", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "AE", + "name": "Aeternity", + "contractAddress": "0x5ca9a71b1d01849c0a95490cc00559717fcf0d1d", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "symbol": "AION", + "name": "Aion", + "contractAddress": "0x4CEdA7906a5Ed2179785Cd3A40A69ee8bc99C466", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8eB24319393716668D768dCEC29356ae9CfFe285/logo.png", + "symbol": "AGI", + "name": "SingularityNET Token", + "contractAddress": "0x8eB24319393716668D768dCEC29356ae9CfFe285", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x626E8036dEB333b408Be468F951bdB42433cBF18/logo.png", + "symbol": "AIOZ", + "name": "AIOZ Network", + "contractAddress": "0x626E8036dEB333b408Be468F951bdB42433cBF18", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x00a8b738E453fFd858a7edf03bcCfe20412f0Eb0/logo.png", + "symbol": "ALBT", + "name": "AllianceBlock Token", + "contractAddress": "0x00a8b738E453fFd858a7edf03bcCfe20412f0Eb0", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "AMLT", + "name": "AMLT by Coinfirm", + "contractAddress": "0xca0e7269600d353f70b14ad118a49575455c0f2f", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x95a4492F028aa1fd432Ea71146b433E7B4446611/logo.png", + "symbol": "APY", + "name": "APY Governance Token", + "contractAddress": "0x95a4492F028aa1fd432Ea71146b433E7B4446611", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "symbol": "ARN", + "name": "Aeron", + "contractAddress": "0xBA5F11b16B155792Cf3B2E6880E8706859A8AEB6", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 4, + "symbol": "AST", + "name": "Airswap", + "contractAddress": "0x27054b13b1b798b345b591a4d22e6562d47ea75a", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "AVM", + "name": "AVM Ecosystem", + "contractAddress": "0x74004a7227615fb52b82d17ffabfa376907d8a4d", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "ELF", + "name": "Aelf", + "contractAddress": "0xbf2179859fc6d5bee9bf9158632dc51678a4100e", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "AMB", + "name": "Amber", + "contractAddress": "0x4dc3643dbc642b72c158e7f3d2ff232df61cb6ce", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xfF20817765cB7f73d4bde2e66e067E58D11095C2/logo.png", + "symbol": "AMP", + "name": "Amp", + "contractAddress": "0xfF20817765cB7f73d4bde2e66e067E58D11095C2", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 9, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets//logo.png", + "symbol": "AMPL", + "name": "Ampleforth", + "contractAddress": "0xD46bA6D942050d489DBd938a2C909A5d5039A161", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa117000000f279D81A1D3cc75430fAA017FA5A2e/logo.png", + "symbol": "ANT", + "name": "Aragon Network Token", + "contractAddress": "0xa117000000f279D81A1D3cc75430fAA017FA5A2e", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "APPC", + "name": "AppCoins", + "contractAddress": "0x1a7a8bd9106f2b8d977e08582dc7d24c723ab0db", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1337DEF16F9B486fAEd0293eb623Dc8395dFE46a/logo.png", + "symbol": "ARMOR", + "name": "Armor", + "contractAddress": "0x1337DEF16F9B486fAEd0293eb623Dc8395dFE46a", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 0, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xdacD69347dE42baBfAEcD09dC88958378780FB62/logo.png", + "symbol": "ATRI", + "name": "AtariToken", + "contractAddress": "0xdacD69347dE42baBfAEcD09dC88958378780FB62", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "ATMI", + "name": "Atonomi", + "contractAddress": "0x97aeb5066e1a590e868b511457beb6fe99d329f5", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x18aAA7115705e8be94bfFEBDE57Af9BFc265B998/logo.png", + "symbol": "AUDIO", + "name": "Audius", + "contractAddress": "0x18aAA7115705e8be94bfFEBDE57Af9BFc265B998", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "AUTO", + "name": "CUBE", + "contractAddress": "0x622dFfCc4e83C64ba959530A5a5580687a57581b", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xF5D669627376EBd411E34b98F19C868c8ABA5ADA/logo.png", + "symbol": "AXS", + "name": "Axie Infinity", + "contractAddress": "0xF5D669627376EBd411E34b98F19C868c8ABA5ADA", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1C9491865a1DE77C5b6e19d2E6a5F1D7a6F2b25F/logo.png", + "symbol": "MATTER", + "name": "Antimatter.Finance Governance Token", + "contractAddress": "0x1C9491865a1DE77C5b6e19d2E6a5F1D7a6F2b25F", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x71F85B2E46976bD21302B64329868fd15eb0D127/logo.png", + "symbol": "AXN", + "name": "Axion", + "contractAddress": "0x71F85B2E46976bD21302B64329868fd15eb0D127", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xba100000625a3754423978a60c9317c58a424e3D/logo.png", + "symbol": "BAL", + "name": "Balancer", + "contractAddress": "0xba100000625a3754423978a60c9317c58a424e3D", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "BIX", + "name": "BIX Token", + "contractAddress": "0xb3104b4b9da82025e8b9f8fb28b3553ce2f67069", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5Dc02Ea99285E17656b8350722694c35154DB1E8/logo.png", + "symbol": "BOND", + "name": "BOND", + "contractAddress": "0x5Dc02Ea99285E17656b8350722694c35154DB1E8", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "BRD", + "name": "BRD Token", + "contractAddress": "0x558ec3152e2eb2174905cd19aea4e34a23de9ad6", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "BNT", + "name": "Bancor", + "contractAddress": "0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xBA11D00c5f74255f56a5E366F4F77f5A186d7f55/logo.png", + "symbol": "BAND", + "name": "BandToken", + "contractAddress": "0xBA11D00c5f74255f56a5E366F4F77f5A186d7f55", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x54C9EA2E9C9E8eD865Db4A4ce6711C2a0d5063Ba/logo.png", + "symbol": "BART", + "name": "BarterTrade", + "contractAddress": "0x54C9EA2E9C9E8eD865Db4A4ce6711C2a0d5063Ba", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 9, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x07150e919B4De5fD6a63DE1F9384828396f25fDC/logo.png", + "symbol": "BASE", + "name": "Base Protocol", + "contractAddress": "0x07150e919B4De5fD6a63DE1F9384828396f25fDC", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0D8775F648430679A709E98d2b0Cb6250d2887EF/logo.png", + "symbol": "BAT", + "name": "Basic Attention Token", + "contractAddress": "0x0D8775F648430679A709E98d2b0Cb6250d2887EF", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA91ac63D040dEB1b7A5E4d4134aD23eb0ba07e14/logo.png", + "symbol": "BEL", + "name": "Bella", + "contractAddress": "0xA91ac63D040dEB1b7A5E4d4134aD23eb0ba07e14", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "BNB", + "name": "Binance Coin", + "contractAddress": "0xb8c77482e45f1f44de1745f52c74426c631bdd52", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "BUSD", + "name": "Binance USD", + "contractAddress": "0x4fabb145d64652a948d72533023f6e7a623c7c53", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 9, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x67c597624B17b16fb77959217360B7cD18284253/logo.png", + "symbol": "MARK", + "name": "Benchmark", + "contractAddress": "0x67c597624B17b16fb77959217360B7cD18284253", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7671904eed7f10808B664fc30BB8693FD7237abF/logo.png", + "symbol": "BBR", + "name": "BitberryToken", + "contractAddress": "0x7671904eed7f10808B664fc30BB8693FD7237abF", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xCF3C8Be2e2C42331Da80EF210e9B1b307C03d36A/logo.png", + "symbol": "BEPRO", + "name": "BetProtocolToken", + "contractAddress": "0xCF3C8Be2e2C42331Da80EF210e9B1b307C03d36A", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0c7D5ae016f806603CB1782bEa29AC69471CAb9c/logo.png", + "symbol": "BFC", + "name": "Bifrost", + "contractAddress": "0x0c7D5ae016f806603CB1782bEa29AC69471CAb9c", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9BE89D2a4cd102D8Fecc6BF9dA793be995C22541/logo.png", + "symbol": "BBTC", + "name": "Binance Wrapped BTC", + "contractAddress": "0x9BE89D2a4cd102D8Fecc6BF9dA793be995C22541", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x25e1474170c4c0aA64fa98123bdc8dB49D7802fa/logo.png", + "symbol": "BID", + "name": "Bidao", + "contractAddress": "0x25e1474170c4c0aA64fa98123bdc8dB49D7802fa", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x70401dFD142A16dC7031c56E862Fc88Cb9537Ce0/logo.png", + "symbol": "BIRD", + "name": "Bird.Money", + "contractAddress": "0x70401dFD142A16dC7031c56E862Fc88Cb9537Ce0", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xAec7e1f531Bb09115103C53ba76829910Ec48966/logo.png", + "symbol": "BLANK", + "name": "Blank Token", + "contractAddress": "0xAec7e1f531Bb09115103C53ba76829910Ec48966", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "BLZ", + "name": "Bluzelle", + "contractAddress": "0x5732046a883704404f284ce41ffadd5b007fd668", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x725C263e32c72dDC3A19bEa12C5a0479a81eE688/logo.png", + "symbol": "BMI", + "name": "Bridge Mutual", + "contractAddress": "0x725C263e32c72dDC3A19bEa12C5a0479a81eE688", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xD2dDa223b2617cB616c1580db421e4cFAe6a8a85/logo.png", + "symbol": "BONDLY", + "name": "Bondly Token", + "contractAddress": "0xD2dDa223b2617cB616c1580db421e4cFAe6a8a85", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "symbol": "BTM", + "name": "Bytom", + "contractAddress": "0xcb97e65f07da24d46bcdd078ebebd7c6e6e3d750", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "CANDY", + "name": "Candy", + "contractAddress": "0xf2eab3a2034d3f6b63734d2e08262040e3ff7b48", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x954b890704693af242613edEf1B603825afcD708/logo.png", + "symbol": "CARD", + "name": "Cardstack", + "contractAddress": "0x954b890704693af242613edEf1B603825afcD708", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "CAS", + "name": "Cashaa", + "contractAddress": "0xe8780b48bdb05f928697a5e8155f672ed91462f7", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "CCC", + "name": "Container Crypto Coin", + "contractAddress": "0x9e3359f862b6c7f5c660cfd6d1aa6909b1d9504d", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x17aC188e09A7890a1844E5E65471fE8b0CcFadF3/logo.png", + "symbol": "CC10", + "name": "Cryptocurrency Top 10 Tokens Index", + "contractAddress": "0x17aC188e09A7890a1844E5E65471fE8b0CcFadF3", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x26c8AFBBFE1EBaca03C2bB082E69D0476Bffe099/logo.png", + "symbol": "CELL", + "name": "Cellframe Token", + "contractAddress": "0x26c8AFBBFE1EBaca03C2bB082E69D0476Bffe099", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 4, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xaaAEBE6Fe48E54f431b0C390CfaF0b017d09D42d/logo.png", + "symbol": "CEL", + "name": "Celsius", + "contractAddress": "0xaaAEBE6Fe48E54f431b0C390CfaF0b017d09D42d", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "CFI", + "name": "Cofound.it", + "contractAddress": "0x12FEF5e57bF45873Cd9B62E9DBd7BFb99e32D73e", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC4C2614E694cF534D407Ee49F8E44D125E4681c4/logo.png", + "symbol": "CHAIN", + "name": "Chain Games", + "contractAddress": "0xC4C2614E694cF534D407Ee49F8E44D125E4681c4", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x63b4f3e3fa4e438698CE330e365E831F7cCD1eF4/logo.png", + "symbol": "CFi", + "name": "CyberFi Token", + "contractAddress": "0x63b4f3e3fa4e438698CE330e365E831F7cCD1eF4", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "CNN", + "name": "CNN Token", + "contractAddress": "0x8713d26637cf49e1b6b4a7ce57106aabc9325343", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xDDB3422497E61e13543BeA06989C0789117555c5/logo.png", + "symbol": "COTI", + "name": "COTI Token", + "contractAddress": "0xDDB3422497E61e13543BeA06989C0789117555c5", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "CSP", + "name": "Caspian", + "contractAddress": "0xa6446d655a0c34bc4f05042ee88170d056cbaf45", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1fE24F25b1Cf609B9c4e7E12D802e3640dFA5e43/logo.png", + "symbol": "CGG", + "name": "ChainGuardians Governance Token", + "contractAddress": "0x1fE24F25b1Cf609B9c4e7E12D802e3640dFA5e43", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "CMT", + "name": "CyberMiles", + "contractAddress": "0xf85fEea2FdD81d51177F6b8F35F0e6734Ce45F5F", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "CND", + "name": "Cindicator", + "contractAddress": "0xd4c435f5b09f855c3317c8524cb1f586e42795fa", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "symbol": "CVC", + "name": "Civic", + "contractAddress": "0x41e5560054824ea6b0732e656e3ad64e20e94e45", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x69692D3345010a207b759a7D1af6fc7F38b35c5E/logo.png", + "symbol": "CHADS", + "name": "chads.vc", + "contractAddress": "0x69692D3345010a207b759a7D1af6fc7F38b35c5E", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x514910771AF9Ca656af840dff83E8264EcF986CA/logo.png", + "symbol": "Link", + "name": "ChainLink", + "contractAddress": "0x514910771AF9Ca656af840dff83E8264EcF986CA", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "symbol": "TIME", + "name": "Chronobank", + "contractAddress": "0x6531f133e6DeeBe7F2dcE5A0441aA7ef330B4e53", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xE61fDAF474Fac07063f2234Fb9e60C1163Cfa850/logo.png", + "symbol": "COIN", + "name": "Coin Utility Token", + "contractAddress": "0xE61fDAF474Fac07063f2234Fb9e60C1163Cfa850", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xc00e94Cb662C3520282E6f5717214004A7f26888/logo.png", + "symbol": "COMP", + "name": "Compound", + "contractAddress": "0xc00e94Cb662C3520282E6f5717214004A7f26888", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x38e4adB44ef08F22F5B5b76A8f0c2d0dCbE7DcA1/logo.png", + "symbol": "CVP", + "name": "Concentrated Voting Power", + "contractAddress": "0x38e4adB44ef08F22F5B5b76A8f0c2d0dCbE7DcA1", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xc834Fa996fA3BeC7aAD3693af486ae53D8aA8B50/logo.png", + "symbol": "CONV", + "name": "Convergence", + "contractAddress": "0xc834Fa996fA3BeC7aAD3693af486ae53D8aA8B50", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x62359Ed7505Efc61FF1D56fEF82158CcaffA23D7/logo.png", + "symbol": "CORE", + "name": "cVault.finance", + "contractAddress": "0x62359Ed7505Efc61FF1D56fEF82158CcaffA23D7", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x2ba592F78dB6436527729929AAf6c908497cB200/logo.png", + "symbol": "CREAM", + "name": "Cream", + "contractAddress": "0x2ba592F78dB6436527729929AAf6c908497cB200", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 6, + "symbol": "CS", + "name": "Credits", + "contractAddress": "0x46b9ad944d1059450da1163511069c718f699d31", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xaC0104Cca91D167873B8601d2e71EB3D4D8c33e0/logo.png", + "symbol": "CWS", + "name": "Crowns", + "contractAddress": "0xaC0104Cca91D167873B8601d2e71EB3D4D8c33e0", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "CRPT", + "name": "Crypterium", + "contractAddress": "0x08389495d7456e1951ddf7c3a1314a4bfb646d8b", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0f51bb10119727a7e5eA3538074fb341F56B09Ad/logo.png", + "symbol": "DAO", + "name": "DAO Maker Token", + "contractAddress": "0x0f51bb10119727a7e5eA3538074fb341F56B09Ad", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x26CE25148832C04f3d7F26F32478a9fe55197166/logo.png", + "symbol": "DEXT", + "name": "DEXTools", + "contractAddress": "0x26CE25148832C04f3d7F26F32478a9fe55197166", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6B175474E89094C44Da98b954EedeAC495271d0F/logo.png", + "symbol": "DAI", + "name": "Dai Stablecoin", + "contractAddress": "0x6B175474E89094C44Da98b954EedeAC495271d0F", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xFbEEa1C75E4c4465CB2FCCc9c6d6afe984558E20/logo.png", + "symbol": "DDIM", + "name": "DuckDaoDime", + "contractAddress": "0xFbEEa1C75E4c4465CB2FCCc9c6d6afe984558E20", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xE1c7E30C42C24582888C758984f6e382096786bd/logo.png", + "symbol": "XCUR", + "name": "Curate", + "contractAddress": "0xE1c7E30C42C24582888C758984f6e382096786bd", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xD533a949740bb3306d119CC777fa900bA034cd52/logo.png", + "symbol": "CRV", + "name": "Curve DAO Token", + "contractAddress": "0xD533a949740bb3306d119CC777fa900bA034cd52", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x30f271C9E86D2B7d00a6376Cd96A1cFBD5F0b9b3/logo.png", + "symbol": "DEC", + "name": "Decentr", + "contractAddress": "0x30f271C9E86D2B7d00a6376Cd96A1cFBD5F0b9b3", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9EA3b5b4EC044b70375236A281986106457b20EF/logo.png", + "symbol": "DELTA", + "name": "Delta Financial", + "contractAddress": "0x9EA3b5b4EC044b70375236A281986106457b20EF", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "symbol": "DENT", + "name": "DENT", + "contractAddress": "0x3597bfd533a99c9aa083587b074434e61eb0a258", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xd379700999F4805Ce80aa32DB46A94dF64561108/logo.png", + "symbol": "DETS", + "name": "Dextrust", + "contractAddress": "0xd379700999F4805Ce80aa32DB46A94dF64561108", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5cAf454Ba92e6F2c929DF14667Ee360eD9fD5b26/logo.png", + "symbol": "DEV", + "name": "Dev", + "contractAddress": "0x5cAf454Ba92e6F2c929DF14667Ee360eD9fD5b26", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5F64Ab1544D28732F0A24F4713c2C8ec0dA089f0/logo.png", + "symbol": "DEXTF", + "name": "DEXTF Token", + "contractAddress": "0x5F64Ab1544D28732F0A24F4713c2C8ec0dA089f0", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xc719d010B63E5bbF2C0551872CD5316ED26AcD83/logo.png", + "symbol": "DIP", + "name": "Decentralized Insurance Protocol", + "contractAddress": "0xc719d010B63E5bbF2C0551872CD5316ED26AcD83", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x26E43759551333e57F073bb0772F50329A957b30/logo.png", + "symbol": "DGVC", + "name": "DegenVC", + "contractAddress": "0x26E43759551333e57F073bb0772F50329A957b30", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x152687Bc4A7FCC89049cF119F9ac3e5aCF2eE7ef/logo.png", + "symbol": "DHC", + "name": "DeltaHub Community", + "contractAddress": "0x152687Bc4A7FCC89049cF119F9ac3e5aCF2eE7ef", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x431ad2ff6a9C365805eBaD47Ee021148d6f7DBe0/logo.png", + "symbol": "DF", + "name": "dForce", + "contractAddress": "0x431ad2ff6a9C365805eBaD47Ee021148d6f7DBe0", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 9, + "symbol": "DGD", + "name": "DigixDAO", + "contractAddress": "0xE0B7927c4aF23765Cb51314A0E0521A9645F0E2A", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x88EF27e69108B2633F8E1C184CC37940A075cC02/logo.png", + "symbol": "DEGO", + "name": "dego.finance", + "contractAddress": "0x88EF27e69108B2633F8E1C184CC37940A075cC02", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x84cA8bc7997272c7CfB4D0Cd3D55cd942B3c9419/logo.png", + "symbol": "DIA", + "name": "DIA", + "contractAddress": "0x84cA8bc7997272c7CfB4D0Cd3D55cd942B3c9419", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xEd91879919B71bB6905f23af0A68d231EcF87b14/logo.png", + "symbol": "DMG", + "name": "DMM: Governance", + "contractAddress": "0xEd91879919B71bB6905f23af0A68d231EcF87b14", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9cEB84f92A0561fa3Cc4132aB9c0b76A59787544/logo.png", + "symbol": "DOKI", + "name": "DokiDokiFinance", + "contractAddress": "0x9cEB84f92A0561fa3Cc4132aB9c0b76A59787544", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "DOV", + "name": "Dovu", + "contractAddress": "0xac3211a5025414af2866ff09c23fc18bc97e79b1", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "DRGN", + "name": "Dragon", + "contractAddress": "0x419c4db4b9e25d6db2ad9691ccb832c8d9fda05e", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "DNT", + "name": "district0x", + "contractAddress": "0x0abdace70d3790235af448c88547603b945604ea", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x3aFfCCa64c2A6f4e3B6Bd9c64CD2C969EFd1ECBe/logo.png", + "symbol": "DSLA", + "name": "DSLA", + "contractAddress": "0x3aFfCCa64c2A6f4e3B6Bd9c64CD2C969EFd1ECBe", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC0bA369c8Db6eB3924965e5c4FD0b4C1B91e305F/logo.png", + "symbol": "DUCK", + "name": "DLP Duck Token", + "contractAddress": "0xC0bA369c8Db6eB3924965e5c4FD0b4C1B91e305F", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x961C8c0B1aaD0c0b10a51FeF6a867E3091BCef17/logo.png", + "symbol": "DYP", + "name": "DeFiYieldProtocol", + "contractAddress": "0x961C8c0B1aaD0c0b10a51FeF6a867E3091BCef17", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x740623d2c797b7D8D1EcB98e9b4Afcf99Ec31E14/logo.png", + "symbol": "DYT", + "name": "DoYourTip", + "contractAddress": "0x740623d2c797b7D8D1EcB98e9b4Afcf99Ec31E14", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "EOS", + "name": "EOS", + "contractAddress": "0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xAa6E8127831c9DE45ae56bB1b0d4D4Da6e5665BD/logo.png", + "symbol": "ETH2x-FLI", + "name": "ETH 2x Flexible Leverage Index", + "contractAddress": "0xAa6E8127831c9DE45ae56bB1b0d4D4Da6e5665BD", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 2, + "symbol": "EUR.AVM", + "name": "EUR AVM", + "contractAddress": "0xF01Cd2f1c9E42c509d309aC8C5b29B6dA8E64b1a", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 0, + "symbol": "EDG", + "name": "Edgeless", + "contractAddress": "0x08711D3B02C8758F2FB3ab4e80228418a7F8e39c", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "EDO", + "name": "Eidoo", + "contractAddress": "0xced4e93198734ddaff8492d525bd258d49eb388e", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x178c820f862B14f316509ec36b13123DA19A6054/logo.png", + "symbol": "EWTB", + "name": "Energy Web Token Bridged", + "contractAddress": "0x178c820f862B14f316509ec36b13123DA19A6054", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xF629cBd94d3791C9250152BD8dfBDF380E2a3B9c/logo.png", + "symbol": "ENJ", + "name": "Enjin Coin", + "contractAddress": "0xF629cBd94d3791C9250152BD8dfBDF380E2a3B9c", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa0246c9032bC3A600820415aE600c6388619A14D/logo.png", + "symbol": "FARM", + "name": "FARM Reward Token", + "contractAddress": "0xa0246c9032bC3A600820415aE600c6388619A14D", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 6, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xE5CAeF4Af8780E59Df925470b050Fb23C43CA68C/logo.png", + "symbol": "FRM", + "name": "Ferrum Network Token", + "contractAddress": "0xE5CAeF4Af8780E59Df925470b050Fb23C43CA68C", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "FXC", + "name": "Flexacoin", + "contractAddress": "0x4a57E687b9126435a9B19E4A802113e266AdeBde", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8a40c222996f9F3431f63Bf80244C36822060f12/logo.png", + "symbol": "FXF", + "name": "Finxflo", + "contractAddress": "0x8a40c222996f9F3431f63Bf80244C36822060f12", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x853d955aCEf822Db058eb8505911ED77F175b99e/logo.png", + "symbol": "FRAX", + "name": "Frax", + "contractAddress": "0x853d955aCEf822Db058eb8505911ED77F175b99e", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 9, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x29502fE4d233EF0b45C3647101Fa1252cE0634BD/logo.png", + "symbol": "FROGE", + "name": "Froge Finance", + "contractAddress": "0x29502fE4d233EF0b45C3647101Fa1252cE0634BD", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b/logo.png", + "symbol": "FUN", + "name": "FunFair", + "contractAddress": "0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8c15Ef5b4B21951d50E53E4fbdA8298FFAD25057/logo.png", + "symbol": "FX", + "name": "Function X", + "contractAddress": "0x8c15Ef5b4B21951d50E53E4fbdA8298FFAD25057", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6BFf2fE249601ed0Db3a87424a2E923118BB0312/logo.png", + "symbol": "FYZ", + "name": "Fyooz", + "contractAddress": "0x6BFf2fE249601ed0Db3a87424a2E923118BB0312", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8a854288a5976036A725879164Ca3e91d30c6A1B/logo.png", + "symbol": "GET", + "name": "GET", + "contractAddress": "0x8a854288a5976036A725879164Ca3e91d30c6A1B", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 9, + "symbol": "GNX", + "name": "Genaro Network", + "contractAddress": "0x6ec8a24cabdc339a06a172f8223ea557055adaa5", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9F9c8ec3534c3cE16F928381372BfbFBFb9F4D24/logo.png", + "symbol": "GLQ", + "name": "GraphLinq", + "contractAddress": "0x9F9c8ec3534c3cE16F928381372BfbFBFb9F4D24", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6810e776880C02933D47DB1b9fc05908e5386b96/logo.png", + "symbol": "GNO", + "name": "Gnosis Token", + "contractAddress": "0x6810e776880C02933D47DB1b9fc05908e5386b96", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "GNT", + "name": "Golem", + "contractAddress": "0xa74476443119A942dE498590Fe1f2454d7D4aC0d", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xc3771d47E2Ab5A519E2917E61e23078d0C05Ed7f/logo.png", + "symbol": "GTH", + "name": "Gather", + "contractAddress": "0xc3771d47E2Ab5A519E2917E61e23078d0C05Ed7f", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 2, + "symbol": "GUSD", + "name": "Gemini Dollar", + "contractAddress": "0x056fd409e1d7a124bd7017459dfea2f387b6d5cd", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "GVT", + "name": "Genesis Vision", + "contractAddress": "0x103c3A209da59d3E7C4A89307e66521e081CFDF0", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xbEa98c05eEAe2f3bC8c3565Db7551Eb738c8CCAb/logo.png", + "symbol": "GYSR", + "name": "Geyser", + "contractAddress": "0xbEa98c05eEAe2f3bC8c3565Db7551Eb738c8CCAb", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 0, + "symbol": "GD", + "name": "Zilla GD", + "contractAddress": "0xb4cebdb66b3a5183fd4764a25cad1fc109535016", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x09e64c2B61a5f1690Ee6fbeD9baf5D6990F8dFd0/logo.png", + "symbol": "GRO", + "name": "Growth", + "contractAddress": "0x09e64c2B61a5f1690Ee6fbeD9baf5D6990F8dFd0", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xc944E90C64B2c07662A292be6244BDf05Cda44a7/logo.png", + "symbol": "GRT", + "name": "Graph Token", + "contractAddress": "0xc944E90C64B2c07662A292be6244BDf05Cda44a7", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 3, + "symbol": "GUP", + "name": "Matchpool", + "contractAddress": "0xf7B098298f7C69Fc14610bf71d5e02c60792894C", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39/logo.png", + "symbol": "HEX", + "name": "HEX", + "contractAddress": "0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "HPB", + "name": "HPBCoin", + "contractAddress": "0x38c6a68304cdefb9bec48bbfaaba5c5b47818bb2", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x584bC13c7D411c00c01A62e8019472dE68768430/logo.png", + "symbol": "HEGIC", + "name": "Hegic", + "contractAddress": "0x584bC13c7D411c00c01A62e8019472dE68768430", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xEEF9f339514298C6A857EfCfC1A762aF84438dEE/logo.png", + "symbol": "HEZ", + "name": "Hermez Network Token", + "contractAddress": "0xEEF9f339514298C6A857EfCfC1A762aF84438dEE", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6c6EE5e31d828De241282B9606C8e98Ea48526E2/logo.png", + "symbol": "HOT", + "name": "HoloToken", + "contractAddress": "0x6c6EE5e31d828De241282B9606C8e98Ea48526E2", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "HYDRO", + "name": "Hydro", + "contractAddress": "0xebbdf302c940c6bfd49c6b165f457fdb324649bc", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "ICX", + "name": "ICON", + "contractAddress": "0xb5a5f22694352c15b00323844ad545abb2b11028", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "ICN", + "name": "ICONOMI", + "contractAddress": "0x888666CA69E0f178DED6D75b5726Cee99A87D698", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xEBd9D99A3982d547C5Bb4DB7E3b1F9F14b67Eb83/logo.png", + "symbol": "ID", + "name": "Everest ID", + "contractAddress": "0xEBd9D99A3982d547C5Bb4DB7E3b1F9F14b67Eb83", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x72630B1e3B42874bf335020Ba0249e3E9e47Bafc/logo.png", + "symbol": "EPAN", + "name": "Paypolitan Token", + "contractAddress": "0x72630B1e3B42874bf335020Ba0249e3E9e47Bafc", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "symbol": "GBX", + "name": "Globitex", + "contractAddress": "0x12fcd6463e66974cf7bbc24ffc4d40d6be458283", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xf6537FE0df7F0Cc0985Cf00792CC98249E73EFa0/logo.png", + "symbol": "GIV", + "name": "GIVToken", + "contractAddress": "0xf6537FE0df7F0Cc0985Cf00792CC98249E73EFa0", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xeEAA40B28A2d1b0B08f6f97bB1DD4B75316c6107/logo.png", + "symbol": "GOVI", + "name": "GOVI", + "contractAddress": "0xeEAA40B28A2d1b0B08f6f97bB1DD4B75316c6107", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 5, + "symbol": "GTO", + "name": "Gifto", + "contractAddress": "0xc5bbae50781be1669306b9e001eff57a2957b09d", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "IOST", + "name": "IOStoken", + "contractAddress": "0xfa1a856cfa3409cfa145fa4e20eb270df3eb21ab", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "ITC", + "name": "IOT Chain", + "contractAddress": "0x5e6b6d9abad9093fdc861ea1600eba1b355cd940", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xe28b3B32B6c345A34Ff64674606124Dd5Aceca30/logo.png", + "symbol": "INJ", + "name": "Injective Token", + "contractAddress": "0xe28b3B32B6c345A34Ff64674606124Dd5Aceca30", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4B1E80cAC91e2216EEb63e29B957eB91Ae9C2Be8/logo.png", + "symbol": "JUP", + "name": "Jupiter", + "contractAddress": "0x4B1E80cAC91e2216EEb63e29B957eB91Ae9C2Be8", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x85Eee30c52B0b379b046Fb0F85F4f3Dc3009aFEC/logo.png", + "symbol": "KEEP", + "name": "KEEP Token", + "contractAddress": "0x85Eee30c52B0b379b046Fb0F85F4f3Dc3009aFEC", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 6, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x16980b3B4a3f9D89E33311B5aa8f80303E5ca4F8/logo.png", + "symbol": "KEX", + "name": "KIRA Network", + "contractAddress": "0x16980b3B4a3f9D89E33311B5aa8f80303E5ca4F8", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "KIN", + "name": "Kin", + "contractAddress": "0x818Fc6C2Ec5986bc6E2CBf00939d90556aB12ce5", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xCbfef8fdd706cde6F208460f2Bf39Aa9c785F05D/logo.png", + "symbol": "KINE", + "name": "Kine Governance Token", + "contractAddress": "0xCbfef8fdd706cde6F208460f2Bf39Aa9c785F05D", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1cEB5cB57C4D4E2b2433641b95Dd330A33185A44/logo.png", + "symbol": "KP3R", + "name": "Keep3rV1", + "contractAddress": "0x1cEB5cB57C4D4E2b2433641b95Dd330A33185A44", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1dD80016e3d4ae146Ee2EBB484e8edD92dacC4ce/logo.png", + "symbol": "LEAD", + "name": "Lead Token", + "contractAddress": "0x1dD80016e3d4ae146Ee2EBB484e8edD92dacC4ce", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0fF6ffcFDa92c53F615a4A75D982f399C989366b/logo.png", + "symbol": "LAYER", + "name": "Unilayer", + "contractAddress": "0x0fF6ffcFDa92c53F615a4A75D982f399C989366b", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xBBbbCA6A901c926F240b89EacB641d8Aec7AEafD/logo.png", + "symbol": "LRC", + "name": "LoopringCoin V2", + "contractAddress": "0xBBbbCA6A901c926F240b89EacB641d8Aec7AEafD", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x3593D125a4f7849a1B059E64F4517A86Dd60c95d/logo.png", + "symbol": "OM", + "name": "MANTRA DAO", + "contractAddress": "0x3593D125a4f7849a1B059E64F4517A86Dd60c95d", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "MAN", + "name": "MATRIX AI Network", + "contractAddress": "0xe25bcec5d3801ce3a794079bf94adf1b8ccd802d", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "symbol": "MYST", + "name": "Mysterium", + "contractAddress": "0xa645264C5603E96c3b0B078cdab68733794B0A71", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "NULS", + "name": "Nuls", + "contractAddress": "0xb91318f35bdb262e9423bc7c7c2a3a93dd93c92c", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 9, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xfAd45E47083e4607302aa43c65fB3106F1cd7607/logo.png", + "symbol": "HOGE", + "name": "hoge.finance", + "contractAddress": "0xfAd45E47083e4607302aa43c65fB3106F1cd7607", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "symbol": "IXT", + "name": "iXledger", + "contractAddress": "0xfca47962d45adfdfd1ab2d972315db4ce7ccf094", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB9d99C33eA2d86EC5eC6b8A4dD816EBBA64404AF/logo.png", + "symbol": "K21", + "name": "k21.kanon.art", + "contractAddress": "0xB9d99C33eA2d86EC5eC6b8A4dD816EBBA64404AF", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0F5D2fB29fb7d3CFeE444a200298f468908cC942/logo.png", + "symbol": "MANA", + "name": "Decentraland MANA", + "contractAddress": "0x0F5D2fB29fb7d3CFeE444a200298f468908cC942", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1494CA1F11D487c2bBe4543E90080AeBa4BA3C2b/logo.png", + "symbol": "DPI", + "name": "DefiPulse Index", + "contractAddress": "0x1494CA1F11D487c2bBe4543E90080AeBa4BA3C2b", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "symbol": "ENG", + "name": "Enigma", + "contractAddress": "0xf0ee6b27b759c9893ce4f094b49ad28fd15a23e4", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 16, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xD6c67B93a7b248dF608a653d82a100556144c5DA/logo.png", + "symbol": "EXNT", + "name": "ExNetwork Community Token", + "contractAddress": "0xD6c67B93a7b248dF608a653d82a100556144c5DA", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xfffffffFf15AbF397dA76f1dcc1A1604F45126DB/logo.png", + "symbol": "FSW", + "name": "FalconSwap Token", + "contractAddress": "0xfffffffFf15AbF397dA76f1dcc1A1604F45126DB", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xF4d861575ecC9493420A3f5a14F85B13f0b50EB3/logo.png", + "symbol": "FCL", + "name": "Fractal Protocol Token", + "contractAddress": "0xF4d861575ecC9493420A3f5a14F85B13f0b50EB3", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x956F47F50A910163D8BF957Cf5846D573E7f87CA/logo.png", + "symbol": "FEI", + "name": "Fei USD", + "contractAddress": "0x956F47F50A910163D8BF957Cf5846D573E7f87CA", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "FSN", + "name": "Fusion", + "contractAddress": "0xd0352a019e9ab9d757776f532377aaebd36fd541", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xdd974D5C2e2928deA5F71b9825b8b646686BD200/logo.png", + "symbol": "KNC", + "name": "Kyber Network Crystal", + "contractAddress": "0xdd974D5C2e2928deA5F71b9825b8b646686BD200", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x67B6D479c7bB412C54e03dCA8E1Bc6740ce6b99C/logo.png", + "symbol": "KYL", + "name": "Kylin Network", + "contractAddress": "0x67B6D479c7bB412C54e03dCA8E1Bc6740ce6b99C", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xaE697F994Fc5eBC000F8e22EbFfeE04612f98A0d/logo.png", + "symbol": "LGCY", + "name": "LGCY Network", + "contractAddress": "0xaE697F994Fc5eBC000F8e22EbFfeE04612f98A0d", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0a50C93c762fDD6E56D86215C24AaAD43aB629aa/logo.png", + "symbol": "LGO", + "name": "LGO Token", + "contractAddress": "0x0a50C93c762fDD6E56D86215C24AaAD43aB629aa", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6149C26Cd2f7b5CCdb32029aF817123F6E37Df5B/logo.png", + "symbol": "LPOOL", + "name": "Launchpool token", + "contractAddress": "0x6149C26Cd2f7b5CCdb32029aF817123F6E37Df5B", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x80fB784B7eD66730e8b1DBd9820aFD29931aab03/logo.png", + "symbol": "LEND", + "name": "Lend", + "contractAddress": "0x80fB784B7eD66730e8b1DBd9820aFD29931aab03", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0000000000095413afC295d19EDeb1Ad7B71c952/logo.png", + "symbol": "LON", + "name": "Tokenlon", + "contractAddress": "0x0000000000095413afC295d19EDeb1Ad7B71c952", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4de2573e27E648607B50e1Cfff921A33E4A34405/logo.png", + "symbol": "LST", + "name": "Lendroid Support Token", + "contractAddress": "0x4de2573e27E648607B50e1Cfff921A33E4A34405", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "LOOM", + "name": "Loom", + "contractAddress": "0xa4e8c3ec456107ea67d3075bf9e3df3a75823db0", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x3DB6Ba6ab6F95efed1a6E794caD492fAAabF294D/logo.png", + "symbol": "LTO", + "name": "LTO Network Token", + "contractAddress": "0x3DB6Ba6ab6F95efed1a6E794caD492fAAabF294D", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA8b919680258d369114910511cc87595aec0be6D/logo.png", + "symbol": "LYXe", + "name": "LUKSO Token", + "contractAddress": "0xA8b919680258d369114910511cc87595aec0be6D", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa393473d64d2F9F026B60b6Df7859A689715d092/logo.png", + "symbol": "LTX", + "name": "Lattice Token", + "contractAddress": "0xa393473d64d2F9F026B60b6Df7859A689715d092", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "LUN", + "name": "Lunyr", + "contractAddress": "0xfa05A73FfE78ef8f1a739473e462c54bae6567D9", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xd2877702675e6cEb975b4A1dFf9fb7BAF4C91ea9/logo.png", + "symbol": "LUNA", + "name": "Wrapped LUNA Token", + "contractAddress": "0xd2877702675e6cEb975b4A1dFf9fb7BAF4C91ea9", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5f98805A4E8be255a32880FDeC7F6728C6568bA0/logo.png", + "symbol": "LUSD", + "name": "LUSD Stablecoin", + "contractAddress": "0x5f98805A4E8be255a32880FDeC7F6728C6568bA0", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB4d930279552397bbA2ee473229f89Ec245bc365/logo.png", + "symbol": "MAHA", + "name": "MahaDAO", + "contractAddress": "0xB4d930279552397bbA2ee473229f89Ec245bc365", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xfC98e825A2264D890F9a1e68ed50E1526abCcacD/logo.png", + "symbol": "MCO2", + "name": "Moss Carbon Credit", + "contractAddress": "0xfC98e825A2264D890F9a1e68ed50E1526abCcacD", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xAd4f86a25bbc20FfB751f2FAC312A0B4d8F88c64/logo.png", + "symbol": "ROOM", + "name": "OptionRoom Token", + "contractAddress": "0xAd4f86a25bbc20FfB751f2FAC312A0B4d8F88c64", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5a666c7d92E5fA7Edcb6390E4efD6d0CDd69cF37/logo.png", + "symbol": "MARSH", + "name": "UnmarshalToken", + "contractAddress": "0x5a666c7d92E5fA7Edcb6390E4efD6d0CDd69cF37", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x69af81e73A73B40adF4f3d4223Cd9b1ECE623074/logo.png", + "symbol": "MASK", + "name": "Mask Network", + "contractAddress": "0x69af81e73A73B40adF4f3d4223Cd9b1ECE623074", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4e352cF164E64ADCBad318C3a1e222E9EBa4Ce42/logo.png", + "symbol": "MCB", + "name": "MCDEX Token", + "contractAddress": "0x4e352cF164E64ADCBad318C3a1e222E9EBa4Ce42", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0/logo.png", + "symbol": "MATIC", + "name": "Matic Token", + "contractAddress": "0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xD5525D397898e5502075Ea5E830d8914f6F0affe/logo.png", + "symbol": "MEME", + "name": "MEME", + "contractAddress": "0xD5525D397898e5502075Ea5E830d8914f6F0affe", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2/logo.png", + "symbol": "MKR", + "name": "MakerDAO", + "contractAddress": "0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa3BeD4E1c75D00fa6f4E5E6922DB7261B5E9AcD2/logo.png", + "symbol": "MTA", + "name": "Meta", + "contractAddress": "0xa3BeD4E1c75D00fa6f4E5E6922DB7261B5E9AcD2", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "symbol": "MTL", + "name": "Metal", + "contractAddress": "0xF433089366899D83a9f26A773D59ec7eCF30355e", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x72e364F2ABdC788b7E918bc238B21f109Cd634D7/logo.png", + "symbol": "MVI", + "name": "Metaverse Index", + "contractAddress": "0x72e364F2ABdC788b7E918bc238B21f109Cd634D7", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB26631c6dda06aD89B93C71400D25692de89c068/logo.png", + "symbol": "MINDS", + "name": "Minds", + "contractAddress": "0xB26631c6dda06aD89B93C71400D25692de89c068", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xEA1ea0972fa092dd463f2968F9bB51Cc4c981D71/logo.png", + "symbol": "MOD", + "name": "Modefi", + "contractAddress": "0xEA1ea0972fa092dd463f2968F9bB51Cc4c981D71", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "symbol": "MCO", + "name": "Monaco", + "contractAddress": "0xB63B606Ac810a52cCa15e44bB630fd42D8d1d83d", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "NAS", + "name": "Nebulas", + "contractAddress": "0x5d65D971895Edc438f465c17DB6992698a52318D", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xaAAf91D9b90dF800Df4F55c205fd6989c977E73a/logo.png", + "symbol": "TKN", + "name": "Monolith TKN", + "contractAddress": "0xaAAf91D9b90dF800Df4F55c205fd6989c977E73a", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa8c8CfB141A3bB59FEA1E2ea6B79b5ECBCD7b6ca/logo.png", + "symbol": "NOIA", + "name": "NOIA Token", + "contractAddress": "0xa8c8CfB141A3bB59FEA1E2ea6B79b5ECBCD7b6ca", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB62132e35a6c13ee1EE0f84dC5d40bad8d815206/logo.png", + "symbol": "NEXO", + "name": "Nexo", + "contractAddress": "0xB62132e35a6c13ee1EE0f84dC5d40bad8d815206", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1cBb83EbcD552D5EBf8131eF8c9CD9d9BAB342bC/logo.png", + "symbol": "NFY", + "name": "Non-Fungible Yearn", + "contractAddress": "0x1cBb83EbcD552D5EBf8131eF8c9CD9d9BAB342bC", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "NMR", + "name": "Numeraire", + "contractAddress": "0x1776e1F26f98b1A5dF9cD347953a26dd3Cb46671", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x967da4048cD07aB37855c090aAF366e4ce1b9F48/logo.png", + "symbol": "OCEAN", + "name": "Ocean Protocol", + "contractAddress": "0x967da4048cD07aB37855c090aAF366e4ce1b9F48", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets//logo.png", + "symbol": "OMG", + "name": "OMG Network", + "contractAddress": "0xd26114cd6EE289AccF82350c8d8487fedB8A0C07", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4c11249814f11b9346808179Cf06e71ac328c1b5/logo.png", + "symbol": "ORAI", + "name": "Oraichain Token", + "contractAddress": "0x4c11249814f11b9346808179Cf06e71ac328c1b5", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8207c1FfC5B6804F6024322CcF34F29c3541Ae26/logo.png", + "symbol": "OGN", + "name": "OriginToken", + "contractAddress": "0x8207c1FfC5B6804F6024322CcF34F29c3541Ae26", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0258F474786DdFd37ABCE6df6BBb1Dd5dfC4434a/logo.png", + "symbol": "ORN", + "name": "Orion Protocol", + "contractAddress": "0x0258F474786DdFd37ABCE6df6BBb1Dd5dfC4434a", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "OST", + "name": "OST", + "contractAddress": "0x2C4e8f2D746113d0696cE89B35F0d8bF88E0AEcA", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x21BfBDa47A0B4B5b1248c767Ee49F7caA9B23697/logo.png", + "symbol": "OVR", + "name": "OVR", + "contractAddress": "0x21BfBDa47A0B4B5b1248c767Ee49F7caA9B23697", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4575f41308EC1483f3d399aa9a2826d74Da13Deb/logo.png", + "symbol": "OXT", + "name": "Orchid", + "contractAddress": "0x4575f41308EC1483f3d399aa9a2826d74Da13Deb", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1614F18Fc94f47967A3Fbe5FfcD46d4e7Da3D787/logo.png", + "symbol": "PAID", + "name": "PAID Network", + "contractAddress": "0x1614F18Fc94f47967A3Fbe5FfcD46d4e7Da3D787", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8c8687fC965593DFb2F0b4EAeFD55E9D8df348df/logo.png", + "symbol": "PAID", + "name": "PAID Network", + "contractAddress": "0x8c8687fC965593DFb2F0b4EAeFD55E9D8df348df", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x45804880De22913dAFE09f4980848ECE6EcbAf78/logo.png", + "symbol": "PAXG", + "name": "Paxos Gold", + "contractAddress": "0x45804880De22913dAFE09f4980848ECE6EcbAf78", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "PAX", + "name": "Paxos Standard", + "contractAddress": "0x8e870d67f660d95d5be530380d0ec0bd388289e1", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xbC396689893D065F41bc2C6EcbeE5e0085233447/logo.png", + "symbol": "PERP", + "name": "Perpetual", + "contractAddress": "0xbC396689893D065F41bc2C6EcbeE5e0085233447", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x429881672B9AE42b8EbA0E26cD9C73711b891Ca5/logo.png", + "symbol": "PICKLE", + "name": "PickleToken", + "contractAddress": "0x429881672B9AE42b8EbA0E26cD9C73711b891Ca5", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x93ED3FBe21207Ec2E8f2d3c3de6e058Cb73Bc04d/logo.png", + "symbol": "PNK", + "name": "Pinakion", + "contractAddress": "0x93ED3FBe21207Ec2E8f2d3c3de6e058Cb73Bc04d", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "symbol": "POE", + "name": "Po.et", + "contractAddress": "0x0e0989b1f9b8a38983c2ba8053269ca62ec9b195", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "PAL", + "name": "PolicyPal Network", + "contractAddress": "0xfeDAE5642668f8636A11987Ff386bfd215F942EE", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8B39B70E39Aa811b69365398e0aACe9bee238AEb/logo.png", + "symbol": "PKF", + "name": "PolkaFoundry", + "contractAddress": "0x8B39B70E39Aa811b69365398e0aACe9bee238AEb", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xD478161C952357F05f0292B56012Cd8457F1cfbF/logo.png", + "symbol": "POLK", + "name": "Polkamarkets", + "contractAddress": "0xD478161C952357F05f0292B56012Cd8457F1cfbF", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x83e6f1E41cdd28eAcEB20Cb649155049Fac3D5Aa/logo.png", + "symbol": "POLS", + "name": "PolkastarterToken", + "contractAddress": "0x83e6f1E41cdd28eAcEB20Cb649155049Fac3D5Aa", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "PRL", + "name": "Oyster Pearl", + "contractAddress": "0x1844b21593262668b7248d0f57a220caaba46ab9", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x362bc847A3a9637d3af6624EeC853618a43ed7D2/logo.png", + "symbol": "PRQ", + "name": "Parsiq Token", + "contractAddress": "0x362bc847A3a9637d3af6624EeC853618a43ed7D2", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "symbol": "PTOY", + "name": "Patientory", + "contractAddress": "0x8Ae4BF2C33a8e667de34B54938B0ccD03Eb8CC06", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1796ae0b0fa4862485106a0de9b654eFE301D0b2/logo.png", + "symbol": "PMON", + "name": "Polkamon", + "contractAddress": "0x1796ae0b0fa4862485106a0de9b654eFE301D0b2", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "POLY", + "name": "Polymath", + "contractAddress": "0x9992eC3cF6A55b00978cdDF2b27BC6882d88D1eC", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "symbol": "PPT", + "name": "Populous", + "contractAddress": "0xd4fa1460F537bb9085d22C7bcCB5DD450Ef28e3a", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 6, + "symbol": "POWR", + "name": "Power Ledger", + "contractAddress": "0x595832f8fc6bf59c85c527fec3740a1b7a361269", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC57d533c50bC22247d49a368880fb49a1caA39F7/logo.png", + "symbol": "PTF", + "name": "PowerTrade Fuel Token", + "contractAddress": "0xC57d533c50bC22247d49a368880fb49a1caA39F7", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0CDF9acd87E940837ff21BB40c9fd55F68bba059/logo.png", + "symbol": "MINT", + "name": "Public Mint", + "contractAddress": "0x0CDF9acd87E940837ff21BB40c9fd55F68bba059", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 6, + "symbol": "QASH", + "name": "QASH", + "contractAddress": "0x618e75ac90b12c6049ba3b27f5d5f8651b0037f6", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4a220E6096B25EADb88358cb44068A3248254675/logo.png", + "symbol": "QNT", + "name": "Quant", + "contractAddress": "0x4a220E6096B25EADb88358cb44068A3248254675", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6e0daDE58D2d89eBBe7aFc384e3E4f15b70b14D8/logo.png", + "symbol": "QRX", + "name": "QuiverX", + "contractAddress": "0x6e0daDE58D2d89eBBe7aFc384e3E4f15b70b14D8", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x33D0568941C0C64ff7e0FB4fbA0B11BD37deEd9f/logo.png", + "symbol": "RAMP", + "name": "RAMP DEFI", + "contractAddress": "0x33D0568941C0C64ff7e0FB4fbA0B11BD37deEd9f", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 6, + "symbol": "RC1U03", + "name": "RentalCoin 1.0", + "contractAddress": "0x7384db1aaa06364d1e6e67f5434391d3eea2a038", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "RDN", + "name": "Raiden Network", + "contractAddress": "0x255aa6df07540cb5d3d297f0d0d4d84cb52bc8e6", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xf1f955016EcbCd7321c7266BccFB96c68ea5E49b/logo.png", + "symbol": "RLY", + "name": "Rally", + "contractAddress": "0xf1f955016EcbCd7321c7266BccFB96c68ea5E49b", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA4EED63db85311E22dF4473f87CcfC3DaDCFA3E3/logo.png", + "symbol": "RBC", + "name": "Rubic", + "contractAddress": "0xA4EED63db85311E22dF4473f87CcfC3DaDCFA3E3", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8f8221aFbB33998d8584A2B05749bA73c37a938a/logo.png", + "symbol": "REQ", + "name": "Request Token", + "contractAddress": "0x8f8221aFbB33998d8584A2B05749bA73c37a938a", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8762db106B2c2A0bccB3A80d1Ed41273552616E8/logo.png", + "symbol": "RSR", + "name": "Reserve Rights", + "contractAddress": "0x8762db106B2c2A0bccB3A80d1Ed41273552616E8", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 9, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7dE91B204C1C737bcEe6F000AAA6569Cf7061cb7/logo.png", + "symbol": "XRT", + "name": "Robonomics", + "contractAddress": "0x7dE91B204C1C737bcEe6F000AAA6569Cf7061cb7", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xCb5f72d37685C3D5aD0bB5F982443BC8FcdF570E/logo.png", + "symbol": "ROOT", + "name": "RootKit", + "contractAddress": "0xCb5f72d37685C3D5aD0bB5F982443BC8FcdF570E", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x3155BA85D5F96b2d030a4966AF206230e46849cb/logo.png", + "symbol": "RUNE", + "name": "THORChain ETH.RUNE", + "contractAddress": "0x3155BA85D5F96b2d030a4966AF206230e46849cb", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "SAN", + "name": "SAN", + "contractAddress": "0x7C5A0CE9267ED19B22F8cae653F198e3E8daf098", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE/logo.png", + "symbol": "SHIB", + "name": "SHIBA INU", + "contractAddress": "0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x72e9D9038cE484EE986FEa183f8d8Df93f9aDA13/logo.png", + "symbol": "SMARTCREDIT", + "name": "SMARTCREDIT Token", + "contractAddress": "0x72e9D9038cE484EE986FEa183f8d8Df93f9aDA13", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "SRN", + "name": "SIRIN", + "contractAddress": "0x68d57c9a1C35f63E2c83eE8e49A64e9d70528D25", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "SNM", + "name": "SONM", + "contractAddress": "0x983F6d60db79ea8cA4eB9968C6aFf8cfA04B3c63", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets//logo.png", + "symbol": "SNX", + "name": "Synthetix Network Token", + "contractAddress": "0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x23B608675a2B2fB1890d3ABBd85c5775c51691d5/logo.png", + "symbol": "SOCKS", + "name": "Unisocks Edition 0", + "contractAddress": "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0Ae055097C6d159879521C384F1D2123D1f195e6/logo.png", + "symbol": "STAKE", + "name": "xDai", + "contractAddress": "0x0Ae055097C6d159879521C384F1D2123D1f195e6", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "SUB", + "name": "Substratum", + "contractAddress": "0x8D75959f1E61EC2571aa72798237101F084DE63a", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xEa319e87Cf06203DAe107Dd8E5672175e3Ee976c/logo.png", + "symbol": "SURF", + "name": "SURF.Finance", + "contractAddress": "0xEa319e87Cf06203DAe107Dd8E5672175e3Ee976c", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xCC4304A31d09258b0029eA7FE63d032f52e44EFe/logo.png", + "symbol": "SWAP", + "name": "TrustSwap Token", + "contractAddress": "0xCC4304A31d09258b0029eA7FE63d032f52e44EFe", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB6ff96B8A8d214544Ca0dBc9B33f7AD6503eFD32/logo.png", + "symbol": "SYNC", + "name": "SYNC", + "contractAddress": "0xB6ff96B8A8d214544Ca0dBc9B33f7AD6503eFD32", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB987D48Ed8f2C468D52D6405624EADBa5e76d723/logo.png", + "symbol": "STBZ", + "name": "Stabilize Token", + "contractAddress": "0xB987D48Ed8f2C468D52D6405624EADBa5e76d723", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "SNT", + "name": "Status", + "contractAddress": "0x744d70FDBE2Ba4CF95131626614a1763DF805B9E", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "SEED", + "name": "Superbloom Network", + "contractAddress": "0x4e7bd88e3996f48e2a24d15e37ca4c02b4d134d2", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1C9922314ED1415c95b9FD453c3818fd41867d0B/logo.png", + "symbol": "TOWER", + "name": "TOWER", + "contractAddress": "0x1C9922314ED1415c95b9FD453c3818fd41867d0B", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xd084B83C305daFD76AE3E1b4E1F1fe2eCcCb3988/logo.png", + "symbol": "TVK", + "name": "Terra Virtua Kolect", + "contractAddress": "0xd084B83C305daFD76AE3E1b4E1F1fe2eCcCb3988", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xaA7a9CA87d3694B5755f213B5D04094b8d0F0A6F/logo.png", + "symbol": "TRAC", + "name": "Trace Token", + "contractAddress": "0xaA7a9CA87d3694B5755f213B5D04094b8d0F0A6F", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6F87D756DAf0503d08Eb8993686c7Fc01Dc44fB1/logo.png", + "symbol": "TRADE", + "name": "UniTrade", + "contractAddress": "0x6F87D756DAf0503d08Eb8993686c7Fc01Dc44fB1", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xc7283b66Eb1EB5FB86327f08e1B5816b0720212B/logo.png", + "symbol": "TRIBE", + "name": "Tribe", + "contractAddress": "0xc7283b66Eb1EB5FB86327f08e1B5816b0720212B", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0000000000085d4780B73119b644AE5ecd22b376/logo.png", + "symbol": "TUSD", + "name": "TrueUSD", + "contractAddress": "0x0000000000085d4780B73119b644AE5ecd22b376", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4C19596f5aAfF459fA38B0f7eD92F11AE6543784/logo.png", + "symbol": "TRU", + "name": "TrustToken", + "contractAddress": "0x4C19596f5aAfF459fA38B0f7eD92F11AE6543784", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8400D94A5cb0fa0D041a3788e395285d61c9ee5e/logo.png", + "symbol": "UBT", + "name": "Unibright", + "contractAddress": "0x8400D94A5cb0fa0D041a3788e395285d61c9ee5e", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "symbol": "UCASH", + "name": "UCASH", + "contractAddress": "0x92e52a1a235d9a103d970901066ce910aacefd37", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x04Fa0d235C4abf4BcF4787aF4CF447DE572eF828/logo.png", + "symbol": "UMA", + "name": "UMA Voting Token v1", + "contractAddress": "0x04Fa0d235C4abf4BcF4787aF4CF447DE572eF828", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6fC13EACE26590B80cCCAB1ba5d51890577D83B2/logo.png", + "symbol": "UMB", + "name": "Umbrella", + "contractAddress": "0x6fC13EACE26590B80cCCAB1ba5d51890577D83B2", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9Ed8e7C9604790F7Ec589F99b94361d8AAB64E5E/logo.png", + "symbol": "UNISTAKE", + "name": "Unistake", + "contractAddress": "0x9Ed8e7C9604790F7Ec589F99b94361d8AAB64E5E", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x10Be9a8dAe441d276a5027936c3aADEd2d82bC15/logo.png", + "symbol": "UMX", + "name": "https://unimex.network/", + "contractAddress": "0x10Be9a8dAe441d276a5027936c3aADEd2d82bC15", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x226f7b842E0F0120b7E194D05432b3fd14773a9D/logo.png", + "symbol": "UNN", + "name": "UNION Protocol Governance Token", + "contractAddress": "0x226f7b842E0F0120b7E194D05432b3fd14773a9D", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "VIB", + "name": "Viberate", + "contractAddress": "0x2C974B2d0BA1716E644c1FC59982a89DDD2fF724", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x3D3D35bb9bEC23b06Ca00fe472b50E7A4c692C30/logo.png", + "symbol": "VIDYA", + "name": "Vidya", + "contractAddress": "0x3D3D35bb9bEC23b06Ca00fe472b50E7A4c692C30", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xF938424F7210f31dF2Aee3011291b658f872e91e/logo.png", + "symbol": "VISR", + "name": "VISOR", + "contractAddress": "0xF938424F7210f31dF2Aee3011291b658f872e91e", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "symbol": "WAX", + "name": "WAX", + "contractAddress": "0x39Bb259F66E1C59d5ABEF88375979b4D20D98022", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7a2Bc711E19ba6aff6cE8246C546E8c4B4944DFD/logo.png", + "symbol": "WAXE", + "name": "WAX Economic Token", + "contractAddress": "0x7a2Bc711E19ba6aff6cE8246C546E8c4B4944DFD", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "WINGS", + "name": "Wings", + "contractAddress": "0x667088b212ce3d06a1b553a7221E1fD19000d9aF", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x66a0f676479Cee1d7373f3DC2e2952778BfF5bd6/logo.png", + "symbol": "WISE", + "name": "Wise Token", + "contractAddress": "0x66a0f676479Cee1d7373f3DC2e2952778BfF5bd6", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xBF494F02EE3FdE1F20BEE6242bCe2d1ED0c15e47/logo.png", + "symbol": "WORLD", + "name": "World Token", + "contractAddress": "0xBF494F02EE3FdE1F20BEE6242bCe2d1ED0c15e47", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 6, + "symbol": "WRC", + "name": "Worldcore", + "contractAddress": "0x72adadb447784dd7ab1f472467750fc485e4cb2d", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599/logo.png", + "symbol": "WBTC", + "name": "Wrapped BTC", + "contractAddress": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", + "symbol": "WETH", + "name": "Wrapped Ethereum", + "contractAddress": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x09a3EcAFa817268f77BE1283176B946C4ff2E608/logo.png", + "symbol": "MIR", + "name": "Wrapped MIR Token", + "contractAddress": "0x09a3EcAFa817268f77BE1283176B946C4ff2E608", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8888801aF4d980682e47f1A9036e589479e835C5/logo.png", + "symbol": "MPH", + "name": "88mph.app", + "contractAddress": "0x8888801aF4d980682e47f1A9036e589479e835C5", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9469D013805bFfB7D3DEBe5E7839237e535ec483/logo.png", + "symbol": "RING", + "name": "Evolution Land Global Token", + "contractAddress": "0x9469D013805bFfB7D3DEBe5E7839237e535ec483", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 0, + "symbol": "XJP", + "name": "Digital JPY", + "contractAddress": "0x39689fE671C01fcE173395f6BC45D4C332026666", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xF063fE1aB7a291c5d06a86e14730b00BF24cB589/logo.png", + "symbol": "SALE", + "name": "DxSale.Network", + "contractAddress": "0xF063fE1aB7a291c5d06a86e14730b00BF24cB589", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "XUC", + "name": "Exchange Union", + "contractAddress": "0xc324a2f6b05880503444451b8b27e6f9e63287cb", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xee573a945B01B788B9287CE062A0CFC15bE9fd86/logo.png", + "symbol": "XED", + "name": "Exeedme", + "contractAddress": "0xee573a945B01B788B9287CE062A0CFC15bE9fd86", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x2e1E15C44Ffe4Df6a0cb7371CD00d5028e571d14/logo.png", + "symbol": "MTLX", + "name": "Mettalex", + "contractAddress": "0x2e1E15C44Ffe4Df6a0cb7371CD00d5028e571d14", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "NCASH", + "name": "Nucleus Vision", + "contractAddress": "0x809826cceab68c387726af962713b64cb5cb3cca", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA15C7Ebe1f07CaF6bFF097D8a589fb8AC49Ae5B3/logo.png", + "symbol": "NPXS", + "name": "Pundi X Token", + "contractAddress": "0xA15C7Ebe1f07CaF6bFF097D8a589fb8AC49Ae5B3", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4fE83213D56308330EC302a8BD641f1d0113A4Cc/logo.png", + "symbol": "NU", + "name": "NuCypher", + "contractAddress": "0x4fE83213D56308330EC302a8BD641f1d0113A4Cc", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "symbol": "QRL", + "name": "QRL", + "contractAddress": "0x697beac28B09E122C4332D163985e8a73121b97F", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "PAY", + "name": "TenX", + "contractAddress": "0xB97048628DB6B661D4C2aA833e95Dbe1A905B280", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xe3818504c1B32bF1557b16C238B2E01Fd3149C17/logo.png", + "symbol": "PLR", + "name": "PILLAR", + "contractAddress": "0xe3818504c1B32bF1557b16C238B2E01Fd3149C17", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "PMTN", + "name": "Peer Mountain", + "contractAddress": "0xe91df2bb9bccd18c45f01843cab88768d64f2d32", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5BEfBB272290dD5b8521D4a938f6c4757742c430/logo.png", + "symbol": "XFI", + "name": "Xfinance", + "contractAddress": "0x5BEfBB272290dD5b8521D4a938f6c4757742c430", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0f7F961648aE6Db43C75663aC7E5414Eb79b5704/logo.png", + "symbol": "XIO", + "name": "XIO Network", + "contractAddress": "0x0f7F961648aE6Db43C75663aC7E5414Eb79b5704", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 9, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x607F4C5BB672230e8672085532f7e901544a7375/logo.png", + "symbol": "RLC", + "name": "iExec RLC", + "contractAddress": "0x607F4C5BB672230e8672085532f7e901544a7375", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x89Ab32156e46F46D02ade3FEcbe5Fc4243B9AAeD/logo.png", + "symbol": "PNT", + "name": "pNetwork Token", + "contractAddress": "0x89Ab32156e46F46D02ade3FEcbe5Fc4243B9AAeD", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x69A95185ee2a045CDC4bCd1b1Df10710395e4e23/logo.png", + "symbol": "POOLZ", + "name": "$Poolz Finance", + "contractAddress": "0x69A95185ee2a045CDC4bCd1b1Df10710395e4e23", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + }, + "symbol": "QCX", + "name": "QuickX Protocol", + "contractAddress": "0xf9e5af7b42d31d51677c75bbbd37c1986ec79aee", + "customIcon": "qcx" + }, + { + "decimalCount": 18, + "symbol": "QSP", + "name": "Quantstamp", + "contractAddress": "0x99ea4dB9EE77ACD40B119BD1dC4E33e1C070b80d", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x03ab458634910AaD20eF5f1C8ee96F1D6ac54919/logo.png", + "symbol": "RAI", + "name": "Rai Reflex Index", + "contractAddress": "0x03ab458634910AaD20eF5f1C8ee96F1D6ac54919", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xFca59Cd816aB1eaD66534D82bc21E7515cE441CF/logo.png", + "symbol": "RARI", + "name": "Rarible", + "contractAddress": "0xFca59Cd816aB1eaD66534D82bc21E7515cE441CF", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x50DE6856358Cc35f3A9a57eAAA34BD4cB707d2cd/logo.png", + "symbol": "RAZOR", + "name": "RAZOR", + "contractAddress": "0x50DE6856358Cc35f3A9a57eAAA34BD4cB707d2cd", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "RCN", + "name": "Ripio Credit", + "contractAddress": "0xf970b8e36e23f7fc3fd752eea86f8be8d83375a6", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xFE3E6a25e6b192A42a44ecDDCd13796471735ACf/logo.png", + "symbol": "REEF", + "name": "Reef.finance", + "contractAddress": "0xFE3E6a25e6b192A42a44ecDDCd13796471735ACf", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x408e41876cCCDC0F92210600ef50372656052a38/logo.png", + "symbol": "REN", + "name": "Republic Token", + "contractAddress": "0x408e41876cCCDC0F92210600ef50372656052a38", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1985365e9f78359a9B6AD760e32412f4a445E862/logo.png", + "symbol": "REP", + "name": "Reputation", + "contractAddress": "0x1985365e9f78359a9B6AD760e32412f4a445E862", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x557B933a7C2c45672B610F8954A3deB39a51A8Ca/logo.png", + "symbol": "REVV", + "name": "REVV", + "contractAddress": "0x557B933a7C2c45672B610F8954A3deB39a51A8Ca", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa1d6Df714F91DeBF4e0802A542E13067f31b8262/logo.png", + "symbol": "RFOX", + "name": "RFOX", + "contractAddress": "0xa1d6Df714F91DeBF4e0802A542E13067f31b8262", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "RUFF", + "name": "RUFF", + "contractAddress": "0xf278c1ca969095ffddded020290cf8b5c424ace2", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xf21661D0D1d76d3ECb8e1B9F1c923DBfffAe4097/logo.png", + "symbol": "RIO", + "name": "Realio Network", + "contractAddress": "0xf21661D0D1d76d3ECb8e1B9F1c923DBfffAe4097", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "RCC", + "name": "Reality Clash", + "contractAddress": "0x9b6443b0fb9c241a7fdac375595cea13e6b7807a", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x221657776846890989a759BA2973e427DfF5C9bB/logo.png", + "symbol": "REPv2", + "name": "Reputation", + "contractAddress": "0x221657776846890989a759BA2973e427DfF5C9bB", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 0, + "symbol": "REV", + "name": "Revain", + "contractAddress": "0x48f775efbe4f5ece6e0df2f7b5932df56823b990", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xaf9f549774ecEDbD0966C52f250aCc548D3F36E5/logo.png", + "symbol": "RFuel", + "name": "Rio Fuel Token", + "contractAddress": "0xaf9f549774ecEDbD0966C52f250aCc548D3F36E5", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "RVT", + "name": "Rivetz", + "contractAddress": "0x3d1ba9be9f66b8ee101911bc36d3fb562eac2244", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB4EFd85c19999D84251304bDA99E90B92300Bd93/logo.png", + "symbol": "RPL", + "name": "Rocket Pool", + "contractAddress": "0xB4EFd85c19999D84251304bDA99E90B92300Bd93", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x3845badAde8e6dFF049820680d1F14bD3903a5d0/logo.png", + "symbol": "SAND", + "name": "SAND", + "contractAddress": "0x3845badAde8e6dFF049820680d1F14bD3903a5d0", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "symbol": "SENT", + "name": "SENTinel", + "contractAddress": "0xa44e5137293e855b1b7bc7e2c6f8cd796ffcb037", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xD23Ac27148aF6A2f339BD82D0e3CFF380b5093de/logo.png", + "symbol": "SI", + "name": "SIREN", + "contractAddress": "0xD23Ac27148aF6A2f339BD82D0e3CFF380b5093de", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "STK", + "name": "STK", + "contractAddress": "0xae73b38d1c9a8b274127ec30160a4927c4d71824", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "SAI", + "name": "Sai", + "contractAddress": "0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "symbol": "SALT", + "name": "SALT", + "contractAddress": "0x4156D3342D5c385a87D264F90653733592000581", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9B02dD390a603Add5c07f9fd9175b7DABE8D63B7/logo.png", + "symbol": "SPI", + "name": "Shopping.io", + "contractAddress": "0x9B02dD390a603Add5c07f9fd9175b7DABE8D63B7", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 0, + "symbol": "SNGLS", + "name": "Singular DTV", + "contractAddress": "0xaeC2E87E0A235266D9C5ADc9DEb4b2E29b54D009", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 0, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x37236CD05b34Cc79d3715AF2383E96dd7443dCF1/logo.png", + "symbol": "SLP", + "name": "Small Love Potion", + "contractAddress": "0x37236CD05b34Cc79d3715AF2383E96dd7443dCF1", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x06A01a4d579479Dd5D884EBf61A31727A3d8D442/logo.png", + "symbol": "Skey", + "name": "SmartKey", + "contractAddress": "0x06A01a4d579479Dd5D884EBf61A31727A3d8D442", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0488401c3F535193Fa8Df029d9fFe615A06E74E6/logo.png", + "symbol": "SRK", + "name": "SparkPoint", + "contractAddress": "0x0488401c3F535193Fa8Df029d9fFe615A06E74E6", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xb753428af26E81097e7fD17f40c88aaA3E04902c/logo.png", + "symbol": "SFI", + "name": "Spice", + "contractAddress": "0xb753428af26E81097e7fD17f40c88aaA3E04902c", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x00c83aeCC790e8a4453e5dD3B0B4b3680501a7A7/logo.png", + "symbol": "SKL", + "name": "SKALE", + "contractAddress": "0x00c83aeCC790e8a4453e5dD3B0B4b3680501a7A7", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x40FD72257597aA14C7231A7B1aaa29Fce868F677/logo.png", + "symbol": "XOR", + "name": "Sora", + "contractAddress": "0x40FD72257597aA14C7231A7B1aaa29Fce868F677", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "symbol": "STORJ", + "name": "Storj", + "contractAddress": "0xB64ef51C888972c908CFacf59B47C1AfBC0Ab8aC", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xbE9375C6a420D2eEB258962efB95551A5b722803/logo.png", + "symbol": "STMX", + "name": "StormX", + "contractAddress": "0xbE9375C6a420D2eEB258962efB95551A5b722803", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "STQ", + "name": "Storiqa", + "contractAddress": "0x5c3a228510d246b78a3765c20221cbf3082b44a4", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "STORM", + "name": "Storm", + "contractAddress": "0xD0a4b8946Cb52f0661273bfbC6fD0E0C75Fc6433", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x990f341946A3fdB507aE7e52d17851B87168017c/logo.png", + "symbol": "STRONG", + "name": "Strong", + "contractAddress": "0x990f341946A3fdB507aE7e52d17851B87168017c", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "STX", + "name": "Stox", + "contractAddress": "0x006BeA43Baa3f7A6f765F14f10A1a1b08334EF45", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "SNC", + "name": "SunContract", + "contractAddress": "0xF4134146AF2d511Dd5EA8cDB1C4AC88C57D60404", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xe53EC727dbDEB9E2d5456c3be40cFF031AB40A55/logo.png", + "symbol": "SUPER", + "name": "SuperFarm", + "contractAddress": "0xe53EC727dbDEB9E2d5456c3be40cFF031AB40A55", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6B3595068778DD592e39A122f4f5a5cF09C90fE2/logo.png", + "symbol": "SUSHI", + "name": "SushiSwap", + "contractAddress": "0x6B3595068778DD592e39A122f4f5a5cF09C90fE2", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8CE9137d39326AD0cD6491fb5CC0CbA0e089b6A9/logo.png", + "symbol": "SXP", + "name": "Swipe", + "contractAddress": "0x8CE9137d39326AD0cD6491fb5CC0CbA0e089b6A9", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9f7229aF0c4b9740e207Ea283b9094983f78ba04/logo.png", + "symbol": "TAD", + "name": "Tadpole", + "contractAddress": "0x9f7229aF0c4b9740e207Ea283b9094983f78ba04", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa1eD0364D53394209D61aE8bfdb8Ff50484D8c91/logo.png", + "symbol": "TBC", + "name": "TeraBlock Token", + "contractAddress": "0xa1eD0364D53394209D61aE8bfdb8Ff50484D8c91", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "THETA", + "name": "Theta Token", + "contractAddress": "0x3883f5e181fccaf8410fa61e12b59bad963fb645", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x77777FeDdddFfC19Ff86DB637967013e6C6A116C/logo.png", + "symbol": "TORN", + "name": "TornadoCash", + "contractAddress": "0x77777FeDdddFfC19Ff86DB637967013e6C6A116C", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 6, + "symbol": "TRX", + "name": "TRON", + "contractAddress": "0xf230b790e05390fc8295f4d3f60332c93bed42e2", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0202Be363B8a4820f3F4DE7FaF5224fF05943AB1/logo.png", + "symbol": "UFT", + "name": "UniLend Finance Token", + "contractAddress": "0x0202Be363B8a4820f3F4DE7FaF5224fF05943AB1", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 6, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png", + "symbol": "USDC", + "name": "USDC", + "contractAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "STU", + "name": "bitJob", + "contractAddress": "0x0371a82e4a9d0a4312f3ee2ac9c6958512891372", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xBa21Ef4c9f433Ede00badEFcC2754B8E74bd538A/logo.png", + "symbol": "SWFL", + "name": "Swapfolio", + "contractAddress": "0xBa21Ef4c9f433Ede00badEFcC2754B8E74bd538A", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8dAEBADE922dF735c38C80C7eBD708Af50815fAa/logo.png", + "symbol": "TBTC", + "name": "tBTC", + "contractAddress": "0x8dAEBADE922dF735c38C80C7eBD708Af50815fAa", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x57Ab1ec28D129707052df4dF418D58a2D46d5f51/logo.png", + "symbol": "sUSD", + "name": "Synth sUSD", + "contractAddress": "0x57Ab1ec28D129707052df4dF418D58a2D46d5f51", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 2, + "symbol": "TEL", + "name": "Telcoin", + "contractAddress": "0x467bccd9d29f223bce8043b84e8c8b282827790f", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xDcB01cc464238396E213a6fDd933E36796eAfF9f/logo.png", + "symbol": "YLD", + "name": "Yield", + "contractAddress": "0xDcB01cc464238396E213a6fDd933E36796eAfF9f", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7F3EDcdD180Dbe4819Bd98FeE8929b5cEdB3AdEB/logo.png", + "symbol": "XTK", + "name": "xToken", + "contractAddress": "0x7F3EDcdD180Dbe4819Bd98FeE8929b5cEdB3AdEB", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xAE1eaAE3F627AAca434127644371b67B18444051/logo.png", + "symbol": "YOP", + "name": "YOP", + "contractAddress": "0xAE1eaAE3F627AAca434127644371b67B18444051", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xf4CD3d3Fda8d7Fd6C5a500203e38640A70Bf9577/logo.png", + "symbol": "Yf-DAI", + "name": "YfDAI.finance", + "contractAddress": "0xf4CD3d3Fda8d7Fd6C5a500203e38640A70Bf9577", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x2e2f3246b6c65CCc4239c9Ee556EC143a7E5DE2c/logo.png", + "symbol": "YFIM", + "name": "yfi.mobi", + "contractAddress": "0x2e2f3246b6c65CCc4239c9Ee556EC143a7E5DE2c", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB1e9157c2Fdcc5a856C8DA8b2d89b6C32b3c1229/logo.png", + "symbol": "ZEFU", + "name": "Zenfuse Trading Platform Token", + "contractAddress": "0xB1e9157c2Fdcc5a856C8DA8b2d89b6C32b3c1229", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 12, + "symbol": "ZIL", + "name": "Zilliqa", + "contractAddress": "0x05f4a42e251f2d52b8ed15E9FEdAacFcEF1FAD27", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "ZLA", + "name": "Zilla", + "contractAddress": "0xfd8971d5e8e1740ce2d0a84095fca4de729d0c16", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xE95A203B1a91a908F9B9CE46459d101078c2c3cb/logo.png", + "symbol": "aEth", + "name": "aEthereum", + "contractAddress": "0xE95A203B1a91a908F9B9CE46459d101078c2c3cb", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7b123f53421b1bF8533339BFBdc7C98aA94163db/logo.png", + "symbol": "buidl", + "name": "dfohub", + "contractAddress": "0x7b123f53421b1bF8533339BFBdc7C98aA94163db", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5218E472cFCFE0b64A064F055B43b4cdC9EfD3A6/logo.png", + "symbol": "eRSDL", + "name": "UnFederalReserveToken", + "contractAddress": "0x5218E472cFCFE0b64A064F055B43b4cdC9EfD3A6", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984/logo.png", + "symbol": "UNI", + "name": "Uniswap", + "contractAddress": "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 6, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xdAC17F958D2ee523a2206206994597C13D831ec7/logo.png", + "symbol": "USDT", + "name": "Tether USD", + "contractAddress": "0xdAC17F958D2ee523a2206206994597C13D831ec7", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "VEE", + "name": "BLOCKv", + "contractAddress": "0x340d2bde5eb28c1eed91b2f790723e3b160613b7", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xF411903cbC70a74d22900a5DE66A2dda66507255/logo.png", + "symbol": "VRA", + "name": "VERA", + "contractAddress": "0xF411903cbC70a74d22900a5DE66A2dda66507255", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1b40183EFB4Dd766f11bDa7A7c3AD8982e998421/logo.png", + "symbol": "VSP", + "name": "VesperToken", + "contractAddress": "0x1b40183EFB4Dd766f11bDa7A7c3AD8982e998421", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "symbol": "VGX", + "name": "Voyager", + "contractAddress": "0x5af2be193a6abca9c8817001f45744777db30756", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 4, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9355372396e3F6daF13359B7b607a3374cc638e0/logo.png", + "symbol": "WHALE", + "name": "WHALE", + "contractAddress": "0x9355372396e3F6daF13359B7b607a3374cc638e0", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "WTC", + "name": "Waltonchain", + "contractAddress": "0xb7cb1c96db6b22b0d3d9536e0108d062bd488f74", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4691937a7508860F876c9c0a2a617E7d9E945D4B/logo.png", + "symbol": "WOO", + "name": "Wootrade Network", + "contractAddress": "0x4691937a7508860F876c9c0a2a617E7d9E945D4B", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "symbol": "WPR", + "name": "WePower", + "contractAddress": "0x4CF488387F035FF08c371515562CBa712f9015d4", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5228a22e72ccC52d415EcFd199F99D0665E7733b/logo.png", + "symbol": "pBTC", + "name": "pTokens BTC", + "contractAddress": "0x5228a22e72ccC52d415EcFd199F99D0665E7733b", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 8, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D/logo.png", + "symbol": "renBTC", + "name": "renBTC", + "contractAddress": "0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x3383c5a8969Dc413bfdDc9656Eb80A1408E4bA20/logo.png", + "symbol": "wANATHA", + "name": "Wrapped ANATHA", + "contractAddress": "0x3383c5a8969Dc413bfdDc9656Eb80A1408E4bA20", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa0afAA285Ce85974c3C881256cB7F225e3A1178a/logo.png", + "symbol": "wCRES", + "name": "Wrapped CRES", + "contractAddress": "0xa0afAA285Ce85974c3C881256cB7F225e3A1178a", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e/logo.png", + "symbol": "YFI", + "name": "Yearn.Finance", + "contractAddress": "0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xF94b5C5651c888d928439aB6514B93944eEE6F48/logo.png", + "symbol": "YLD", + "name": "Yield", + "contractAddress": "0xF94b5C5651c888d928439aB6514B93944eEE6F48", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 10, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xb9EF770B6A5e12E45983C5D80545258aA38F3B78/logo.png", + "symbol": "ZCN", + "name": "0chain", + "contractAddress": "0xb9EF770B6A5e12E45983C5D80545258aA38F3B78", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x2eDf094dB69d6Dcd487f1B3dB9febE2eeC0dd4c5/logo.png", + "symbol": "ZEE", + "name": "ZeroSwapToken", + "contractAddress": "0x2eDf094dB69d6Dcd487f1B3dB9febE2eeC0dd4c5", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xE41d2489571d322189246DaFA5ebDe1F4699F498/logo.png", + "symbol": "ZRX", + "name": "0x Protocol Token", + "contractAddress": "0xE41d2489571d322189246DaFA5ebDe1F4699F498", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 18, + "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1337DEF18C680aF1f9f45cBcab6309562975b1dD/logo.png", + "symbol": "arNXM", + "name": "Armor NXM", + "contractAddress": "0x1337DEF18C680aF1f9f45cBcab6309562975b1dD", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + }, + { + "decimalCount": 9, + "symbol": "ADT", + "name": "adToken", + "contractAddress": "0xD0D6D6C5Fe4a677D343cC433536BB717bAe167dD", + "blockchain": { + "key": "ethereum", + "curve": "secp256k1", + "testnet": false + } + } ] diff --git a/app/src/main/res/layout/item_popular_token.xml b/app/src/main/res/layout/item_popular_token.xml index 8d22ab57b4..9e988b2d7a 100644 --- a/app/src/main/res/layout/item_popular_token.xml +++ b/app/src/main/res/layout/item_popular_token.xml @@ -33,7 +33,7 @@ + + + \ No newline at end of file From 10bab3797333a77b5fc5edb96531e8e2c1043636 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 25 Jun 2021 14:33:24 +0300 Subject: [PATCH 004/189] Updated on 2026-08-14 --- .../main/assets/ethereum_tokens_testnet.json | 26 +++++++++++ .../tangem/tap/common/extensions/Picasso.kt | 15 ++++++- .../com/tangem/tap/domain/TapWalletManager.kt | 2 + .../com/tangem/tap/domain/TapWorkarounds.kt | 5 +++ .../warningMessage/WarningMessage.kt | 5 ++- .../warningMessage/WarningMessagesManager.kt | 12 +++++ .../tap/domain/extensions/Blockchain.kt | 14 ------ .../domain/extensions/WalletManagerFactory.kt | 33 ++++++++++---- .../tap/domain/tokens/CurrenciesRepository.kt | 27 +++-------- .../tap/domain/{ => topup}/TopUpHelper.kt | 19 ++++++-- .../tangem/tap/domain/topup/TopUpManager.kt | 45 +++++++++++++++++++ .../walletconnect/WalletConnectMiddleware.kt | 3 +- .../features/tokens/redux/TokensMiddleware.kt | 11 +++-- .../tap/features/wallet/redux/WalletAction.kt | 3 +- .../tap/features/wallet/redux/WalletState.kt | 7 ++- .../middlewares/MultiWalletMiddleware.kt | 23 ++++++---- .../redux/middlewares/TopUpMiddleware.kt | 31 ++++++++++--- .../redux/middlewares/WalletMiddleware.kt | 1 - .../redux/middlewares/WarningsMiddleware.kt | 6 +++ .../wallet/redux/reducers/WalletReducer.kt | 1 + .../wallet/ui/adapters/WalletAdapter.kt | 2 +- .../ui/adapters/WarningMessagesAdapter.kt | 2 +- app/src/main/res/values/colors.xml | 2 + .../main/res/values/strings_untranslated.xml | 3 ++ 24 files changed, 224 insertions(+), 74 deletions(-) create mode 100644 app/src/main/assets/ethereum_tokens_testnet.json rename app/src/main/java/com/tangem/tap/domain/{ => topup}/TopUpHelper.kt (75%) create mode 100644 app/src/main/java/com/tangem/tap/domain/topup/TopUpManager.kt diff --git a/app/src/main/assets/ethereum_tokens_testnet.json b/app/src/main/assets/ethereum_tokens_testnet.json new file mode 100644 index 0000000000..5222bbe37d --- /dev/null +++ b/app/src/main/assets/ethereum_tokens_testnet.json @@ -0,0 +1,26 @@ +[ + { + "decimalCount" : 18, + "symbol" : "WEENUS", + "name" : "Weenus", + "contractAddress" : "0xaFF4481D10270F50f203E0763e2597776068CBc5" + }, + { + "decimalCount" : 18, + "symbol" : "XEENUS", + "name" : "Xeenus", + "contractAddress" : "0x022E292b44B5a146F2e8ee36Ff44D3dd863C915c" + }, + { + "decimalCount" : 8, + "symbol" : "YEENUS", + "name" : "Yeenus", + "contractAddress" : "0xc6fDe3FD2Cc2b173aEC24cc3f267cb3Cd78a26B7" + }, + { + "decimalCount" : 0, + "symbol" : "ZEENUS", + "name" : "Zeenus", + "contractAddress" : "0x1f9061B953bBa0E36BF50F21876132DcF276fC6e" + } +] 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 e12711e3d0..d470ae2bf6 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,7 +1,9 @@ package com.tangem.tap.common.extensions +import android.graphics.PorterDuff import android.widget.ImageView import android.widget.TextView +import androidx.core.content.ContextCompat import com.squareup.picasso.Callback import com.squareup.picasso.Picasso import com.tangem.blockchain.common.Blockchain @@ -48,6 +50,7 @@ fun Picasso.loadCurrenciesIcon( imageView.colorFilter = null textView.text = null } + if (blockchain.isTestnet()) imageView.tint(R.color.tint) } }) } @@ -70,12 +73,20 @@ private fun setOfflineCurrencyImage( } else { setBlockchainImage(imageView, textView, blockchain) } + if (blockchain.isTestnet()) imageView.tint(R.color.tint) +} + +fun ImageView.tint(colorRes: Int) { +// val color = ContextCompat.getColor(context, colorRes); +// ImageViewCompat.setImageTintList(this, ColorStateList.valueOf(color)); + this.setColorFilter(ContextCompat.getColor(context, colorRes), PorterDuff.Mode.DARKEN); + } private fun setBlockchainImage( imageView: ImageView, textView: TextView, - blockchain: Blockchain + blockchain: Blockchain, ) { imageView.setImageResource(blockchain.getIconRes()) imageView.colorFilter = null @@ -85,7 +96,7 @@ private fun setBlockchainImage( private fun setTokenImage( imageView: ImageView, textView: TextView, - token: Token + token: Token, ) { imageView.setImageResource(R.drawable.shape_circle) imageView.setColorFilter(token.getColor()) 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 acdc924fd4..d0f72cd47b 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -10,6 +10,7 @@ import com.tangem.tap.common.redux.global.FiatCurrencyName import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.currenciesRepository import com.tangem.tap.domain.TapWorkarounds.isStart2Coin +import com.tangem.tap.domain.TapWorkarounds.isTestCard import com.tangem.tap.domain.configurable.config.ConfigManager import com.tangem.tap.domain.extensions.* import com.tangem.tap.domain.tasks.ScanNoteResponse @@ -96,6 +97,7 @@ class TapWalletManager { withContext(Dispatchers.Main) { store.dispatch(WalletAction.ResetState) store.dispatch(GlobalAction.SaveScanNoteResponse(data)) + store.dispatch(WalletAction.SetIfTestnetCard(data.card.isTestCard)) store.dispatch(WalletAction.MultiWallet.SetIsMultiwalletAllowed(data.card.isMultiwalletAllowed)) if (data.card.isTwinCard()) { val secondCardId = TwinsHelper.getTwinsCardId(data.card.cardId) diff --git a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt index 5807bd024e..0c1a379a3c 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt @@ -27,7 +27,12 @@ object TapWorkarounds { } + val Card.isTestCard: Boolean + get() = cardData?.batchId == TEST_CARD_BATCH && cardId.startsWith(TEST_CARD_ID_STARTS_WITH) + private const val START_2_COIN_ISSUER = "start2coin" + private const val TEST_CARD_BATCH = "99FF" + private const val TEST_CARD_ID_STARTS_WITH = "FF99" private val excludedBatches = listOf( "0027", diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessage.kt b/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessage.kt index 3c3ad5f5ce..500f3844b3 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessage.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessage.kt @@ -1,6 +1,5 @@ package com.tangem.tap.domain.configurable.warningMessage -import android.view.View import androidx.annotation.StringRes import com.squareup.moshi.Json import com.tangem.blockchain.common.Blockchain @@ -46,7 +45,9 @@ data class WarningMessage( @Json(name = "temporary") Temporary, // можно скрыть (кнопка ОК) - AppRating + AppRating, + + TestCard } diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt b/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt index 1c59ee5c55..0e48e115e1 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt @@ -156,6 +156,18 @@ class WarningMessagesManager( messageFormatArg = remainingSignatures.toString() ) + fun testCardWarning(): WarningMessage = WarningMessage( + "", + "", + type = WarningMessage.Type.TestCard, + priority = WarningMessage.Priority.Critical, + listOf(WarningMessage.Location.MainScreen), + null, + R.string.alert_title, + R.string.warning_testnet_card_message, + WarningMessage.Origin.Local + ) + const val REMAINING_SIGNATURES_WARNING = 10 } } \ No newline at end of file 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 a89f4fe24c..13946dd481 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 @@ -2,7 +2,6 @@ package com.tangem.tap.domain.extensions import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token -import com.tangem.commands.common.card.EllipticCurve import java.math.BigDecimal @@ -26,17 +25,4 @@ fun Blockchain.minimalAmount(): BigDecimal { return 1.toBigDecimal().movePointLeft(decimals()) } -fun Blockchain.getSupportedCurves(): List? { - return when (this) { - Blockchain.Unknown -> null - Blockchain.Bitcoin, Blockchain.BitcoinTestnet, Blockchain.BitcoinCash, Blockchain.Litecoin, - Blockchain.Ducatus, Blockchain.Ethereum, Blockchain.EthereumTestnet, Blockchain.RSK, - Blockchain.Binance, Blockchain.BinanceTestnet -> listOf(EllipticCurve.Secp256k1) - Blockchain.Tezos, Blockchain.XRP -> listOf(EllipticCurve.Secp256k1, EllipticCurve.Ed25519) - Blockchain.Cardano, Blockchain.CardanoShelley, Blockchain.Stellar -> - listOf(EllipticCurve.Ed25519) - else -> listOf(EllipticCurve.Secp256k1) - } -} - private const val NODL = "NODL" \ No newline at end of file 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 68a0ee5b84..88583ddf84 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 @@ -7,6 +7,7 @@ import com.tangem.commands.common.card.Card import com.tangem.commands.common.card.EllipticCurve import com.tangem.commands.wallet.CardWallet import com.tangem.common.extensions.hexToBytes +import com.tangem.tap.domain.TapWorkarounds.isTestCard import com.tangem.tap.domain.tasks.ScanNoteResponse import com.tangem.tap.domain.twins.isTwinCard @@ -19,28 +20,44 @@ fun WalletManagerFactory.makeWalletManagerForApp( val wallet = selectWallet(wallets) val publicKey = wallet?.publicKey ?: return null val curveToUse = wallet.curve ?: return null - return makeWalletManager(card.cardId, publicKey, blockchain, curveToUse) + return if (card.isTestCard) { + blockchain.getTestnetVersion()?.let { + makeWalletManager(card.cardId, publicKey, it, curveToUse) + } + } else { + makeWalletManager(card.cardId, publicKey, blockchain, curveToUse) + } } private fun selectWallet(wallets: List): CardWallet? { - return when (wallets.size) { - 0 -> null - 1 -> wallets[0] - else -> wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 } ?: wallets[0] - } + return when (wallets.size) { + 0 -> null + 1 -> wallets[0] + else -> wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 } ?: wallets[0] + } } fun WalletManagerFactory.makeWalletManagersForApp( card: Card, blockchains: List, ): List { - return blockchains.mapNotNull { blockchain -> makeWalletManagerForApp(card, blockchain) } + return if (card.isTestCard) { + blockchains.mapNotNull { blockchain -> + blockchain.getTestnetVersion()?.let { makeWalletManagerForApp(card, it) } + } + } else { + blockchains.mapNotNull { blockchain -> makeWalletManagerForApp(card, blockchain) } + } } fun WalletManagerFactory.makePrimaryWalletManager( data: ScanNoteResponse, ): WalletManager? { val card = data.card - val blockchain = card.getBlockchain() + val blockchain = if (card.isTestCard) { + card.getBlockchain()?.getTestnetVersion() + } else { + card.getBlockchain() + } val supportedCurves = blockchain?.getSupportedCurves() ?: return null val wallets = card.wallets.filter { wallet -> supportedCurves.contains(wallet.curve) } val wallet = selectWallet(wallets) 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 754d7ea239..af149d9af5 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 @@ -93,42 +93,29 @@ class CurrenciesRepository(val context: Application) { } } - fun getPopularTokens(): List { - val json = context.assets.readJsonFileToString(POPULAR_TOKENS_FILE_NAME) + fun getPopularTokens(isTestNet: Boolean = false): List { + val fileName = if (isTestNet) TESTNET_TOKENS_FILE_NAME else POPULAR_TOKENS_FILE_NAME + val json = context.assets.readJsonFileToString(fileName) return tokensAdapter.fromJson(json)!!.map { it.toToken() } } - fun getBlockchains(cardFirmware: FirmwareVersion?): List { + fun getBlockchains(cardFirmware: FirmwareVersion?, isTestNet: Boolean = false): List { return if (cardFirmware == null || cardFirmware.major < 4) { - secp256k1Blockchains + Blockchain.secp256k1Blockchains(isTestNet) } else { - secp256k1Blockchains + ed25519Blockchains + Blockchain.secp256k1Blockchains(isTestNet) + Blockchain.ed25519OnlyBlockchains(isTestNet) } } companion object { private const val POPULAR_TOKENS_FILE_NAME = "erc20_tokens" + private const val TESTNET_TOKENS_FILE_NAME = "ethereum_tokens_testnet" private const val FILE_NAME_PREFIX_TOKENS = "tokens" private const val FILE_NAME_PREFIX_BLOCKCHAINS = "blockchains" fun getFileNameForTokens(cardId: String): String = "${FILE_NAME_PREFIX_TOKENS}_$cardId" fun getFileNameForBlockchains(cardId: String): String = "${FILE_NAME_PREFIX_BLOCKCHAINS}_$cardId" - - private val secp256k1Blockchains = listOf( - Blockchain.Bitcoin, - Blockchain.BitcoinCash, - Blockchain.Binance, - Blockchain.BSC, - Blockchain.Litecoin, - Blockchain.XRP, - Blockchain.Tezos, - Blockchain.Ethereum, - Blockchain.RSK, - Blockchain.Polygon, - Blockchain.Dogecoin, - ) - private val ed25519Blockchains = listOf(Blockchain.CardanoShelley, Blockchain.Stellar) } } diff --git a/app/src/main/java/com/tangem/tap/domain/TopUpHelper.kt b/app/src/main/java/com/tangem/tap/domain/topup/TopUpHelper.kt similarity index 75% rename from app/src/main/java/com/tangem/tap/domain/TopUpHelper.kt rename to app/src/main/java/com/tangem/tap/domain/topup/TopUpHelper.kt index dfa301ab78..2ab6b843c8 100644 --- a/app/src/main/java/com/tangem/tap/domain/TopUpHelper.kt +++ b/app/src/main/java/com/tangem/tap/domain/topup/TopUpHelper.kt @@ -1,7 +1,8 @@ -package com.tangem.tap.domain +package com.tangem.tap.domain.topup import android.net.Uri import android.util.Base64 +import com.tangem.blockchain.common.Blockchain import com.tangem.tap.common.redux.global.CryptoCurrencyName import javax.crypto.Mac import javax.crypto.spec.SecretKeySpec @@ -15,10 +16,20 @@ class TopUpHelper { private const val API_KEY_PATH = "?apiKey=" private const val CURRENCY_PATH = "¤cyCode=" private const val WALLET_ADDRESS_PATH = "&walletAddress=" -// private const val REDIRECT_URL_PATH = "&redirectUrl=" + + // private const val REDIRECT_URL_PATH = "&redirectUrl=" private const val SIGNATURE_PATH = "&signature=" - fun getUrl(cryptoCurrencyName: CryptoCurrencyName, walletAddress: String, apiKey: String, secretKey: String): String { + fun getUrl( + blockchain: Blockchain?, + cryptoCurrencyName: CryptoCurrencyName, + walletAddress: String, + apiKey: String, + secretKey: String, + ): String { + if (blockchain?.isTestnet() == true) { + return blockchain.getTestnetTopUpUrl() ?: "" + } val originalQuery = API_KEY_PATH + apiKey.urlEncode() + CURRENCY_PATH + cryptoCurrencyName.urlEncode() + WALLET_ADDRESS_PATH + walletAddress.urlEncode() @@ -41,4 +52,4 @@ class TopUpHelper { return Base64.encodeToString(sha256encoded, Base64.NO_WRAP) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/tangem/tap/domain/topup/TopUpManager.kt b/app/src/main/java/com/tangem/tap/domain/topup/TopUpManager.kt new file mode 100644 index 0000000000..568c65f7d0 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/topup/TopUpManager.kt @@ -0,0 +1,45 @@ +package com.tangem.tap.domain.topup + +import com.tangem.Message +import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager +import com.tangem.blockchain.common.Amount +import com.tangem.blockchain.common.AmountType +import com.tangem.blockchain.common.Token +import com.tangem.blockchain.extensions.Result +import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.domain.TangemSigner +import com.tangem.tap.store +import com.tangem.tap.tangemSdk +import java.math.BigDecimal + +class TopUpManager { + suspend fun topUpTestErc20Tokens(walletManager: EthereumWalletManager, token: Token) { + walletManager.update() + + val amountToSend = Amount(walletManager.wallet.blockchain) + val destinationAddress = token.contractAddress + + val feeResult = walletManager.getFee(amountToSend, + destinationAddress) as? Result.Success ?: return + val fee = feeResult.data[0] + + if ((walletManager.wallet.amounts[AmountType.Coin]?.value ?: BigDecimal.ZERO) < fee.value) { + return + } + + val transaction = walletManager.createTransaction(amountToSend, fee, destinationAddress) + + val signer = TangemSigner( + tangemSdk = tangemSdk, Message() + ) { signResponse -> + store.dispatch( + GlobalAction.UpdateWalletSignedHashes( + walletSignedHashes = signResponse.walletSignedHashes, + remainingSignatures = signResponse.walletRemainingSignatures, + walletPublicKey = walletManager.wallet.publicKey + ) + ) + } + walletManager.send(transaction, signer) + } +} \ 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 89bec71f1f..a4b1faa96b 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.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction +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.walletconnect.WalletConnectManager @@ -153,7 +154,7 @@ class WalletConnectMiddleware { wcUri = wcUri, wallet = WalletForSession( card.cardId, key.toHexString(), - isTestNet = false + isTestNet = card.isTestCard ), )) } 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 cb13d64dd0..f1bcb72e51 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 @@ -2,6 +2,7 @@ package com.tangem.tap.features.tokens.redux import com.tangem.tap.common.redux.AppState import com.tangem.tap.currenciesRepository +import com.tangem.tap.domain.TapWorkarounds.isTestCard import com.tangem.tap.features.tokens.ui.adapters.CurrencyListItem import com.tangem.tap.store import org.rekotlin.Middleware @@ -13,9 +14,13 @@ class TokensMiddleware { { action -> when (action) { is TokensAction.LoadCurrencies -> { - val cardFirmware = state()?.globalState?.scanNoteResponse?.card?.firmwareVersion - val tokens = currenciesRepository.getPopularTokens() - val blockchains = currenciesRepository.getBlockchains(cardFirmware) + val card = state()?.globalState?.scanNoteResponse?.card + val isTestcard = card?.isTestCard ?: false + val tokens = currenciesRepository.getPopularTokens(isTestcard) + val blockchains = currenciesRepository.getBlockchains( + cardFirmware = card?.firmwareVersion, + isTestNet = isTestcard + ) val currencies = CurrencyListItem.createListOfCurrencies( blockchains, tokens ) 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 ebfadb5df3..43617cb446 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 @@ -6,7 +6,6 @@ import com.tangem.blockchain.common.address.AddressType import com.tangem.commands.common.card.Card import com.tangem.tap.common.redux.ErrorAction import com.tangem.tap.common.redux.NotificationAction -import com.tangem.tap.common.redux.global.CryptoCurrencyName import com.tangem.tap.domain.TapError import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.tokens.CardCurrencies @@ -19,6 +18,8 @@ sealed class WalletAction : Action { object ResetState : WalletAction() + data class SetIfTestnetCard(val isTestnet: Boolean) : WalletAction() + object LoadData : WalletAction() { data class Failure(val error: TapError) : WalletAction() } 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 35b301fd50..808201e795 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 @@ -32,7 +32,8 @@ data class WalletState( val cardCurrency: CryptoCurrencyName? = null, val selectedWallet: Currency? = null, val primaryBlockchain: Blockchain? = null, - val primaryToken: Token? = null + val primaryToken: Token? = null, + val isTestnet: Boolean = false, ) : StateType { val primaryWallet = if (wallets.isNotEmpty()) wallets[0] else null @@ -49,7 +50,9 @@ data class WalletState( fun getWalletManager(token: Token?): WalletManager? { if (token == null) return null - val ethereumWalletManager = walletManagers.find { it.wallet.blockchain == Blockchain.Ethereum } + val ethereumWalletManager = walletManagers + .find { it.wallet.blockchain == Blockchain.Ethereum || + it.wallet.blockchain == Blockchain.EthereumTestnet } return if (ethereumWalletManager?.presetTokens?.contains(token) == true) { ethereumWalletManager } else { 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 905ecce398..853b1fb45b 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 @@ -8,6 +8,7 @@ import com.tangem.tap.common.redux.global.GlobalState import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.currenciesRepository +import com.tangem.tap.domain.TapWorkarounds.isTestCard import com.tangem.tap.domain.extensions.makeWalletManagerForApp import com.tangem.tap.domain.extensions.makeWalletManagersForApp import com.tangem.tap.features.wallet.redux.Currency @@ -22,7 +23,7 @@ import kotlinx.coroutines.withContext class MultiWalletMiddleware { fun handle( - action: WalletAction.MultiWallet, walletState: WalletState?, globalState: GlobalState?, + action: WalletAction.MultiWallet, walletState: WalletState?, globalState: GlobalState?, ) { when (action) { is WalletAction.MultiWallet.AddWalletManagers -> { @@ -64,7 +65,10 @@ class MultiWalletMiddleware { val cardId = globalState?.scanNoteResponse?.card?.cardId when (val currency = action.walletData.currency) { is Currency.Blockchain -> { - cardId?.let { currenciesRepository.removeBlockchain(it, currency.blockchain) } + cardId?.let { + currenciesRepository.removeBlockchain(it, + currency.blockchain) + } } is Currency.Token -> { walletState?.getWalletManager(currency.token) @@ -76,8 +80,9 @@ class MultiWalletMiddleware { is WalletAction.MultiWallet.FindBlockchainsInUse -> { val cardFirmware = globalState?.scanNoteResponse?.card?.firmwareVersion val blockchains = currenciesRepository.getBlockchains(cardFirmware) - .filterNot { walletState?.blockchains?.contains(it) == true } - val walletManagers = action.factory.makeWalletManagersForApp(action.card, blockchains) + .filterNot { walletState?.blockchains?.contains(it) == true } + val walletManagers = + action.factory.makeWalletManagersForApp(action.card, blockchains) scope.launch { walletManagers.map { walletManager -> @@ -107,10 +112,10 @@ class MultiWalletMiddleware { is WalletAction.MultiWallet.FindTokensInUse -> { val card = globalState?.scanNoteResponse?.card ?: return val walletManager = walletState?.getWalletManager(Blockchain.Ethereum) - ?: globalState.tapWalletManager.walletManagerFactory.makeWalletManagerForApp( - card = card, - blockchain = Blockchain.Ethereum - ) + ?: globalState.tapWalletManager.walletManagerFactory.makeWalletManagerForApp( + card = card, + blockchain = Blockchain.Ethereum + ) val tokenFinder = walletManager as TokenFinder scope.launch { val result = tokenFinder.findTokens() @@ -184,7 +189,7 @@ class MultiWalletMiddleware { val walletManager = walletState?.getWalletManager(token) ?: globalState.tapWalletManager.walletManagerFactory.makeWalletManagerForApp( card = card, - blockchain = Blockchain.Ethereum + blockchain = if (card.isTestCard) Blockchain.EthereumTestnet else Blockchain.Ethereum )?.also { walletManager -> store.dispatch(WalletAction.MultiWallet.AddWalletManagers(walletManager)) store.dispatch(WalletAction.MultiWallet.AddBlockchain(walletManager.wallet.blockchain)) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TopUpMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TopUpMiddleware.kt index e0706570f7..98b63904ba 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TopUpMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TopUpMiddleware.kt @@ -2,9 +2,15 @@ package com.tangem.tap.features.wallet.redux.middlewares import android.net.Uri import androidx.browser.customtabs.CustomTabsIntent -import com.tangem.tap.domain.TopUpHelper +import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager +import com.tangem.tap.domain.topup.TopUpHelper +import com.tangem.tap.domain.topup.TopUpManager +import com.tangem.tap.features.wallet.redux.Currency import com.tangem.tap.features.wallet.redux.WalletAction +import com.tangem.tap.scope import com.tangem.tap.store +import kotlinx.coroutines.launch + class TopUpMiddleware { fun handle(action: WalletAction.TopUpAction) { @@ -16,16 +22,31 @@ class TopUpMiddleware { if (addresses.list.isEmpty()) return val defaultAddress = addresses.list[0].address - val url = TopUpHelper.getUrl( + val currency = selectedWalletData.currency + + if (currency is Currency.Token && currency.blockchain.isTestnet()) { + val walletManager = store.state.walletState.getWalletManager(currency.token) + if (walletManager !is EthereumWalletManager) return + + scope.launch { + TopUpManager().topUpTestErc20Tokens( + walletManager = walletManager, token = currency.token + ) + } + } else { + val url = TopUpHelper.getUrl( + currency?.blockchain, selectedWalletData.currencyData.currencySymbol!!, defaultAddress, config.moonPayApiKey, config.moonPayApiSecretKey - ) - val customTabsIntent = CustomTabsIntent.Builder() + ) + + val customTabsIntent = CustomTabsIntent.Builder() .setToolbarColor(action.toolbarColor) .build() - customTabsIntent.launchUrl(action.context, Uri.parse(url)); + customTabsIntent.launchUrl(action.context, Uri.parse(url)) + } } } } 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 183d55ddda..81feb660aa 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 @@ -43,7 +43,6 @@ class WalletMiddleware { is WalletAction.Warnings -> warningsMiddleware.handle(action, globalState) is WalletAction.MultiWallet -> multiWalletMiddleware.handle(action, walletState, globalState) - is WalletAction.LoadWallet -> { scope.launch { if (action.blockchain == null) { 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 0ff512dc45..32694c8282 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 @@ -12,6 +12,7 @@ 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 import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager import com.tangem.tap.domain.extensions.getSingleWallet @@ -84,6 +85,11 @@ class WarningsMiddleware { private fun showCardWarningsIfNeeded(globalState: GlobalState?) { globalState?.scanNoteResponse?.card?.let { card -> globalState.warningManager?.removeWarnings(WarningMessage.Origin.Local) + if (card.isTestCard) { + addWarningMessage(WarningMessagesManager.testCardWarning(), autoUpdate = true) + return@let + } + showWarningLowRemainingSignaturesIfNeeded(card) if (card.getType() != CardType.Release) { addWarningMessage(WarningMessagesManager.devCardWarning()) 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 5daef27db2..ddb3276dea 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 @@ -39,6 +39,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState { is WalletAction.MultiWallet -> newState = multiWalletReducer.reduce(action, newState) is WalletAction.ResetState -> newState = WalletState() + is WalletAction.SetIfTestnetCard -> newState = newState.copy(isTestnet = action.isTestnet) is WalletAction.EmptyWallet -> { val creatingWalletAllowed = !(newState.twinCardsState != null && newState.twinCardsState?.isCreatingTwinCardsAllowed != true) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt index 62e9849d04..b90adc2cb6 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt @@ -98,7 +98,7 @@ class WalletAdapter Picasso.get().loadCurrenciesIcon( imageView = view.iv_currency, textView = view.tv_token_letter, - token = token, blockchain = blockchain + token = token, blockchain = blockchain, ) when (wallet.currencyData.status) { 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 c262bbb35c..2b793466c3 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 @@ -73,7 +73,7 @@ class WarningMessageVH(val view: View) : RecyclerView.ViewHolder(view) { } private fun setupControlButtons(warning: WarningMessage) = when (warning.type) { - WarningMessage.Type.Permanent -> { + WarningMessage.Type.Permanent, WarningMessage.Type.TestCard -> { view.group_controls_temporary.hide() view.group_controls_rating.hide() } diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index fc5a1fd7b2..de5579c26b 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -43,6 +43,8 @@ #CBE4FF #E0E6FA + #55000000 + #1C1C1E #FFB71B #CA0F03 diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml index d6c4426536..f3a5887ec0 100644 --- a/app/src/main/res/values/strings_untranslated.xml +++ b/app/src/main/res/values/strings_untranslated.xml @@ -156,4 +156,7 @@ this wallet. This card can’t be used to establish WalletConnect session The operation couldn\'t be completed The operation couldn\'t be completed. \n\nYou have already established a WalletConnect session with this parameters. + + This is a Testnet card. Don\'t accept it as a payment. This card must only be used for testing and development purposes. + From 21e8d45e178dfbaa44086c8196a46c3567087035 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 25 Jun 2021 14:34:57 +0300 Subject: [PATCH 005/189] Updated on 2026-08-14 --- app/src/main/java/com/tangem/tap/common/extensions/Specific.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt b/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt index a792b8b25e..ab289dc6b6 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt @@ -37,7 +37,7 @@ fun BigDecimal.toFiatString(rateValue: BigDecimal, fiatCurrencyName: FiatCurrenc fun BigDecimal.toFiatValue(rateValue: BigDecimal): BigDecimal { val fiatValue = rateValue.multiply(this) - return fiatValue.setScale(2, RoundingMode.DOWN) + return fiatValue.setScale(2, RoundingMode.HALF_UP) } fun BigDecimal.toFormattedFiatValue(fiatCurrencyName: FiatCurrencyName): String { From 6b4c5381fc7d6bdb4e0c2693261789afd314c6ff Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 25 Jun 2021 14:46:32 +0300 Subject: [PATCH 006/189] Updated on 2026-08-14 --- .../wallet/ui/WalletDetailsFragment.kt | 8 ++++++ .../res/layout/fragment_wallet_details.xml | 25 +++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) 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 16f8f2b17b..63a7a78812 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 @@ -99,6 +99,14 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS val selectedWallet = state.getSelectedWalletData() ?: return tv_currency_title.text = selectedWallet.currencyData.currency + val currency = selectedWallet.currency + if (currency is Currency.Token) { + tv_currency_subtitle.text = currency.blockchain.tokenDisplayName() + tv_currency_subtitle.show() + } else { + tv_currency_subtitle.hide() + } + showPendingTransactionsIfPresent(selectedWallet.pendingTransactions) setupAddressCard(selectedWallet) diff --git a/app/src/main/res/layout/fragment_wallet_details.xml b/app/src/main/res/layout/fragment_wallet_details.xml index 757eeab445..8270cd53ac 100644 --- a/app/src/main/res/layout/fragment_wallet_details.xml +++ b/app/src/main/res/layout/fragment_wallet_details.xml @@ -52,7 +52,6 @@ android:layout_height="wrap_content" android:paddingStart="16dp" android:paddingEnd="16dp" - android:paddingBottom="16dp" android:textColor="@color/darkGray6" android:textSize="32sp" android:textStyle="bold" @@ -60,6 +59,26 @@ app:layout_constraintTop_toTopOf="parent" tools:text="Bitcoin" /> + + + + + + android:layout_marginTop="16dp" + app:layout_constraintTop_toBottomOf="@id/barrier_currency_title" /> From a248994ef032f1e21fee78c4b0862eec2e9ca0e6 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sat, 26 Jun 2021 23:11:17 +0300 Subject: [PATCH 007/189] Updated on 2026-08-14 --- .../tap/common/analytics/AnalyticsEvent.kt | 4 ++ .../analytics/FirebaseAnalyticsHandler.kt | 67 +++++++++++++++++-- .../walletconnect/WalletConnectManager.kt | 29 ++++++-- 3 files changed, 92 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEvent.kt b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEvent.kt index 4395bd0922..8ddf2f5ad9 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEvent.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEvent.kt @@ -8,4 +8,8 @@ enum class AnalyticsEvent(val event: String) { APP_RATING_DISMISS("dismiss_rate_app_warning"), APP_RATING_NEGATIVE("negative_rate_app_feedback"), APP_RATING_POSITIVE("positive_rate_app_feedback"), + WC_SUCCESS_RESPONSE("wallet_connect_success_response"), + WC_INVALID_REQUEST("wallet_connect_invalid_request"), + WC_NEW_SESSION("wallet_connect_new_session"), + WC_SESSION_DISCONNECTED("wallet_connect_session_disconnected"), } \ 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 5a91215e83..f9aea4c495 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 @@ -38,8 +38,8 @@ object FirebaseAnalyticsHandler : AnalyticsHandler { params[AnalyticsParam.ERROR_DESCRIPTION] = error.javaClass.simpleName params[AnalyticsParam.ERROR_KEY] = "TangemSdkError" - params.forEach { (key, value) -> - FirebaseCrashlytics.getInstance().setCustomKey(key.param, value) + params.forEach { + FirebaseCrashlytics.getInstance().setCustomKey(it.key.param, it.value) } val cardError = TangemSdk.map(error) FirebaseCrashlytics.getInstance().recordException(cardError) @@ -55,12 +55,55 @@ object FirebaseAnalyticsHandler : AnalyticsHandler { private fun setData(card: Card?, blockchain: Blockchain?): Bundle { if (card == null) return bundleOf() return bundleOf( - AnalyticsParam.BLOCKCHAIN.param to (blockchain?.currency ?: card.cardData?.blockchainName), + AnalyticsParam.BLOCKCHAIN.param to (blockchain?.currency + ?: card.cardData?.blockchainName), AnalyticsParam.BATCH_ID.param to card.cardData?.batchId, AnalyticsParam.FIRMWARE.param to card.firmwareVersion.version ) } + 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"), @@ -70,6 +113,9 @@ object FirebaseAnalyticsHandler : AnalyticsHandler { 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) { @@ -83,4 +129,17 @@ object FirebaseAnalyticsHandler : AnalyticsHandler { PurgeWallet("purge_wallet"), WriteIssuerData("write_issuer_data"), } -} \ No newline at end of file + + 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/domain/walletconnect/WalletConnectManager.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt index 44eece7399..9e11e4920d 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 @@ -3,6 +3,7 @@ package com.tangem.tap.domain.walletconnect import com.tangem.blockchain.common.Blockchain import com.tangem.common.extensions.guard import com.tangem.common.extensions.hexToBytes +import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.features.details.redux.walletconnect.* @@ -16,10 +17,7 @@ import com.trustwallet.walletconnect.models.ethereum.WCEthereumTransaction import com.trustwallet.walletconnect.models.session.WCSession import com.trustwallet.walletconnect.models.session.WCSessionUpdate import kotlinx.coroutines.launch -import okhttp3.Interceptor -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.Response +import okhttp3.* import okhttp3.logging.HttpLoggingInterceptor import timber.log.Timber import java.util.* @@ -136,6 +134,10 @@ class WalletConnectManager { } private fun onSessionClosed(session: WCSession) { + FirebaseAnalyticsHandler.logWcEvent( + FirebaseAnalyticsHandler.WcAnalyticsEvent.Session( + FirebaseAnalyticsHandler.WcSessionEvent.Disconnect, sessions[session]?.peerMeta?.url) + ) sessions.remove(session) walletConnectRepository.removeSession(session) store.dispatchOnMain(WalletConnectAction.RemoveSession(session)) @@ -235,6 +237,10 @@ class WalletConnectManager { store.dispatchOnMain(WalletConnectAction.AcceptOpeningSession( sessionData)) } + FirebaseAnalyticsHandler.logWcEvent( + FirebaseAnalyticsHandler.WcAnalyticsEvent.Session( + FirebaseAnalyticsHandler.WcSessionEvent.Connect, peer.url) + ) } } client.onSessionUpdate = { id: Long, update: WCSessionUpdate -> @@ -244,6 +250,11 @@ class WalletConnectManager { } client.onEthSendTransaction = { id: Long, transaction: WCEthereumTransaction -> Timber.d("onEthSendTransaction: $transaction") + FirebaseAnalyticsHandler.logWcEvent( + FirebaseAnalyticsHandler.WcAnalyticsEvent.Action( + FirebaseAnalyticsHandler.WcAction.SendTransaction + ) + ) sessions[client.session]?.toWalletConnectSession()?.let { sessionData -> store.dispatchOnMain(WalletConnectAction.HandleTransactionRequest( transaction = transaction, @@ -256,6 +267,11 @@ class WalletConnectManager { } client.onEthSignTransaction = { id: Long, transaction: WCEthereumTransaction -> Timber.d("onEthSignTransaction: $transaction") + FirebaseAnalyticsHandler.logWcEvent( + FirebaseAnalyticsHandler.WcAnalyticsEvent.Action( + FirebaseAnalyticsHandler.WcAction.SignTransaction + ) + ) sessions[client.session]?.toWalletConnectSession()?.let { sessionData -> store.dispatchOnMain(WalletConnectAction.HandleTransactionRequest( transaction = transaction, @@ -267,6 +283,11 @@ class WalletConnectManager { } client.onEthSign = { id: Long, message: WCEthereumSignMessage -> Timber.d("onEthSign: $message") + FirebaseAnalyticsHandler.logWcEvent( + FirebaseAnalyticsHandler.WcAnalyticsEvent.Action( + FirebaseAnalyticsHandler.WcAction.PersonalSign + ) + ) sessions[client.session]?.toWalletConnectSession()?.let { sessionData -> store.dispatchOnMain(WalletConnectAction.HandlePersonalSignRequest( message, From a4f979ca5f358d7e5c2f26dc03b1fb213c359eff Mon Sep 17 00:00:00 2001 From: Tangem Date: Sat, 26 Jun 2021 23:13:02 +0300 Subject: [PATCH 008/189] 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 e188911f47..65d2e04b9a 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-26' + implementation 'com.tangem:blockchain:BSK-84_testnets-27' implementation 'com.tangem:core:fixes_for_app_release-44' implementation 'com.tangem:sdk:fixes_for_app_release-44' From baa5a0cb9c144fabf383cf7779978f2ce6fe48c8 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 1 Jul 2021 23:20:08 +0300 Subject: [PATCH 009/189] Updated on 2026-08-14 --- .../configurable/config/ConfigManager.kt | 67 ++++++++++--------- 1 file changed, 34 insertions(+), 33 deletions(-) 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 58096022fd..5822a252bc 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 @@ -8,18 +8,18 @@ import com.tangem.tap.domain.configurable.Loader [REDACTED_AUTHOR] */ data class Config( - val coinMarketCapKey: String = "f6622117-c043-47a0-8975-9d673ce484de", - val moonPayApiKey: String = "pk_test_kc90oYTANy7UQdBavDKGfL4K9l6VEPE", - val moonPayApiSecretKey: String = "sk_test_V8w4M19LbDjjYOt170s0tGuvXAgyEb1C", - val blockchainSdkConfig: BlockchainSdkConfig = BlockchainSdkConfig(), - val isSendingToPayIdEnabled: Boolean = true, - val isTopUpEnabled: Boolean = false, - val isCreatingTwinCardsAllowed: Boolean = false + val coinMarketCapKey: String = "f6622117-c043-47a0-8975-9d673ce484de", + val moonPayApiKey: String = "pk_test_kc90oYTANy7UQdBavDKGfL4K9l6VEPE", + val moonPayApiSecretKey: String = "sk_test_V8w4M19LbDjjYOt170s0tGuvXAgyEb1C", + val blockchainSdkConfig: BlockchainSdkConfig = BlockchainSdkConfig(), + val isSendingToPayIdEnabled: Boolean = true, + val isTopUpEnabled: Boolean = false, + val isCreatingTwinCardsAllowed: Boolean = false ) class ConfigManager( - private val localLoader: Loader, - private val remoteLoader: Loader + private val localLoader: Loader, + private val remoteLoader: Loader ) { var config: Config = Config() @@ -49,10 +49,11 @@ class ConfigManager( fun resetToDefault(name: String) { when (name) { - isSendingToPayIdEnabled -> config = config.copy(isSendingToPayIdEnabled = defaultConfig.isSendingToPayIdEnabled) + isSendingToPayIdEnabled -> config = + config.copy(isSendingToPayIdEnabled = defaultConfig.isSendingToPayIdEnabled) isTopUpEnabled -> config = config.copy(isTopUpEnabled = defaultConfig.isTopUpEnabled) isCreatingTwinCardsAllowed -> config = - config.copy(isCreatingTwinCardsAllowed = defaultConfig.isCreatingTwinCardsAllowed) + config.copy(isCreatingTwinCardsAllowed = defaultConfig.isCreatingTwinCardsAllowed) } } @@ -60,38 +61,38 @@ class ConfigManager( val model = featureModel ?: return config = config.copy( - isTopUpEnabled = model.isTopUpEnabled, - isSendingToPayIdEnabled = model.isSendingToPayIdEnabled, - isCreatingTwinCardsAllowed = model.isCreatingTwinCardsAllowed + isTopUpEnabled = model.isTopUpEnabled, + isSendingToPayIdEnabled = model.isSendingToPayIdEnabled, + isCreatingTwinCardsAllowed = model.isCreatingTwinCardsAllowed ) defaultConfig = defaultConfig.copy( - isTopUpEnabled = model.isTopUpEnabled, - isSendingToPayIdEnabled = model.isSendingToPayIdEnabled, - isCreatingTwinCardsAllowed = model.isCreatingTwinCardsAllowed + isTopUpEnabled = model.isTopUpEnabled, + isSendingToPayIdEnabled = model.isSendingToPayIdEnabled, + isCreatingTwinCardsAllowed = model.isCreatingTwinCardsAllowed ) } private fun setupKey(configValues: ConfigValueModel?) { val values = configValues ?: return config = config.copy( - coinMarketCapKey = values.coinMarketCapKey, - moonPayApiKey = values.moonPayApiKey, - moonPayApiSecretKey = values.moonPayApiSecretKey, - blockchainSdkConfig = BlockchainSdkConfig( - blockchairApiKey = values.blockchairApiKey, - blockcypherTokens = values.blockcypherTokens, - infuraProjectId = values.infuraProjectId - ) + coinMarketCapKey = values.coinMarketCapKey, + moonPayApiKey = values.moonPayApiKey, + moonPayApiSecretKey = values.moonPayApiSecretKey, + blockchainSdkConfig = BlockchainSdkConfig( + blockchairAuthorizationToken = values.blockchairAuthorizationToken, + blockcypherTokens = values.blockcypherTokens, + infuraProjectId = values.infuraProjectId + ) ) defaultConfig = defaultConfig.copy( - coinMarketCapKey = values.coinMarketCapKey, - moonPayApiKey = values.moonPayApiKey, - moonPayApiSecretKey = values.moonPayApiSecretKey, - blockchainSdkConfig = BlockchainSdkConfig( - blockchairApiKey = values.blockchairApiKey, - blockcypherTokens = values.blockcypherTokens, - infuraProjectId = values.infuraProjectId - ) + coinMarketCapKey = values.coinMarketCapKey, + moonPayApiKey = values.moonPayApiKey, + moonPayApiSecretKey = values.moonPayApiSecretKey, + blockchainSdkConfig = BlockchainSdkConfig( + blockchairAuthorizationToken = values.blockchairAuthorizationToken, + blockcypherTokens = values.blockcypherTokens, + infuraProjectId = values.infuraProjectId + ) ) } From b1beae4589161e783a09171448081ff00fea82a9 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sun, 4 Jul 2021 20:39:40 +0300 Subject: [PATCH 010/189] Updated on 2026-08-14 --- app/build.gradle | 6 +- app/src/main/assets/binance_tokens.json | 1658 ++++++ .../main/assets/binance_tokens_testnet.json | 1 + app/src/main/assets/bsc_tokens.json | 782 +++ app/src/main/assets/bsc_tokens_testnet.json | 99 + app/src/main/assets/erc20_tokens.json | 4761 ---------------- app/src/main/assets/ethereum_tokens.json | 4765 +++++++++++++++++ .../main/assets/ethereum_tokens_testnet.json | 28 +- .../com/tangem/tap/domain/TapWalletManager.kt | 10 +- .../domain/configurable/config/JsonModels.kt | 2 +- .../com/tangem/tap/domain/extensions/Card.kt | 3 +- .../tap/domain/tokens/CurrenciesRepository.kt | 107 +- .../tokens/ui/adapters/CurrenciesAdapter.kt | 13 +- .../tap/features/wallet/redux/WalletState.kt | 20 +- .../middlewares/MultiWalletMiddleware.kt | 39 +- .../redux/reducers/MultiWalletReducer.kt | 5 +- .../wallet/redux/reducers/WalletReducer.kt | 237 +- .../main/res/values/strings_untranslated.xml | 3 +- 18 files changed, 7601 insertions(+), 4938 deletions(-) create mode 100644 app/src/main/assets/binance_tokens.json create mode 100644 app/src/main/assets/binance_tokens_testnet.json create mode 100644 app/src/main/assets/bsc_tokens.json create mode 100644 app/src/main/assets/bsc_tokens_testnet.json delete mode 100644 app/src/main/assets/erc20_tokens.json create mode 100644 app/src/main/assets/ethereum_tokens.json diff --git a/app/build.gradle b/app/build.gradle index d2d3028830..853ff850fa 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-84_testnets-27' + implementation 'com.tangem:blockchain:develop-33' implementation 'com.tangem:core:fixes_for_app_release-44' implementation 'com.tangem:sdk:fixes_for_app_release-44' @@ -117,6 +117,10 @@ dependencies { implementation 'com.squareup.moshi:moshi:1.9.2' implementation "com.squareup.moshi:moshi-kotlin:1.9.2" implementation 'com.squareup.okhttp3:logging-interceptor:4.2.2' +// +// //Crypto + implementation "com.madgag.spongycastle:core:1.58.0.0" + testImplementation 'junit:junit:4.13.2' testImplementation "com.google.truth:truth:1.0.1" diff --git a/app/src/main/assets/binance_tokens.json b/app/src/main/assets/binance_tokens.json new file mode 100644 index 0000000000..1d01ab9280 --- /dev/null +++ b/app/src/main/assets/binance_tokens.json @@ -0,0 +1,1658 @@ +[ + { + "symbol" : "BUSD", + "decimalCount" : 8, + "contractAddress" : "BUSD-BD1", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BUSD-BD1/logo.png", + "name" : "Binance USD" + }, + { + "symbol" : "BTCB", + "decimalCount" : 8, + "contractAddress" : "BTCB-1DE", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BTCB-1DE/logo.png", + "name" : "Bitcoin BEP2" + }, + { + "symbol" : "ADA", + "decimalCount" : 8, + "contractAddress" : "ADA-9F4", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ADA-9F4/logo.png", + "name" : "CardanoBEP2" + }, + { + "symbol" : "AERGO", + "decimalCount" : 8, + "contractAddress" : "AERGO-46B", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/AERGO-46B/logo.png", + "name" : "Aergo" + }, + { + "symbol" : "ANKR", + "decimalCount" : 8, + "contractAddress" : "ANKR-E97", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ANKR-E97/logo.png", + "name" : "ANKR" + }, + { + "symbol" : "ARN", + "decimalCount" : 8, + "contractAddress" : "ARN-71B", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ARN-71B/logo.png", + "name" : "Aeron" + }, + { + "symbol" : "ARPA", + "decimalCount" : 8, + "contractAddress" : "ARPA-575", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ARPA-575/logo.png", + "name" : "ARPA" + }, + { + "symbol" : "BZNT", + "decimalCount" : 8, + "contractAddress" : "BZNT-464", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BZNT-464/logo.png", + "name" : "Bezant Token" + }, + { + "symbol" : "DAI", + "decimalCount" : 8, + "contractAddress" : "DAI-D75", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/DAI-D75/logo.png", + "name" : "DaiBEP2" + }, + { + "symbol" : "ETH", + "decimalCount" : 8, + "contractAddress" : "ETH-1C9", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ETH-1C9/logo.png", + "name" : "ETH BEP2" + }, + { + "symbol" : "ART", + "decimalCount" : 8, + "contractAddress" : "ART-3C9", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ART-3C9/logo.png", + "name" : "Maecenas ART Token" + }, + { + "symbol" : "ATOM", + "decimalCount" : 8, + "contractAddress" : "ATOM-596", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ATOM-596/logo.png", + "name" : "ATOMBEP2" + }, + { + "symbol" : "ATP", + "decimalCount" : 8, + "contractAddress" : "ATP-38C", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ATP-38C/logo.png", + "name" : "Atlas Protocol" + }, + { + "symbol" : "CAKE", + "decimalCount" : 8, + "contractAddress" : "CAKE-435", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/CAKE-435/logo.png", + "name" : "PancakeSwap Token" + }, + { + "symbol" : "CAN", + "decimalCount" : 8, + "contractAddress" : "CAN-677", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/CAN-677/logo.png", + "name" : "CanYaCoin" + }, + { + "symbol" : "CAS", + "decimalCount" : 8, + "contractAddress" : "CAS-167", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/CAS-167/logo.png", + "name" : "CASHAA" + }, + { + "symbol" : "CBIX", + "decimalCount" : 8, + "contractAddress" : "CBIX-3C9", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/CBIX-3C9/logo.png", + "name" : "Cubiex" + }, + { + "symbol" : "CBM", + "decimalCount" : 8, + "contractAddress" : "CBM-4B2", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/CBM-4B2/logo.png", + "name" : "CryptoBonusMiles" + }, + { + "symbol" : "CHZ", + "decimalCount" : 8, + "contractAddress" : "CHZ-ECD", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/CHZ-ECD/logo.png", + "name" : "Chiliz" + }, + { + "symbol" : "CNNS", + "decimalCount" : 8, + "contractAddress" : "CNNS-E16", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/CNNS-E16/logo.png", + "name" : "Crypto Neo-value Neural System" + }, + { + "symbol" : "COS", + "decimalCount" : 8, + "contractAddress" : "COS-2E4", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/COS-2E4/logo.png", + "name" : "Contentos" + }, + { + "symbol" : "COTI", + "decimalCount" : 8, + "contractAddress" : "COTI-CBB", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/COTI-CBB/logo.png", + "name" : "COTI" + }, + { + "symbol" : "COVA", + "decimalCount" : 8, + "contractAddress" : "COVA-218", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/COVA-218/logo.png", + "name" : "Covalent Token" + }, + { + "symbol" : "CRPT", + "decimalCount" : 8, + "contractAddress" : "CRPT-8C9", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/CRPT-8C9/logo.png", + "name" : "Crypterium Token" + }, + { + "symbol" : "IDRTB", + "decimalCount" : 8, + "contractAddress" : "IDRTB-178", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/IDRTB-178/logo.png", + "name" : "Rupiah Token" + }, + { + "symbol" : "AVA", + "decimalCount" : 8, + "contractAddress" : "AVA-645", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/AVA-645/logo.png", + "name" : "Travala.com Token" + }, + { + "symbol" : "AWC", + "decimalCount" : 8, + "contractAddress" : "AWC-986", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/AWC-986/logo.png", + "name" : "Atomic Wallet Token" + }, + { + "symbol" : "AXPR", + "decimalCount" : 8, + "contractAddress" : "AXPR-777", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/AXPR-777/logo.png", + "name" : "AXPR.B" + }, + { + "symbol" : "BAKE", + "decimalCount" : 8, + "contractAddress" : "BAKE-5E0", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BAKE-5E0/logo.png", + "name" : "BakeryToken" + }, + { + "symbol" : "BAND", + "decimalCount" : 8, + "contractAddress" : "BAND-34B", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BAND-34B/logo.png", + "name" : "BandProtocolBEP2" + }, + { + "symbol" : "BCH", + "decimalCount" : 8, + "contractAddress" : "BCH-1FD", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BCH-1FD/logo.png", + "name" : "BCH BEP2" + }, + { + "symbol" : "BCPT", + "decimalCount" : 8, + "contractAddress" : "BCPT-95A", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BCPT-95A/logo.png", + "name" : "Blockmason Credit Protocol" + }, + { + "symbol" : "BEAR", + "decimalCount" : 8, + "contractAddress" : "BEAR-14C", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BEAR-14C/logo.png", + "name" : "3X Short Bitcoin Token" + }, + { + "symbol" : "BET", + "decimalCount" : 8, + "contractAddress" : "BET-844", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BET-844/logo.png", + "name" : "EOSBet Token" + }, + { + "symbol" : "TAUDB", + "decimalCount" : 8, + "contractAddress" : "TAUDB-888", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/TAUDB-888/logo.png", + "name" : "TrueAUD" + }, + { + "symbol" : "THKDB", + "decimalCount" : 8, + "contractAddress" : "THKDB-888", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/THKDB-888/logo.png", + "name" : "TrueHKD" + }, + { + "symbol" : "TUSDB", + "decimalCount" : 8, + "contractAddress" : "TUSDB-888", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/TUSDB-888/logo.png", + "name" : "TrueUSD" + }, + { + "symbol" : "USDSB", + "decimalCount" : 8, + "contractAddress" : "USDSB-1AC", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/USDSB-1AC/logo.png", + "name" : "USDS" + }, + { + "symbol" : "USDT", + "decimalCount" : 8, + "contractAddress" : "USDT-6D8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/USDT-6D8/logo.png", + "name" : "USDTBEP2" + }, + { + "symbol" : "BIFI", + "decimalCount" : 8, + "contractAddress" : "BIFI-290", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BIFI-290/logo.png", + "name" : "beefy.finance" + }, + { + "symbol" : "BKBT", + "decimalCount" : 8, + "contractAddress" : "BKBT-3A6", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BKBT-3A6/logo.png", + "name" : "Bitwires Token" + }, + { + "symbol" : "BLINK", + "decimalCount" : 8, + "contractAddress" : "BLINK-9C6", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BLINK-9C6/logo.png", + "name" : "Blockmason Link" + }, + { + "symbol" : "BOLT", + "decimalCount" : 8, + "contractAddress" : "BOLT-4C6", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BOLT-4C6/logo.png", + "name" : "BOLT Token" + }, + { + "symbol" : "BST2", + "decimalCount" : 8, + "contractAddress" : "BST2-2F2", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BST2-2F2/logo.png", + "name" : "BOOSTO" + }, + { + "symbol" : "BTTB", + "decimalCount" : 8, + "contractAddress" : "BTTB-D31", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BTTB-D31/logo.png", + "name" : "BTTB" + }, + { + "symbol" : "BULL", + "decimalCount" : 8, + "contractAddress" : "BULL-BE4", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BULL-BE4/logo.png", + "name" : "3x Long Bitcoin Token" + }, + { + "symbol" : "BURGER", + "decimalCount" : 8, + "contractAddress" : "BURGER-33A", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BURGER-33A/logo.png", + "name" : "Burger Swap" + }, + { + "symbol" : "CSM", + "decimalCount" : 8, + "contractAddress" : "CSM-734", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/CSM-734/logo.png", + "name" : "“Consentium”" + }, + { + "symbol" : "DEFI", + "decimalCount" : 8, + "contractAddress" : "DEFI-FA5", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/DEFI-FA5/logo.png", + "name" : "DeFi Token" + }, + { + "symbol" : "DOGE", + "decimalCount" : 8, + "contractAddress" : "DOGE-B67", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/DOGE-B67/logo.png", + "name" : "Dogecoin" + }, + { + "symbol" : "DOS", + "decimalCount" : 8, + "contractAddress" : "DOS-120", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/DOS-120/logo.png", + "name" : "DOS Network Token" + }, + { + "symbol" : "DOT", + "decimalCount" : 8, + "contractAddress" : "DOT-64C", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/DOT-64C/logo.png", + "name" : "PolkadotBEP2" + }, + { + "symbol" : "DREP", + "decimalCount" : 8, + "contractAddress" : "DREP-7D2", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/DREP-7D2/logo.png", + "name" : "DREP" + }, + { + "symbol" : "DUSK", + "decimalCount" : 8, + "contractAddress" : "DUSK-45E", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/DUSK-45E/logo.png", + "name" : "Dusk Network" + }, + { + "symbol" : "EBST", + "decimalCount" : 8, + "contractAddress" : "EBST-783", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/EBST-783/logo.png", + "name" : "eBoost" + }, + { + "symbol" : "ENTRP", + "decimalCount" : 8, + "contractAddress" : "ENTRP-C8D", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ENTRP-C8D/logo.png", + "name" : "Hut34 Entropy" + }, + { + "symbol" : "EOS", + "decimalCount" : 8, + "contractAddress" : "EOS-CDD", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/EOS-CDD/logo.png", + "name" : "EOS BEP2" + }, + { + "symbol" : "EOSBEAR", + "decimalCount" : 8, + "contractAddress" : "EOSBEAR-721", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/EOSBEAR-721/logo.png", + "name" : "3X Short EOS Token" + }, + { + "symbol" : "EOSBULL", + "decimalCount" : 8, + "contractAddress" : "EOSBULL-F0D", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/EOSBULL-F0D/logo.png", + "name" : "3X Long EOS Token" + }, + { + "symbol" : "EQL", + "decimalCount" : 8, + "contractAddress" : "EQL-586", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/EQL-586/logo.png", + "name" : "EQUAL" + }, + { + "symbol" : "ERD", + "decimalCount" : 8, + "contractAddress" : "ERD-D06", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ERD-D06/logo.png", + "name" : "Elrond" + }, + { + "symbol" : "ETHBEAR", + "decimalCount" : 8, + "contractAddress" : "ETHBEAR-B2B", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ETHBEAR-B2B/logo.png", + "name" : "3X Short Ethereum Token" + }, + { + "symbol" : "ETHBULL", + "decimalCount" : 8, + "contractAddress" : "ETHBULL-D33", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ETHBULL-D33/logo.png", + "name" : "3X Long Ethereum Token" + }, + { + "symbol" : "EVT", + "decimalCount" : 8, + "contractAddress" : "EVT-49B", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/EVT-49B/logo.png", + "name" : "everiToken" + }, + { + "symbol" : "FRM", + "decimalCount" : 8, + "contractAddress" : "FRM-DE7", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/FRM-DE7/logo.png", + "name" : "Ferrum Network Token" + }, + { + "symbol" : "FSN", + "decimalCount" : 8, + "contractAddress" : "FSN-E14", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/FSN-E14/logo.png", + "name" : "Fusion" + }, + { + "symbol" : "FTM", + "decimalCount" : 8, + "contractAddress" : "FTM-A64", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/FTM-A64/logo.png", + "name" : "Fantom" + }, + { + "symbol" : "GIV", + "decimalCount" : 8, + "contractAddress" : "GIV-94E", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/GIV-94E/logo.png", + "name" : "Givly Coin" + }, + { + "symbol" : "GMAT", + "decimalCount" : 8, + "contractAddress" : "GMAT-FC8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/GMAT-FC8/logo.png", + "name" : "GoWithMi" + }, + { + "symbol" : "GTO", + "decimalCount" : 8, + "contractAddress" : "GTO-908", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/GTO-908/logo.png", + "name" : "Gifto" + }, + { + "symbol" : "HNST", + "decimalCount" : 8, + "contractAddress" : "HNST-3C9", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/HNST-3C9/logo.png", + "name" : "Honest" + }, + { + "symbol" : "HYN", + "decimalCount" : 8, + "contractAddress" : "HYN-F21", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/HYN-F21/logo.png", + "name" : "Hyperion Token" + }, + { + "symbol" : "INJ", + "decimalCount" : 8, + "contractAddress" : "INJ-FAE", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/INJ-FAE/logo.png", + "name" : "Injective Protocol" + }, + { + "symbol" : "IRIS", + "decimalCount" : 8, + "contractAddress" : "IRIS-D88", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/IRIS-D88/logo.png", + "name" : "IRIS Network" + }, + { + "symbol" : "KAT", + "decimalCount" : 8, + "contractAddress" : "KAT-7BB", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/KAT-7BB/logo.png", + "name" : "Kambria Token" + }, + { + "symbol" : "LBA", + "decimalCount" : 8, + "contractAddress" : "LBA-340", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/LBA-340/logo.png", + "name" : "Lend-Borrow-Asset" + }, + { + "symbol" : "LIT", + "decimalCount" : 8, + "contractAddress" : "LIT-099", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/LIT-099/logo.png", + "name" : "LITION" + }, + { + "symbol" : "LOKI", + "decimalCount" : 8, + "contractAddress" : "LOKI-6A9", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/LOKI-6A9/logo.png", + "name" : "Loki" + }, + { + "symbol" : "LTC", + "decimalCount" : 8, + "contractAddress" : "LTC-F07", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/LTC-F07/logo.png", + "name" : "LTC BEP2" + }, + { + "symbol" : "LTO", + "decimalCount" : 8, + "contractAddress" : "LTO-BDF", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/LTO-BDF/logo.png", + "name" : "LTO Network" + }, + { + "symbol" : "MATIC", + "decimalCount" : 8, + "contractAddress" : "MATIC-84A", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/MATIC-84A/logo.png", + "name" : "Matic Token" + }, + { + "symbol" : "MDAB", + "decimalCount" : 8, + "contractAddress" : "MDAB-D42", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/MDAB-D42/logo.png", + "name" : "MDAB" + }, + { + "symbol" : "MEETONE", + "decimalCount" : 8, + "contractAddress" : "MEETONE-031", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/MEETONE-031/logo.png", + "name" : "MEET.ONE" + }, + { + "symbol" : "MITH", + "decimalCount" : 8, + "contractAddress" : "MITH-C76", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/MITH-C76/logo.png", + "name" : "Mithril" + }, + { + "symbol" : "MITX", + "decimalCount" : 8, + "contractAddress" : "MITX-CAA", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/MITX-CAA/logo.png", + "name" : "Morpheus Infrastructure Token" + }, + { + "symbol" : "MTV", + "decimalCount" : 8, + "contractAddress" : "MTV-4C6", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/MTV-4C6/logo.png", + "name" : "MultiVAC" + }, + { + "symbol" : "MVL", + "decimalCount" : 8, + "contractAddress" : "MVL-7B0", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/MVL-7B0/logo.png", + "name" : "Mass Vehicle Ledger" + }, + { + "symbol" : "MZK", + "decimalCount" : 8, + "contractAddress" : "MZK-2C7", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/MZK-2C7/logo.png", + "name" : "Muzika" + }, + { + "symbol" : "NEW", + "decimalCount" : 8, + "contractAddress" : "NEW-09E", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/NEW-09E/logo.png", + "name" : "NEWTON" + }, + { + "symbol" : "NEXO", + "decimalCount" : 8, + "contractAddress" : "NEXO-A84", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/NEXO-A84/logo.png", + "name" : "Nexo" + }, + { + "symbol" : "NOIZB", + "decimalCount" : 8, + "contractAddress" : "NOIZB-878", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/NOIZB-878/logo.png", + "name" : "NOIZ Token" + }, + { + "symbol" : "NOW", + "decimalCount" : 8, + "contractAddress" : "NOW-E68", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/NOW-E68/logo.png", + "name" : "NOW Token" + }, + { + "symbol" : "NPXB", + "decimalCount" : 8, + "contractAddress" : "NPXB-1E8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/NPXB-1E8/logo.png", + "name" : "NPX Binance token" + }, + { + "symbol" : "NPXSXEM", + "decimalCount" : 8, + "contractAddress" : "NPXSXEM-89C", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/NPXSXEM-89C/logo.png", + "name" : "Pundi X NEM" + }, + { + "symbol" : "ONE", + "decimalCount" : 8, + "contractAddress" : "ONE-5F9", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ONE-5F9/logo.png", + "name" : "Harmony.One" + }, + { + "symbol" : "ONT", + "decimalCount" : 8, + "contractAddress" : "ONT-33D", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ONT-33D/logo.png", + "name" : "ONTBEP2" + }, + { + "symbol" : "PHB", + "decimalCount" : 8, + "contractAddress" : "PHB-2DF", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/PHB-2DF/logo.png", + "name" : "Red Pulse Phoenix Binance" + }, + { + "symbol" : "PHV", + "decimalCount" : 8, + "contractAddress" : "PHV-4A1", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/PHV-4A1/logo.png", + "name" : "PathHive Network" + }, + { + "symbol" : "PIBNB", + "decimalCount" : 8, + "contractAddress" : "PIBNB-43C", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/PIBNB-43C/logo.png", + "name" : "PCHAIN Token" + }, + { + "symbol" : "PLG", + "decimalCount" : 8, + "contractAddress" : "PLG-D8D", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/PLG-D8D/logo.png", + "name" : "Pledge Coin" + }, + { + "symbol" : "PVT", + "decimalCount" : 8, + "contractAddress" : "PVT-554", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/PVT-554/logo.png", + "name" : "Pivot Token" + }, + { + "symbol" : "PYN", + "decimalCount" : 8, + "contractAddress" : "PYN-C37", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/PYN-C37/logo.png", + "name" : "paycentos" + }, + { + "symbol" : "QBX", + "decimalCount" : 8, + "contractAddress" : "QBX-38C", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/QBX-38C/logo.png", + "name" : "qiibeeToken" + }, + { + "symbol" : "RAVEN", + "decimalCount" : 8, + "contractAddress" : "RAVEN-F66", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/RAVEN-F66/logo.png", + "name" : "Raven Protocol" + }, + { + "symbol" : "RUNE", + "decimalCount" : 8, + "contractAddress" : "RUNE-B1A", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/RUNE-B1A/logo.png", + "name" : "Rune" + }, + { + "symbol" : "SHR", + "decimalCount" : 8, + "contractAddress" : "SHR-DB6", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/SHR-DB6/logo.png", + "name" : "ShareToken" + }, + { + "symbol" : "SLV", + "decimalCount" : 8, + "contractAddress" : "SLV-986", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/SLV-986/logo.png", + "name" : "Silverway" + }, + { + "symbol" : "SPNDB", + "decimalCount" : 8, + "contractAddress" : "SPNDB-916", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/SPNDB-916/logo.png", + "name" : "Spendcoin" + }, + { + "symbol" : "STYL", + "decimalCount" : 8, + "contractAddress" : "STYL-65B", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/STYL-65B/logo.png", + "name" : "Yin Lang Music IP Token" + }, + { + "symbol" : "SWINGBY", + "decimalCount" : 8, + "contractAddress" : "SWINGBY-888", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/SWINGBY-888/logo.png", + "name" : "Swingby Token" + }, + { + "symbol" : "SWIPE.B", + "decimalCount" : 8, + "contractAddress" : "SWIPE.B-DC0", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/SWIPE.B-DC0/logo.png", + "name" : "SWIPE Token" + }, + { + "symbol" : "SXP", + "decimalCount" : 8, + "contractAddress" : "SXP-CCC", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/SXP-CCC/logo.png", + "name" : "Swipe" + }, + { + "symbol" : "TM2", + "decimalCount" : 8, + "contractAddress" : "TM2-0C4", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/TM2-0C4/logo.png", + "name" : "Traxia 2" + }, + { + "symbol" : "TOMOB", + "decimalCount" : 8, + "contractAddress" : "TOMOB-4BC", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/TOMOB-4BC/logo.png", + "name" : "TomoChain" + }, + { + "symbol" : "TOP", + "decimalCount" : 8, + "contractAddress" : "TOP-491", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/TOP-491/logo.png", + "name" : "TOP Network" + }, + { + "symbol" : "TROY", + "decimalCount" : 8, + "contractAddress" : "TROY-9B8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/TROY-9B8/logo.png", + "name" : "TROY" + }, + { + "symbol" : "TRUE", + "decimalCount" : 8, + "contractAddress" : "TRUE-D84", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/TRUE-D84/logo.png", + "name" : "TrueChain" + }, + { + "symbol" : "TRXB", + "decimalCount" : 8, + "contractAddress" : "TRXB-2E6", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/TRXB-2E6/logo.png", + "name" : "TRXB" + }, + { + "symbol" : "TWT", + "decimalCount" : 8, + "contractAddress" : "TWT-8C2", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/TWT-8C2/logo.png", + "name" : "Trust Wallet" + }, + { + "symbol" : "UGAS", + "decimalCount" : 8, + "contractAddress" : "UGAS-B0C", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/UGAS-B0C/logo.png", + "name" : "Ultrain Coin" + }, + { + "symbol" : "UND", + "decimalCount" : 8, + "contractAddress" : "UND-EBC", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/UND-EBC/logo.png", + "name" : "United Network Distribution" + }, + { + "symbol" : "UPX", + "decimalCount" : 8, + "contractAddress" : "UPX-F3E", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/UPX-F3E/logo.png", + "name" : "UPX" + }, + { + "symbol" : "VDX", + "decimalCount" : 8, + "contractAddress" : "VDX-A17", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/VDX-A17/logo.png", + "name" : "Vodi X" + }, + { + "symbol" : "VIDT", + "decimalCount" : 8, + "contractAddress" : "VIDT-F53", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/VIDT-F53/logo.png", + "name" : "V-ID Token" + }, + { + "symbol" : "VOTE", + "decimalCount" : 8, + "contractAddress" : "VOTE-FD4", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/VOTE-FD4/logo.png", + "name" : "Vote" + }, + { + "symbol" : "WICC", + "decimalCount" : 8, + "contractAddress" : "WICC-01D", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/WICC-01D/logo.png", + "name" : "WaykiChain Coin" + }, + { + "symbol" : "WINB", + "decimalCount" : 8, + "contractAddress" : "WINB-41F", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/WINB-41F/logo.png", + "name" : "WINB" + }, + { + "symbol" : "WISH", + "decimalCount" : 8, + "contractAddress" : "WISH-2D5", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/WISH-2D5/logo.png", + "name" : "MyWish" + }, + { + "symbol" : "WRX", + "decimalCount" : 8, + "contractAddress" : "WRX-ED1", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/WRX-ED1/logo.png", + "name" : "WazirX Token" + }, + { + "symbol" : "XBASE", + "decimalCount" : 8, + "contractAddress" : "XBASE-CD2", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/XBASE-CD2/logo.png", + "name" : "Eterbase Coin" + }, + { + "symbol" : "XRP", + "decimalCount" : 8, + "contractAddress" : "XRP-BF2", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/XRP-BF2/logo.png", + "name" : "XRP BEP2" + }, + { + "symbol" : "XRPBEAR", + "decimalCount" : 8, + "contractAddress" : "XRPBEAR-00B", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/XRPBEAR-00B/logo.png", + "name" : "3X Short XRP Token" + }, + { + "symbol" : "XRPBULL", + "decimalCount" : 8, + "contractAddress" : "XRPBULL-E7C", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/XRPBULL-E7C/logo.png", + "name" : "3X Long XRP Token" + }, + { + "symbol" : "XTZ", + "decimalCount" : 8, + "contractAddress" : "XTZ-F7A", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/XTZ-F7A/logo.png", + "name" : "XTZ BEP2" + }, + { + "symbol" : "XVS", + "decimalCount" : 8, + "contractAddress" : "XVS-795", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/XVS-795/logo.png", + "name" : "Venus" + }, + { + "symbol" : "YFII", + "decimalCount" : 8, + "contractAddress" : "YFII-061", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/YFII-061/logo.png", + "name" : "YFIIBEP2" + }, + { + "symbol" : "ZEBI", + "decimalCount" : 8, + "contractAddress" : "ZEBI-84F", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ZEBI-84F/logo.png", + "name" : "ZEBI" + }, + { + "symbol" : "ZEC", + "decimalCount" : 8, + "contractAddress" : "ZEC-93E", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ZEC-93E/logo.png", + "name" : "ZECBEP2" + } +] diff --git a/app/src/main/assets/binance_tokens_testnet.json b/app/src/main/assets/binance_tokens_testnet.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/app/src/main/assets/binance_tokens_testnet.json @@ -0,0 +1 @@ +[] diff --git a/app/src/main/assets/bsc_tokens.json b/app/src/main/assets/bsc_tokens.json new file mode 100644 index 0000000000..712d35c6dd --- /dev/null +++ b/app/src/main/assets/bsc_tokens.json @@ -0,0 +1,782 @@ +[ + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x6c5FE6e99D2484db7E4BF34F365ABA42d0E4dC20/logo.png", + "symbol" : "ABS", + "name" : "Absorber", + "contractAddress" : "0x6c5FE6e99D2484db7E4BF34F365ABA42d0E4dC20", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x4197C6EF3879a08cD51e5560da5064B773aa1d29/logo.png", + "symbol" : "ACS", + "name" : "ACryptoS", + "contractAddress" : "0x4197C6EF3879a08cD51e5560da5064B773aa1d29", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xa1faa113cbE53436Df28FF0aEe54275c13B40975/logo.png", + "symbol" : "ALPHA", + "name" : "Alpha Token", + "contractAddress" : "0xa1faa113cbE53436Df28FF0aEe54275c13B40975", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xa184088a740c695E156F91f5cC086a06bb78b827/logo.png", + "symbol" : "AUTO", + "name" : "AUTOv2", + "contractAddress" : "0xa184088a740c695E156F91f5cC086a06bb78b827", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56/logo.png", + "symbol" : "BUSD", + "name" : "BUSD", + "contractAddress" : "0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xAD6cAEb32CD2c308980a548bD0Bc5AA4306c6c18/logo.png", + "symbol" : "BAND", + "name" : "Binance-Peg Band Protocol", + "contractAddress" : "0xAD6cAEb32CD2c308980a548bD0Bc5AA4306c6c18", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x8fF795a6F4D97E7887C79beA79aba5cc76444aDf/logo.png", + "symbol" : "BCH", + "name" : "Binance-Peg Bitcoin Cash", + "contractAddress" : "0x8fF795a6F4D97E7887C79beA79aba5cc76444aDf", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x72fAa679E1008Ad8382959FF48E392042A8b06f7/logo.png", + "symbol" : "bALBT", + "name" : "AllianceBlock Token", + "contractAddress" : "0x72fAa679E1008Ad8382959FF48E392042A8b06f7", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 6, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x63870A18B6e42b01Ef1Ad8A2302ef50B7132054F/logo.png", + "symbol" : "BLINK", + "name" : "BLink Token", + "contractAddress" : "0x63870A18B6e42b01Ef1Ad8A2302ef50B7132054F", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x5Ac52EE5b2a633895292Ff6d8A89bB9190451587/logo.png", + "symbol" : "BSCX", + "name" : "BSCX", + "contractAddress" : "0x5Ac52EE5b2a633895292Ff6d8A89bB9190451587", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c/logo.png", + "symbol" : "BTCB", + "name" : "Binance-Peg BTCB Token", + "contractAddress" : "0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x3EE2200Efb3400fAbB9AacF31297cBdD1d435D47/logo.png", + "symbol" : "ADA", + "name" : "Binance-Peg Cardano", + "contractAddress" : "0x3EE2200Efb3400fAbB9AacF31297cBdD1d435D47", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xF8A0BF9cF54Bb92F17374d9e9A321E6a111a51bD/logo.png", + "symbol" : "LINK", + "name" : "Binance-Peg ChainLink", + "contractAddress" : "0xF8A0BF9cF54Bb92F17374d9e9A321E6a111a51bD", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x0Eb3a705fc54725037CC9e008bDede697f62F335/logo.png", + "symbol" : "ATOM", + "name" : "Binance-Peg Cosmos", + "contractAddress" : "0x0Eb3a705fc54725037CC9e008bDede697f62F335", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xC9849E6fdB743d08fAeE3E34dd2D1bc69EA11a51/logo.png", + "symbol" : "BUNNY", + "name" : "Bunny Token", + "contractAddress" : "0xC9849E6fdB743d08fAeE3E34dd2D1bc69EA11a51", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xBf5140A22578168FD562DCcF235E5D43A02ce9B1/logo.png", + "symbol" : "UNI", + "name" : "Binance-Peg Uniswap", + "contractAddress" : "0xBf5140A22578168FD562DCcF235E5D43A02ce9B1", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x88f1A5ae2A3BF98AEAF342D26B30a79438c9142e/logo.png", + "symbol" : "YFI", + "name" : "Binance-Peg yearn.finance", + "contractAddress" : "0x88f1A5ae2A3BF98AEAF342D26B30a79438c9142e", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x81859801b01764D4f0Fa5E64729f5a6C3b91435b/logo.png", + "symbol" : "BFI", + "name" : "bearn.fi", + "contractAddress" : "0x81859801b01764D4f0Fa5E64729f5a6C3b91435b", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xCa3F508B8e4Dd382eE878A314789373D80A5190A/logo.png", + "symbol" : "BIFI", + "name" : "beefy.finance", + "contractAddress" : "0xCa3F508B8e4Dd382eE878A314789373D80A5190A", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x7F70642d88cf1C4a3a7abb072B53B929b653edA5/logo.png", + "symbol" : "YFII", + "name" : "Binance-Peg YFII.finance", + "contractAddress" : "0x7F70642d88cf1C4a3a7abb072B53B929b653edA5", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x790Be81C3cA0e53974bE2688cDb954732C9862e1/logo.png", + "symbol" : "BREW", + "name" : "CafeSwap Token", + "contractAddress" : "0x790Be81C3cA0e53974bE2688cDb954732C9862e1", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xcc2E12a9b5b75360c6FBf23B584c275D52cDdb0E/logo.png", + "symbol" : "CROW", + "name" : "Crow Token", + "contractAddress" : "0xcc2E12a9b5b75360c6FBf23B584c275D52cDdb0E", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 6, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xA8c2B8eec3d368C0253ad3dae65a5F2BBB89c929/logo.png", + "symbol" : "CTK", + "name" : "CertiK Token", + "contractAddress" : "0xA8c2B8eec3d368C0253ad3dae65a5F2BBB89c929", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x97a30C692eCe9C317235d48287d23d358170FC40/logo.png", + "symbol" : "CRX", + "name" : "CryptEx Token", + "contractAddress" : "0x97a30C692eCe9C317235d48287d23d358170FC40", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xF952Fc3ca7325Cc27D15885d37117676d25BfdA6/logo.png", + "symbol" : "EGG", + "name" : "Goose Golden Egg", + "contractAddress" : "0xF952Fc3ca7325Cc27D15885d37117676d25BfdA6", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xbF7c81FFF98BbE61B40Ed186e4AfD6DDd01337fe/logo.png", + "symbol" : "EGLD", + "name" : "Elrond", + "contractAddress" : "0xbF7c81FFF98BbE61B40Ed186e4AfD6DDd01337fe", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x928e55daB735aa8260AF3cEDadA18B5f70C72f1b/logo.png", + "symbol" : "FRONT", + "name" : "Frontier Token", + "contractAddress" : "0x928e55daB735aa8260AF3cEDadA18B5f70C72f1b", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 6, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xf79037F6f6bE66832DE4E7516be52826BC3cBcc4/logo.png", + "symbol" : "HARD", + "name" : "Hard Protocol", + "contractAddress" : "0xf79037F6f6bE66832DE4E7516be52826BC3cBcc4", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 6, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xC7d8D35EBA58a0935ff2D5a33Df105DD9f071731/logo.png", + "symbol" : "HGET", + "name" : "Hedget", + "contractAddress" : "0xC7d8D35EBA58a0935ff2D5a33Df105DD9f071731", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x948d2a81086A075b3130BAc19e4c6DEe1D2E3fE8/logo.png", + "symbol" : "Helmet", + "name" : "Helmet.insure Governance Token", + "contractAddress" : "0x948d2a81086A075b3130BAc19e4c6DEe1D2E3fE8", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xa2B726B1145A4773F68593CF171187d8EBe4d495/logo.png", + "symbol" : "INJ", + "name" : "Injective Protocol", + "contractAddress" : "0xa2B726B1145A4773F68593CF171187d8EBe4d495", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x7979F6C54ebA05E18Ded44C4F986F49a5De551c2/logo.png", + "symbol" : "KEBAB", + "name" : "Kebab Token", + "contractAddress" : "0x7979F6C54ebA05E18Ded44C4F986F49a5De551c2", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xb59490aB09A0f526Cc7305822aC65f2Ab12f9723/logo.png", + "symbol" : "LIT", + "name" : "Litentry", + "contractAddress" : "0xb59490aB09A0f526Cc7305822aC65f2Ab12f9723", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0/logo.png", + "symbol" : "MATIC", + "name" : "Matic Token", + "contractAddress" : "0xcc42724c6683b7e57334c4e856f4c9965ed682bd", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 9, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xC7FB8c5eFE8564205eE5CbA4dE8eeA8a086a4bd2/logo.png", + "symbol" : "POCROC", + "name" : "PocketRocket", + "contractAddress" : "0xC7FB8c5eFE8564205eE5CbA4dE8eeA8a086a4bd2", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xF21768cCBC73Ea5B6fd3C687208a7c2def2d966e/logo.png", + "symbol" : "REEF", + "name" : "Reef.finance", + "contractAddress" : "0xF21768cCBC73Ea5B6fd3C687208a7c2def2d966e", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xE40255C5d7fa7ceEc5120408C78C787CECB4cfdb/logo.png", + "symbol" : "SWGb", + "name" : "SWGb", + "contractAddress" : "0xE40255C5d7fa7ceEc5120408C78C787CECB4cfdb", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xD41FDb03Ba84762dD66a0af1a6C8540FF1ba5dfb/logo.png", + "symbol" : "SFP", + "name" : "SafePal Token", + "contractAddress" : "0xD41FDb03Ba84762dD66a0af1a6C8540FF1ba5dfb", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x78650B139471520656b9E7aA7A5e9276814a38e9/logo.png", + "symbol" : "BTCST", + "name" : "StandardBTCHashrateToken", + "contractAddress" : "0x78650B139471520656b9E7aA7A5e9276814a38e9", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3/logo.png", + "symbol" : "DAI", + "name" : "Binance-Peg Dai Token", + "contractAddress" : "0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x7083609fCE4d1d8Dc0C979AAb8c869Ea2C873402/logo.png", + "symbol" : "DOT", + "name" : "Binance-Peg Polkadot", + "contractAddress" : "0x7083609fCE4d1d8Dc0C979AAb8c869Ea2C873402", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x67ee3Cb086F8a16f34beE3ca72FAD36F7Db929e2/logo.png", + "symbol" : "DODO", + "name" : "DODO", + "contractAddress" : "0x67ee3Cb086F8a16f34beE3ca72FAD36F7Db929e2", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x56b6fB708fC5732DEC1Afc8D8556423A2EDcCbD6/logo.png", + "symbol" : "EOS", + "name" : "Binance-Peg EOS", + "contractAddress" : "0x56b6fB708fC5732DEC1Afc8D8556423A2EDcCbD6", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 9, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x233d91A0713155003fc4DcE0AFa871b508B3B715/logo.png", + "symbol" : "DITTO", + "name" : "Ditto", + "contractAddress" : "0x233d91A0713155003fc4DcE0AFa871b508B3B715", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x2170Ed0880ac9A755fd29B2688956BD959F933F8/logo.png", + "symbol" : "ETH", + "name" : "Ethereum Token", + "contractAddress" : "0x2170Ed0880ac9A755fd29B2688956BD959F933F8", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x190b589cf9Fb8DDEabBFeae36a813FFb2A702454/logo.png", + "symbol" : "BDO", + "name" : "bDollar", + "contractAddress" : "0x190b589cf9Fb8DDEabBFeae36a813FFb2A702454", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82/logo.png", + "symbol" : "Cake", + "name" : "PancakeSwap Token", + "contractAddress" : "0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x0D8Ce2A99Bb6e3B7Db580eD848240e4a0F9aE153/logo.png", + "symbol" : "FIL", + "name" : "Binance-Peg Filecoin", + "contractAddress" : "0x0D8Ce2A99Bb6e3B7Db580eD848240e4a0F9aE153", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x4338665CBB7B2485A8855A139b75D5e34AB0DB94/logo.png", + "symbol" : "LTC", + "name" : "Binance-Peg Litecoin", + "contractAddress" : "0x4338665CBB7B2485A8855A139b75D5e34AB0DB94", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x55d398326f99059fF775485246999027B3197955/logo.png", + "symbol" : "USDT", + "name" : "Binance-Peg Tether USD", + "contractAddress" : "0x55d398326f99059fF775485246999027B3197955", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x2090c8295769791ab7A3CF1CC6e0AA19F35e441A/logo.png", + "symbol" : "Fuel", + "name" : "Fuel Token", + "contractAddress" : "0x2090c8295769791ab7A3CF1CC6e0AA19F35e441A", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 9, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x3aabCf53A1930A42E18D938C019E83Ebee50a849/logo.png", + "symbol" : "SPG", + "name" : "SPONGE", + "contractAddress" : "0x3aabCf53A1930A42E18D938C019E83Ebee50a849", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x37Ac4D6140e54304D77437A5c11924f61a2D976f/logo.png", + "symbol" : "SFUEL", + "name" : "SparkPoint Fuel", + "contractAddress" : "0x37Ac4D6140e54304D77437A5c11924f61a2D976f", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x47BEAd2563dCBf3bF2c9407fEa4dC236fAbA485A/logo.png", + "symbol" : "SXP", + "name" : "Swipe", + "contractAddress" : "0x47BEAd2563dCBf3bF2c9407fEa4dC236fAbA485A", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xeAF7D8395CCE52DAef138d39a1CEfA51b97C15aE/logo.png", + "symbol" : "TBC", + "name" : "TeraBlock Token", + "contractAddress" : "0xeAF7D8395CCE52DAef138d39a1CEfA51b97C15aE", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x4B0F1812e5Df2A09796481Ff14017e6005508003/logo.png", + "symbol" : "TWT", + "name" : "Trust Wallet", + "contractAddress" : "0x4B0F1812e5Df2A09796481Ff14017e6005508003", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x728C5baC3C3e370E372Fc4671f9ef6916b814d8B/logo.png", + "symbol" : "UNFI", + "name" : "UNFI", + "contractAddress" : "0x728C5baC3C3e370E372Fc4671f9ef6916b814d8B", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x4BD17003473389A42DAF6a0a729f6Fdb328BbBd7/logo.png", + "symbol" : "VAI", + "name" : "VAI Stablecoin", + "contractAddress" : "0x4BD17003473389A42DAF6a0a729f6Fdb328BbBd7", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x23396cF899Ca06c4472205fC903bDB4de249D6fC/logo.png", + "symbol" : "UST", + "name" : "Wrapped UST Token", + "contractAddress" : "0x23396cF899Ca06c4472205fC903bDB4de249D6fC", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x1D2F0da169ceB9fC7B3144628dB156f3F6c60dBE/logo.png", + "symbol" : "XRP", + "name" : "Binance-Peg XRP", + "contractAddress" : "0x1D2F0da169ceB9fC7B3144628dB156f3F6c60dBE", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x16939ef78684453bfDFb47825F8a5F714f12623a/logo.png", + "symbol" : "XTZ", + "name" : "Binance-Peg Tezos", + "contractAddress" : "0x16939ef78684453bfDFb47825F8a5F714f12623a", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xcF6BB5389c92Bdda8a3747Ddb454cB7a64626C63/logo.png", + "symbol" : "XVS", + "name" : "Venus", + "contractAddress" : "0xcF6BB5389c92Bdda8a3747Ddb454cB7a64626C63", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x0231f91e02DebD20345Ae8AB7D71A41f8E140cE7/logo.png", + "symbol" : "bwJUP", + "name" : "Jupiter", + "contractAddress" : "0x0231f91e02DebD20345Ae8AB7D71A41f8E140cE7", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c/logo.png", + "symbol" : "WBNB", + "name" : "Wrapped BNB", + "contractAddress" : "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x44754455564474A89358B2C2265883DF993b12F0/logo.png", + "symbol" : "ZEE", + "name" : "ZeroSwapToken", + "contractAddress" : "0x44754455564474A89358B2C2265883DF993b12F0", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : false + } + } +] diff --git a/app/src/main/assets/bsc_tokens_testnet.json b/app/src/main/assets/bsc_tokens_testnet.json new file mode 100644 index 0000000000..c07946b0d7 --- /dev/null +++ b/app/src/main/assets/bsc_tokens_testnet.json @@ -0,0 +1,99 @@ +[ + { + "decimalCount" : 18, + "symbol" : "BUSD", + "name" : "Binance-Peg BUSD Token", + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56/logo.png", + "contractAddress" : "0xed24fc36d5ee211ea25a80239fb8c4cfd80f12ee", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : true + } + }, + { + "decimalCount" : 18, + "symbol" : "ETH", + "name" : "Ethereum Token", + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x2170Ed0880ac9A755fd29B2688956BD959F933F8/logo.png", + "contractAddress" : "0xd66c6b4f0be8ce5b39d52e0fd1344c389929b378", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : true + } + }, + { + "decimalCount" : 18, + "symbol" : "BTCB", + "name" : "Binance-Peg BTCB Token", + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c/logo.png", + "contractAddress" : "0x6ce8da28e2f864420840cf74474eff5fd80e65b8", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : true + } + }, + { + "decimalCount" : 18, + "symbol" : "USDT", + "name" : "Binance-Peg Tether USD", + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x55d398326f99059fF775485246999027B3197955/logo.png", + "contractAddress" : "0x337610d27c682e347c9cd60bd4b3b107c9d34ddd", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : true + } + }, + { + "decimalCount" : 18, + "symbol" : "USDT", + "name" : "Tether USD", + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x55d398326f99059fF775485246999027B3197955/logo.png", + "contractAddress" : "0x7ef95a0fee0dd31b22626fa2e10ee6a223f8a684", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : true + } + }, + { + "decimalCount" : 18, + "symbol" : "XRP", + "name" : "Binance-Peg XRP", + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x1D2F0da169ceB9fC7B3144628dB156f3F6c60dBE/logo.png", + "contractAddress" : "0xa83575490d7df4e2f47b7d38ef351a2722ca45b9", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : true + } + }, + { + "decimalCount" : 18, + "symbol" : "DAI", + "name" : "Binance-Peg Dai Token", + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3/logo.png", + "contractAddress" : "0xec5dcb5dbf4b114c9d0f65bccab49ec54f6a0867", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : true + } + }, + { + "decimalCount" : 18, + "symbol" : "USDC", + "name" : "Binance-Peg USDC Token", + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png", + "contractAddress" : "0x64544969ed7ebf5f083679233325356ebe738930", + "blockchain" : { + "key" : "bsc", + "curve" : "secp256k1", + "testnet" : true + } + } +] + diff --git a/app/src/main/assets/erc20_tokens.json b/app/src/main/assets/erc20_tokens.json deleted file mode 100644 index 829096ad47..0000000000 --- a/app/src/main/assets/erc20_tokens.json +++ /dev/null @@ -1,4761 +0,0 @@ -[ - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7FF4169a6B5122b664c51c95727d87750eC07c84/logo.png", - "symbol": "10SET", - "name": "10Set Token", - "contractAddress": "0x7FF4169a6B5122b664c51c95727d87750eC07c84", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x111111111117dC0aa78b770fA6A738034120C302/logo.png", - "symbol": "1INCH", - "name": "1INCH Token", - "contractAddress": "0x111111111117dC0aa78b770fA6A738034120C302", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xBBc2AE13b23d715c30720F079fcd9B4a74093505/logo.png", - "symbol": "ERN", - "name": "@EthernityChain $ERN Token", - "contractAddress": "0xBBc2AE13b23d715c30720F079fcd9B4a74093505", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "1ST", - "name": "FirstBlood", - "contractAddress": "0xAf30D2a7E90d7DC361c8C4585e9BB7D2F6f15bc7", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9/logo.png", - "symbol": "AAVE", - "name": "Aave Token", - "contractAddress": "0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "ABT", - "name": "ArcBlock", - "contractAddress": "0xb98d4c97425d9908e66e53a6fdf673acca0be986", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xADE00C28244d5CE17D72E40330B1c318cD12B7c3/logo.png", - "symbol": "ADX", - "name": "AdEx Network", - "contractAddress": "0xADE00C28244d5CE17D72E40330B1c318cD12B7c3", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "AE", - "name": "Aeternity", - "contractAddress": "0x5ca9a71b1d01849c0a95490cc00559717fcf0d1d", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "symbol": "AION", - "name": "Aion", - "contractAddress": "0x4CEdA7906a5Ed2179785Cd3A40A69ee8bc99C466", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8eB24319393716668D768dCEC29356ae9CfFe285/logo.png", - "symbol": "AGI", - "name": "SingularityNET Token", - "contractAddress": "0x8eB24319393716668D768dCEC29356ae9CfFe285", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x626E8036dEB333b408Be468F951bdB42433cBF18/logo.png", - "symbol": "AIOZ", - "name": "AIOZ Network", - "contractAddress": "0x626E8036dEB333b408Be468F951bdB42433cBF18", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x00a8b738E453fFd858a7edf03bcCfe20412f0Eb0/logo.png", - "symbol": "ALBT", - "name": "AllianceBlock Token", - "contractAddress": "0x00a8b738E453fFd858a7edf03bcCfe20412f0Eb0", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "AMLT", - "name": "AMLT by Coinfirm", - "contractAddress": "0xca0e7269600d353f70b14ad118a49575455c0f2f", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x95a4492F028aa1fd432Ea71146b433E7B4446611/logo.png", - "symbol": "APY", - "name": "APY Governance Token", - "contractAddress": "0x95a4492F028aa1fd432Ea71146b433E7B4446611", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "symbol": "ARN", - "name": "Aeron", - "contractAddress": "0xBA5F11b16B155792Cf3B2E6880E8706859A8AEB6", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 4, - "symbol": "AST", - "name": "Airswap", - "contractAddress": "0x27054b13b1b798b345b591a4d22e6562d47ea75a", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "AVM", - "name": "AVM Ecosystem", - "contractAddress": "0x74004a7227615fb52b82d17ffabfa376907d8a4d", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "ELF", - "name": "Aelf", - "contractAddress": "0xbf2179859fc6d5bee9bf9158632dc51678a4100e", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "AMB", - "name": "Amber", - "contractAddress": "0x4dc3643dbc642b72c158e7f3d2ff232df61cb6ce", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xfF20817765cB7f73d4bde2e66e067E58D11095C2/logo.png", - "symbol": "AMP", - "name": "Amp", - "contractAddress": "0xfF20817765cB7f73d4bde2e66e067E58D11095C2", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 9, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets//logo.png", - "symbol": "AMPL", - "name": "Ampleforth", - "contractAddress": "0xD46bA6D942050d489DBd938a2C909A5d5039A161", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa117000000f279D81A1D3cc75430fAA017FA5A2e/logo.png", - "symbol": "ANT", - "name": "Aragon Network Token", - "contractAddress": "0xa117000000f279D81A1D3cc75430fAA017FA5A2e", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "APPC", - "name": "AppCoins", - "contractAddress": "0x1a7a8bd9106f2b8d977e08582dc7d24c723ab0db", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1337DEF16F9B486fAEd0293eb623Dc8395dFE46a/logo.png", - "symbol": "ARMOR", - "name": "Armor", - "contractAddress": "0x1337DEF16F9B486fAEd0293eb623Dc8395dFE46a", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 0, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xdacD69347dE42baBfAEcD09dC88958378780FB62/logo.png", - "symbol": "ATRI", - "name": "AtariToken", - "contractAddress": "0xdacD69347dE42baBfAEcD09dC88958378780FB62", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "ATMI", - "name": "Atonomi", - "contractAddress": "0x97aeb5066e1a590e868b511457beb6fe99d329f5", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x18aAA7115705e8be94bfFEBDE57Af9BFc265B998/logo.png", - "symbol": "AUDIO", - "name": "Audius", - "contractAddress": "0x18aAA7115705e8be94bfFEBDE57Af9BFc265B998", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "AUTO", - "name": "CUBE", - "contractAddress": "0x622dFfCc4e83C64ba959530A5a5580687a57581b", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xF5D669627376EBd411E34b98F19C868c8ABA5ADA/logo.png", - "symbol": "AXS", - "name": "Axie Infinity", - "contractAddress": "0xF5D669627376EBd411E34b98F19C868c8ABA5ADA", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1C9491865a1DE77C5b6e19d2E6a5F1D7a6F2b25F/logo.png", - "symbol": "MATTER", - "name": "Antimatter.Finance Governance Token", - "contractAddress": "0x1C9491865a1DE77C5b6e19d2E6a5F1D7a6F2b25F", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x71F85B2E46976bD21302B64329868fd15eb0D127/logo.png", - "symbol": "AXN", - "name": "Axion", - "contractAddress": "0x71F85B2E46976bD21302B64329868fd15eb0D127", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xba100000625a3754423978a60c9317c58a424e3D/logo.png", - "symbol": "BAL", - "name": "Balancer", - "contractAddress": "0xba100000625a3754423978a60c9317c58a424e3D", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "BIX", - "name": "BIX Token", - "contractAddress": "0xb3104b4b9da82025e8b9f8fb28b3553ce2f67069", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5Dc02Ea99285E17656b8350722694c35154DB1E8/logo.png", - "symbol": "BOND", - "name": "BOND", - "contractAddress": "0x5Dc02Ea99285E17656b8350722694c35154DB1E8", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "BRD", - "name": "BRD Token", - "contractAddress": "0x558ec3152e2eb2174905cd19aea4e34a23de9ad6", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "BNT", - "name": "Bancor", - "contractAddress": "0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xBA11D00c5f74255f56a5E366F4F77f5A186d7f55/logo.png", - "symbol": "BAND", - "name": "BandToken", - "contractAddress": "0xBA11D00c5f74255f56a5E366F4F77f5A186d7f55", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x54C9EA2E9C9E8eD865Db4A4ce6711C2a0d5063Ba/logo.png", - "symbol": "BART", - "name": "BarterTrade", - "contractAddress": "0x54C9EA2E9C9E8eD865Db4A4ce6711C2a0d5063Ba", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 9, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x07150e919B4De5fD6a63DE1F9384828396f25fDC/logo.png", - "symbol": "BASE", - "name": "Base Protocol", - "contractAddress": "0x07150e919B4De5fD6a63DE1F9384828396f25fDC", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0D8775F648430679A709E98d2b0Cb6250d2887EF/logo.png", - "symbol": "BAT", - "name": "Basic Attention Token", - "contractAddress": "0x0D8775F648430679A709E98d2b0Cb6250d2887EF", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA91ac63D040dEB1b7A5E4d4134aD23eb0ba07e14/logo.png", - "symbol": "BEL", - "name": "Bella", - "contractAddress": "0xA91ac63D040dEB1b7A5E4d4134aD23eb0ba07e14", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "BNB", - "name": "Binance Coin", - "contractAddress": "0xb8c77482e45f1f44de1745f52c74426c631bdd52", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "BUSD", - "name": "Binance USD", - "contractAddress": "0x4fabb145d64652a948d72533023f6e7a623c7c53", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 9, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x67c597624B17b16fb77959217360B7cD18284253/logo.png", - "symbol": "MARK", - "name": "Benchmark", - "contractAddress": "0x67c597624B17b16fb77959217360B7cD18284253", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7671904eed7f10808B664fc30BB8693FD7237abF/logo.png", - "symbol": "BBR", - "name": "BitberryToken", - "contractAddress": "0x7671904eed7f10808B664fc30BB8693FD7237abF", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xCF3C8Be2e2C42331Da80EF210e9B1b307C03d36A/logo.png", - "symbol": "BEPRO", - "name": "BetProtocolToken", - "contractAddress": "0xCF3C8Be2e2C42331Da80EF210e9B1b307C03d36A", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0c7D5ae016f806603CB1782bEa29AC69471CAb9c/logo.png", - "symbol": "BFC", - "name": "Bifrost", - "contractAddress": "0x0c7D5ae016f806603CB1782bEa29AC69471CAb9c", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9BE89D2a4cd102D8Fecc6BF9dA793be995C22541/logo.png", - "symbol": "BBTC", - "name": "Binance Wrapped BTC", - "contractAddress": "0x9BE89D2a4cd102D8Fecc6BF9dA793be995C22541", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x25e1474170c4c0aA64fa98123bdc8dB49D7802fa/logo.png", - "symbol": "BID", - "name": "Bidao", - "contractAddress": "0x25e1474170c4c0aA64fa98123bdc8dB49D7802fa", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x70401dFD142A16dC7031c56E862Fc88Cb9537Ce0/logo.png", - "symbol": "BIRD", - "name": "Bird.Money", - "contractAddress": "0x70401dFD142A16dC7031c56E862Fc88Cb9537Ce0", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xAec7e1f531Bb09115103C53ba76829910Ec48966/logo.png", - "symbol": "BLANK", - "name": "Blank Token", - "contractAddress": "0xAec7e1f531Bb09115103C53ba76829910Ec48966", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "BLZ", - "name": "Bluzelle", - "contractAddress": "0x5732046a883704404f284ce41ffadd5b007fd668", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x725C263e32c72dDC3A19bEa12C5a0479a81eE688/logo.png", - "symbol": "BMI", - "name": "Bridge Mutual", - "contractAddress": "0x725C263e32c72dDC3A19bEa12C5a0479a81eE688", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xD2dDa223b2617cB616c1580db421e4cFAe6a8a85/logo.png", - "symbol": "BONDLY", - "name": "Bondly Token", - "contractAddress": "0xD2dDa223b2617cB616c1580db421e4cFAe6a8a85", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "symbol": "BTM", - "name": "Bytom", - "contractAddress": "0xcb97e65f07da24d46bcdd078ebebd7c6e6e3d750", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "CANDY", - "name": "Candy", - "contractAddress": "0xf2eab3a2034d3f6b63734d2e08262040e3ff7b48", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x954b890704693af242613edEf1B603825afcD708/logo.png", - "symbol": "CARD", - "name": "Cardstack", - "contractAddress": "0x954b890704693af242613edEf1B603825afcD708", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "CAS", - "name": "Cashaa", - "contractAddress": "0xe8780b48bdb05f928697a5e8155f672ed91462f7", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "CCC", - "name": "Container Crypto Coin", - "contractAddress": "0x9e3359f862b6c7f5c660cfd6d1aa6909b1d9504d", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x17aC188e09A7890a1844E5E65471fE8b0CcFadF3/logo.png", - "symbol": "CC10", - "name": "Cryptocurrency Top 10 Tokens Index", - "contractAddress": "0x17aC188e09A7890a1844E5E65471fE8b0CcFadF3", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x26c8AFBBFE1EBaca03C2bB082E69D0476Bffe099/logo.png", - "symbol": "CELL", - "name": "Cellframe Token", - "contractAddress": "0x26c8AFBBFE1EBaca03C2bB082E69D0476Bffe099", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 4, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xaaAEBE6Fe48E54f431b0C390CfaF0b017d09D42d/logo.png", - "symbol": "CEL", - "name": "Celsius", - "contractAddress": "0xaaAEBE6Fe48E54f431b0C390CfaF0b017d09D42d", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "CFI", - "name": "Cofound.it", - "contractAddress": "0x12FEF5e57bF45873Cd9B62E9DBd7BFb99e32D73e", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC4C2614E694cF534D407Ee49F8E44D125E4681c4/logo.png", - "symbol": "CHAIN", - "name": "Chain Games", - "contractAddress": "0xC4C2614E694cF534D407Ee49F8E44D125E4681c4", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x63b4f3e3fa4e438698CE330e365E831F7cCD1eF4/logo.png", - "symbol": "CFi", - "name": "CyberFi Token", - "contractAddress": "0x63b4f3e3fa4e438698CE330e365E831F7cCD1eF4", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "CNN", - "name": "CNN Token", - "contractAddress": "0x8713d26637cf49e1b6b4a7ce57106aabc9325343", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xDDB3422497E61e13543BeA06989C0789117555c5/logo.png", - "symbol": "COTI", - "name": "COTI Token", - "contractAddress": "0xDDB3422497E61e13543BeA06989C0789117555c5", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "CSP", - "name": "Caspian", - "contractAddress": "0xa6446d655a0c34bc4f05042ee88170d056cbaf45", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1fE24F25b1Cf609B9c4e7E12D802e3640dFA5e43/logo.png", - "symbol": "CGG", - "name": "ChainGuardians Governance Token", - "contractAddress": "0x1fE24F25b1Cf609B9c4e7E12D802e3640dFA5e43", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "CMT", - "name": "CyberMiles", - "contractAddress": "0xf85fEea2FdD81d51177F6b8F35F0e6734Ce45F5F", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "CND", - "name": "Cindicator", - "contractAddress": "0xd4c435f5b09f855c3317c8524cb1f586e42795fa", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "symbol": "CVC", - "name": "Civic", - "contractAddress": "0x41e5560054824ea6b0732e656e3ad64e20e94e45", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x69692D3345010a207b759a7D1af6fc7F38b35c5E/logo.png", - "symbol": "CHADS", - "name": "chads.vc", - "contractAddress": "0x69692D3345010a207b759a7D1af6fc7F38b35c5E", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x514910771AF9Ca656af840dff83E8264EcF986CA/logo.png", - "symbol": "Link", - "name": "ChainLink", - "contractAddress": "0x514910771AF9Ca656af840dff83E8264EcF986CA", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "symbol": "TIME", - "name": "Chronobank", - "contractAddress": "0x6531f133e6DeeBe7F2dcE5A0441aA7ef330B4e53", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xE61fDAF474Fac07063f2234Fb9e60C1163Cfa850/logo.png", - "symbol": "COIN", - "name": "Coin Utility Token", - "contractAddress": "0xE61fDAF474Fac07063f2234Fb9e60C1163Cfa850", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xc00e94Cb662C3520282E6f5717214004A7f26888/logo.png", - "symbol": "COMP", - "name": "Compound", - "contractAddress": "0xc00e94Cb662C3520282E6f5717214004A7f26888", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x38e4adB44ef08F22F5B5b76A8f0c2d0dCbE7DcA1/logo.png", - "symbol": "CVP", - "name": "Concentrated Voting Power", - "contractAddress": "0x38e4adB44ef08F22F5B5b76A8f0c2d0dCbE7DcA1", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xc834Fa996fA3BeC7aAD3693af486ae53D8aA8B50/logo.png", - "symbol": "CONV", - "name": "Convergence", - "contractAddress": "0xc834Fa996fA3BeC7aAD3693af486ae53D8aA8B50", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x62359Ed7505Efc61FF1D56fEF82158CcaffA23D7/logo.png", - "symbol": "CORE", - "name": "cVault.finance", - "contractAddress": "0x62359Ed7505Efc61FF1D56fEF82158CcaffA23D7", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x2ba592F78dB6436527729929AAf6c908497cB200/logo.png", - "symbol": "CREAM", - "name": "Cream", - "contractAddress": "0x2ba592F78dB6436527729929AAf6c908497cB200", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 6, - "symbol": "CS", - "name": "Credits", - "contractAddress": "0x46b9ad944d1059450da1163511069c718f699d31", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xaC0104Cca91D167873B8601d2e71EB3D4D8c33e0/logo.png", - "symbol": "CWS", - "name": "Crowns", - "contractAddress": "0xaC0104Cca91D167873B8601d2e71EB3D4D8c33e0", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "CRPT", - "name": "Crypterium", - "contractAddress": "0x08389495d7456e1951ddf7c3a1314a4bfb646d8b", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0f51bb10119727a7e5eA3538074fb341F56B09Ad/logo.png", - "symbol": "DAO", - "name": "DAO Maker Token", - "contractAddress": "0x0f51bb10119727a7e5eA3538074fb341F56B09Ad", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x26CE25148832C04f3d7F26F32478a9fe55197166/logo.png", - "symbol": "DEXT", - "name": "DEXTools", - "contractAddress": "0x26CE25148832C04f3d7F26F32478a9fe55197166", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6B175474E89094C44Da98b954EedeAC495271d0F/logo.png", - "symbol": "DAI", - "name": "Dai Stablecoin", - "contractAddress": "0x6B175474E89094C44Da98b954EedeAC495271d0F", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xFbEEa1C75E4c4465CB2FCCc9c6d6afe984558E20/logo.png", - "symbol": "DDIM", - "name": "DuckDaoDime", - "contractAddress": "0xFbEEa1C75E4c4465CB2FCCc9c6d6afe984558E20", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xE1c7E30C42C24582888C758984f6e382096786bd/logo.png", - "symbol": "XCUR", - "name": "Curate", - "contractAddress": "0xE1c7E30C42C24582888C758984f6e382096786bd", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xD533a949740bb3306d119CC777fa900bA034cd52/logo.png", - "symbol": "CRV", - "name": "Curve DAO Token", - "contractAddress": "0xD533a949740bb3306d119CC777fa900bA034cd52", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x30f271C9E86D2B7d00a6376Cd96A1cFBD5F0b9b3/logo.png", - "symbol": "DEC", - "name": "Decentr", - "contractAddress": "0x30f271C9E86D2B7d00a6376Cd96A1cFBD5F0b9b3", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9EA3b5b4EC044b70375236A281986106457b20EF/logo.png", - "symbol": "DELTA", - "name": "Delta Financial", - "contractAddress": "0x9EA3b5b4EC044b70375236A281986106457b20EF", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "symbol": "DENT", - "name": "DENT", - "contractAddress": "0x3597bfd533a99c9aa083587b074434e61eb0a258", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xd379700999F4805Ce80aa32DB46A94dF64561108/logo.png", - "symbol": "DETS", - "name": "Dextrust", - "contractAddress": "0xd379700999F4805Ce80aa32DB46A94dF64561108", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5cAf454Ba92e6F2c929DF14667Ee360eD9fD5b26/logo.png", - "symbol": "DEV", - "name": "Dev", - "contractAddress": "0x5cAf454Ba92e6F2c929DF14667Ee360eD9fD5b26", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5F64Ab1544D28732F0A24F4713c2C8ec0dA089f0/logo.png", - "symbol": "DEXTF", - "name": "DEXTF Token", - "contractAddress": "0x5F64Ab1544D28732F0A24F4713c2C8ec0dA089f0", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xc719d010B63E5bbF2C0551872CD5316ED26AcD83/logo.png", - "symbol": "DIP", - "name": "Decentralized Insurance Protocol", - "contractAddress": "0xc719d010B63E5bbF2C0551872CD5316ED26AcD83", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x26E43759551333e57F073bb0772F50329A957b30/logo.png", - "symbol": "DGVC", - "name": "DegenVC", - "contractAddress": "0x26E43759551333e57F073bb0772F50329A957b30", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x152687Bc4A7FCC89049cF119F9ac3e5aCF2eE7ef/logo.png", - "symbol": "DHC", - "name": "DeltaHub Community", - "contractAddress": "0x152687Bc4A7FCC89049cF119F9ac3e5aCF2eE7ef", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x431ad2ff6a9C365805eBaD47Ee021148d6f7DBe0/logo.png", - "symbol": "DF", - "name": "dForce", - "contractAddress": "0x431ad2ff6a9C365805eBaD47Ee021148d6f7DBe0", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 9, - "symbol": "DGD", - "name": "DigixDAO", - "contractAddress": "0xE0B7927c4aF23765Cb51314A0E0521A9645F0E2A", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x88EF27e69108B2633F8E1C184CC37940A075cC02/logo.png", - "symbol": "DEGO", - "name": "dego.finance", - "contractAddress": "0x88EF27e69108B2633F8E1C184CC37940A075cC02", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x84cA8bc7997272c7CfB4D0Cd3D55cd942B3c9419/logo.png", - "symbol": "DIA", - "name": "DIA", - "contractAddress": "0x84cA8bc7997272c7CfB4D0Cd3D55cd942B3c9419", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xEd91879919B71bB6905f23af0A68d231EcF87b14/logo.png", - "symbol": "DMG", - "name": "DMM: Governance", - "contractAddress": "0xEd91879919B71bB6905f23af0A68d231EcF87b14", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9cEB84f92A0561fa3Cc4132aB9c0b76A59787544/logo.png", - "symbol": "DOKI", - "name": "DokiDokiFinance", - "contractAddress": "0x9cEB84f92A0561fa3Cc4132aB9c0b76A59787544", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "DOV", - "name": "Dovu", - "contractAddress": "0xac3211a5025414af2866ff09c23fc18bc97e79b1", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "DRGN", - "name": "Dragon", - "contractAddress": "0x419c4db4b9e25d6db2ad9691ccb832c8d9fda05e", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "DNT", - "name": "district0x", - "contractAddress": "0x0abdace70d3790235af448c88547603b945604ea", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x3aFfCCa64c2A6f4e3B6Bd9c64CD2C969EFd1ECBe/logo.png", - "symbol": "DSLA", - "name": "DSLA", - "contractAddress": "0x3aFfCCa64c2A6f4e3B6Bd9c64CD2C969EFd1ECBe", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC0bA369c8Db6eB3924965e5c4FD0b4C1B91e305F/logo.png", - "symbol": "DUCK", - "name": "DLP Duck Token", - "contractAddress": "0xC0bA369c8Db6eB3924965e5c4FD0b4C1B91e305F", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x961C8c0B1aaD0c0b10a51FeF6a867E3091BCef17/logo.png", - "symbol": "DYP", - "name": "DeFiYieldProtocol", - "contractAddress": "0x961C8c0B1aaD0c0b10a51FeF6a867E3091BCef17", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x740623d2c797b7D8D1EcB98e9b4Afcf99Ec31E14/logo.png", - "symbol": "DYT", - "name": "DoYourTip", - "contractAddress": "0x740623d2c797b7D8D1EcB98e9b4Afcf99Ec31E14", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "EOS", - "name": "EOS", - "contractAddress": "0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xAa6E8127831c9DE45ae56bB1b0d4D4Da6e5665BD/logo.png", - "symbol": "ETH2x-FLI", - "name": "ETH 2x Flexible Leverage Index", - "contractAddress": "0xAa6E8127831c9DE45ae56bB1b0d4D4Da6e5665BD", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 2, - "symbol": "EUR.AVM", - "name": "EUR AVM", - "contractAddress": "0xF01Cd2f1c9E42c509d309aC8C5b29B6dA8E64b1a", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 0, - "symbol": "EDG", - "name": "Edgeless", - "contractAddress": "0x08711D3B02C8758F2FB3ab4e80228418a7F8e39c", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "EDO", - "name": "Eidoo", - "contractAddress": "0xced4e93198734ddaff8492d525bd258d49eb388e", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x178c820f862B14f316509ec36b13123DA19A6054/logo.png", - "symbol": "EWTB", - "name": "Energy Web Token Bridged", - "contractAddress": "0x178c820f862B14f316509ec36b13123DA19A6054", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xF629cBd94d3791C9250152BD8dfBDF380E2a3B9c/logo.png", - "symbol": "ENJ", - "name": "Enjin Coin", - "contractAddress": "0xF629cBd94d3791C9250152BD8dfBDF380E2a3B9c", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa0246c9032bC3A600820415aE600c6388619A14D/logo.png", - "symbol": "FARM", - "name": "FARM Reward Token", - "contractAddress": "0xa0246c9032bC3A600820415aE600c6388619A14D", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 6, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xE5CAeF4Af8780E59Df925470b050Fb23C43CA68C/logo.png", - "symbol": "FRM", - "name": "Ferrum Network Token", - "contractAddress": "0xE5CAeF4Af8780E59Df925470b050Fb23C43CA68C", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "FXC", - "name": "Flexacoin", - "contractAddress": "0x4a57E687b9126435a9B19E4A802113e266AdeBde", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8a40c222996f9F3431f63Bf80244C36822060f12/logo.png", - "symbol": "FXF", - "name": "Finxflo", - "contractAddress": "0x8a40c222996f9F3431f63Bf80244C36822060f12", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x853d955aCEf822Db058eb8505911ED77F175b99e/logo.png", - "symbol": "FRAX", - "name": "Frax", - "contractAddress": "0x853d955aCEf822Db058eb8505911ED77F175b99e", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 9, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x29502fE4d233EF0b45C3647101Fa1252cE0634BD/logo.png", - "symbol": "FROGE", - "name": "Froge Finance", - "contractAddress": "0x29502fE4d233EF0b45C3647101Fa1252cE0634BD", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b/logo.png", - "symbol": "FUN", - "name": "FunFair", - "contractAddress": "0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8c15Ef5b4B21951d50E53E4fbdA8298FFAD25057/logo.png", - "symbol": "FX", - "name": "Function X", - "contractAddress": "0x8c15Ef5b4B21951d50E53E4fbdA8298FFAD25057", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6BFf2fE249601ed0Db3a87424a2E923118BB0312/logo.png", - "symbol": "FYZ", - "name": "Fyooz", - "contractAddress": "0x6BFf2fE249601ed0Db3a87424a2E923118BB0312", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8a854288a5976036A725879164Ca3e91d30c6A1B/logo.png", - "symbol": "GET", - "name": "GET", - "contractAddress": "0x8a854288a5976036A725879164Ca3e91d30c6A1B", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 9, - "symbol": "GNX", - "name": "Genaro Network", - "contractAddress": "0x6ec8a24cabdc339a06a172f8223ea557055adaa5", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9F9c8ec3534c3cE16F928381372BfbFBFb9F4D24/logo.png", - "symbol": "GLQ", - "name": "GraphLinq", - "contractAddress": "0x9F9c8ec3534c3cE16F928381372BfbFBFb9F4D24", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6810e776880C02933D47DB1b9fc05908e5386b96/logo.png", - "symbol": "GNO", - "name": "Gnosis Token", - "contractAddress": "0x6810e776880C02933D47DB1b9fc05908e5386b96", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "GNT", - "name": "Golem", - "contractAddress": "0xa74476443119A942dE498590Fe1f2454d7D4aC0d", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xc3771d47E2Ab5A519E2917E61e23078d0C05Ed7f/logo.png", - "symbol": "GTH", - "name": "Gather", - "contractAddress": "0xc3771d47E2Ab5A519E2917E61e23078d0C05Ed7f", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 2, - "symbol": "GUSD", - "name": "Gemini Dollar", - "contractAddress": "0x056fd409e1d7a124bd7017459dfea2f387b6d5cd", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "GVT", - "name": "Genesis Vision", - "contractAddress": "0x103c3A209da59d3E7C4A89307e66521e081CFDF0", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xbEa98c05eEAe2f3bC8c3565Db7551Eb738c8CCAb/logo.png", - "symbol": "GYSR", - "name": "Geyser", - "contractAddress": "0xbEa98c05eEAe2f3bC8c3565Db7551Eb738c8CCAb", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 0, - "symbol": "GD", - "name": "Zilla GD", - "contractAddress": "0xb4cebdb66b3a5183fd4764a25cad1fc109535016", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x09e64c2B61a5f1690Ee6fbeD9baf5D6990F8dFd0/logo.png", - "symbol": "GRO", - "name": "Growth", - "contractAddress": "0x09e64c2B61a5f1690Ee6fbeD9baf5D6990F8dFd0", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xc944E90C64B2c07662A292be6244BDf05Cda44a7/logo.png", - "symbol": "GRT", - "name": "Graph Token", - "contractAddress": "0xc944E90C64B2c07662A292be6244BDf05Cda44a7", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 3, - "symbol": "GUP", - "name": "Matchpool", - "contractAddress": "0xf7B098298f7C69Fc14610bf71d5e02c60792894C", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39/logo.png", - "symbol": "HEX", - "name": "HEX", - "contractAddress": "0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "HPB", - "name": "HPBCoin", - "contractAddress": "0x38c6a68304cdefb9bec48bbfaaba5c5b47818bb2", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x584bC13c7D411c00c01A62e8019472dE68768430/logo.png", - "symbol": "HEGIC", - "name": "Hegic", - "contractAddress": "0x584bC13c7D411c00c01A62e8019472dE68768430", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xEEF9f339514298C6A857EfCfC1A762aF84438dEE/logo.png", - "symbol": "HEZ", - "name": "Hermez Network Token", - "contractAddress": "0xEEF9f339514298C6A857EfCfC1A762aF84438dEE", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6c6EE5e31d828De241282B9606C8e98Ea48526E2/logo.png", - "symbol": "HOT", - "name": "HoloToken", - "contractAddress": "0x6c6EE5e31d828De241282B9606C8e98Ea48526E2", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "HYDRO", - "name": "Hydro", - "contractAddress": "0xebbdf302c940c6bfd49c6b165f457fdb324649bc", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "ICX", - "name": "ICON", - "contractAddress": "0xb5a5f22694352c15b00323844ad545abb2b11028", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "ICN", - "name": "ICONOMI", - "contractAddress": "0x888666CA69E0f178DED6D75b5726Cee99A87D698", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xEBd9D99A3982d547C5Bb4DB7E3b1F9F14b67Eb83/logo.png", - "symbol": "ID", - "name": "Everest ID", - "contractAddress": "0xEBd9D99A3982d547C5Bb4DB7E3b1F9F14b67Eb83", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x72630B1e3B42874bf335020Ba0249e3E9e47Bafc/logo.png", - "symbol": "EPAN", - "name": "Paypolitan Token", - "contractAddress": "0x72630B1e3B42874bf335020Ba0249e3E9e47Bafc", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "symbol": "GBX", - "name": "Globitex", - "contractAddress": "0x12fcd6463e66974cf7bbc24ffc4d40d6be458283", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xf6537FE0df7F0Cc0985Cf00792CC98249E73EFa0/logo.png", - "symbol": "GIV", - "name": "GIVToken", - "contractAddress": "0xf6537FE0df7F0Cc0985Cf00792CC98249E73EFa0", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xeEAA40B28A2d1b0B08f6f97bB1DD4B75316c6107/logo.png", - "symbol": "GOVI", - "name": "GOVI", - "contractAddress": "0xeEAA40B28A2d1b0B08f6f97bB1DD4B75316c6107", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 5, - "symbol": "GTO", - "name": "Gifto", - "contractAddress": "0xc5bbae50781be1669306b9e001eff57a2957b09d", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "IOST", - "name": "IOStoken", - "contractAddress": "0xfa1a856cfa3409cfa145fa4e20eb270df3eb21ab", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "ITC", - "name": "IOT Chain", - "contractAddress": "0x5e6b6d9abad9093fdc861ea1600eba1b355cd940", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xe28b3B32B6c345A34Ff64674606124Dd5Aceca30/logo.png", - "symbol": "INJ", - "name": "Injective Token", - "contractAddress": "0xe28b3B32B6c345A34Ff64674606124Dd5Aceca30", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4B1E80cAC91e2216EEb63e29B957eB91Ae9C2Be8/logo.png", - "symbol": "JUP", - "name": "Jupiter", - "contractAddress": "0x4B1E80cAC91e2216EEb63e29B957eB91Ae9C2Be8", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x85Eee30c52B0b379b046Fb0F85F4f3Dc3009aFEC/logo.png", - "symbol": "KEEP", - "name": "KEEP Token", - "contractAddress": "0x85Eee30c52B0b379b046Fb0F85F4f3Dc3009aFEC", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 6, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x16980b3B4a3f9D89E33311B5aa8f80303E5ca4F8/logo.png", - "symbol": "KEX", - "name": "KIRA Network", - "contractAddress": "0x16980b3B4a3f9D89E33311B5aa8f80303E5ca4F8", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "KIN", - "name": "Kin", - "contractAddress": "0x818Fc6C2Ec5986bc6E2CBf00939d90556aB12ce5", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xCbfef8fdd706cde6F208460f2Bf39Aa9c785F05D/logo.png", - "symbol": "KINE", - "name": "Kine Governance Token", - "contractAddress": "0xCbfef8fdd706cde6F208460f2Bf39Aa9c785F05D", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1cEB5cB57C4D4E2b2433641b95Dd330A33185A44/logo.png", - "symbol": "KP3R", - "name": "Keep3rV1", - "contractAddress": "0x1cEB5cB57C4D4E2b2433641b95Dd330A33185A44", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1dD80016e3d4ae146Ee2EBB484e8edD92dacC4ce/logo.png", - "symbol": "LEAD", - "name": "Lead Token", - "contractAddress": "0x1dD80016e3d4ae146Ee2EBB484e8edD92dacC4ce", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0fF6ffcFDa92c53F615a4A75D982f399C989366b/logo.png", - "symbol": "LAYER", - "name": "Unilayer", - "contractAddress": "0x0fF6ffcFDa92c53F615a4A75D982f399C989366b", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xBBbbCA6A901c926F240b89EacB641d8Aec7AEafD/logo.png", - "symbol": "LRC", - "name": "LoopringCoin V2", - "contractAddress": "0xBBbbCA6A901c926F240b89EacB641d8Aec7AEafD", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x3593D125a4f7849a1B059E64F4517A86Dd60c95d/logo.png", - "symbol": "OM", - "name": "MANTRA DAO", - "contractAddress": "0x3593D125a4f7849a1B059E64F4517A86Dd60c95d", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "MAN", - "name": "MATRIX AI Network", - "contractAddress": "0xe25bcec5d3801ce3a794079bf94adf1b8ccd802d", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "symbol": "MYST", - "name": "Mysterium", - "contractAddress": "0xa645264C5603E96c3b0B078cdab68733794B0A71", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "NULS", - "name": "Nuls", - "contractAddress": "0xb91318f35bdb262e9423bc7c7c2a3a93dd93c92c", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 9, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xfAd45E47083e4607302aa43c65fB3106F1cd7607/logo.png", - "symbol": "HOGE", - "name": "hoge.finance", - "contractAddress": "0xfAd45E47083e4607302aa43c65fB3106F1cd7607", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "symbol": "IXT", - "name": "iXledger", - "contractAddress": "0xfca47962d45adfdfd1ab2d972315db4ce7ccf094", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB9d99C33eA2d86EC5eC6b8A4dD816EBBA64404AF/logo.png", - "symbol": "K21", - "name": "k21.kanon.art", - "contractAddress": "0xB9d99C33eA2d86EC5eC6b8A4dD816EBBA64404AF", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0F5D2fB29fb7d3CFeE444a200298f468908cC942/logo.png", - "symbol": "MANA", - "name": "Decentraland MANA", - "contractAddress": "0x0F5D2fB29fb7d3CFeE444a200298f468908cC942", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1494CA1F11D487c2bBe4543E90080AeBa4BA3C2b/logo.png", - "symbol": "DPI", - "name": "DefiPulse Index", - "contractAddress": "0x1494CA1F11D487c2bBe4543E90080AeBa4BA3C2b", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "symbol": "ENG", - "name": "Enigma", - "contractAddress": "0xf0ee6b27b759c9893ce4f094b49ad28fd15a23e4", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 16, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xD6c67B93a7b248dF608a653d82a100556144c5DA/logo.png", - "symbol": "EXNT", - "name": "ExNetwork Community Token", - "contractAddress": "0xD6c67B93a7b248dF608a653d82a100556144c5DA", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xfffffffFf15AbF397dA76f1dcc1A1604F45126DB/logo.png", - "symbol": "FSW", - "name": "FalconSwap Token", - "contractAddress": "0xfffffffFf15AbF397dA76f1dcc1A1604F45126DB", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xF4d861575ecC9493420A3f5a14F85B13f0b50EB3/logo.png", - "symbol": "FCL", - "name": "Fractal Protocol Token", - "contractAddress": "0xF4d861575ecC9493420A3f5a14F85B13f0b50EB3", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x956F47F50A910163D8BF957Cf5846D573E7f87CA/logo.png", - "symbol": "FEI", - "name": "Fei USD", - "contractAddress": "0x956F47F50A910163D8BF957Cf5846D573E7f87CA", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "FSN", - "name": "Fusion", - "contractAddress": "0xd0352a019e9ab9d757776f532377aaebd36fd541", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xdd974D5C2e2928deA5F71b9825b8b646686BD200/logo.png", - "symbol": "KNC", - "name": "Kyber Network Crystal", - "contractAddress": "0xdd974D5C2e2928deA5F71b9825b8b646686BD200", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x67B6D479c7bB412C54e03dCA8E1Bc6740ce6b99C/logo.png", - "symbol": "KYL", - "name": "Kylin Network", - "contractAddress": "0x67B6D479c7bB412C54e03dCA8E1Bc6740ce6b99C", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xaE697F994Fc5eBC000F8e22EbFfeE04612f98A0d/logo.png", - "symbol": "LGCY", - "name": "LGCY Network", - "contractAddress": "0xaE697F994Fc5eBC000F8e22EbFfeE04612f98A0d", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0a50C93c762fDD6E56D86215C24AaAD43aB629aa/logo.png", - "symbol": "LGO", - "name": "LGO Token", - "contractAddress": "0x0a50C93c762fDD6E56D86215C24AaAD43aB629aa", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6149C26Cd2f7b5CCdb32029aF817123F6E37Df5B/logo.png", - "symbol": "LPOOL", - "name": "Launchpool token", - "contractAddress": "0x6149C26Cd2f7b5CCdb32029aF817123F6E37Df5B", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x80fB784B7eD66730e8b1DBd9820aFD29931aab03/logo.png", - "symbol": "LEND", - "name": "Lend", - "contractAddress": "0x80fB784B7eD66730e8b1DBd9820aFD29931aab03", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0000000000095413afC295d19EDeb1Ad7B71c952/logo.png", - "symbol": "LON", - "name": "Tokenlon", - "contractAddress": "0x0000000000095413afC295d19EDeb1Ad7B71c952", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4de2573e27E648607B50e1Cfff921A33E4A34405/logo.png", - "symbol": "LST", - "name": "Lendroid Support Token", - "contractAddress": "0x4de2573e27E648607B50e1Cfff921A33E4A34405", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "LOOM", - "name": "Loom", - "contractAddress": "0xa4e8c3ec456107ea67d3075bf9e3df3a75823db0", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x3DB6Ba6ab6F95efed1a6E794caD492fAAabF294D/logo.png", - "symbol": "LTO", - "name": "LTO Network Token", - "contractAddress": "0x3DB6Ba6ab6F95efed1a6E794caD492fAAabF294D", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA8b919680258d369114910511cc87595aec0be6D/logo.png", - "symbol": "LYXe", - "name": "LUKSO Token", - "contractAddress": "0xA8b919680258d369114910511cc87595aec0be6D", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa393473d64d2F9F026B60b6Df7859A689715d092/logo.png", - "symbol": "LTX", - "name": "Lattice Token", - "contractAddress": "0xa393473d64d2F9F026B60b6Df7859A689715d092", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "LUN", - "name": "Lunyr", - "contractAddress": "0xfa05A73FfE78ef8f1a739473e462c54bae6567D9", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xd2877702675e6cEb975b4A1dFf9fb7BAF4C91ea9/logo.png", - "symbol": "LUNA", - "name": "Wrapped LUNA Token", - "contractAddress": "0xd2877702675e6cEb975b4A1dFf9fb7BAF4C91ea9", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5f98805A4E8be255a32880FDeC7F6728C6568bA0/logo.png", - "symbol": "LUSD", - "name": "LUSD Stablecoin", - "contractAddress": "0x5f98805A4E8be255a32880FDeC7F6728C6568bA0", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB4d930279552397bbA2ee473229f89Ec245bc365/logo.png", - "symbol": "MAHA", - "name": "MahaDAO", - "contractAddress": "0xB4d930279552397bbA2ee473229f89Ec245bc365", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xfC98e825A2264D890F9a1e68ed50E1526abCcacD/logo.png", - "symbol": "MCO2", - "name": "Moss Carbon Credit", - "contractAddress": "0xfC98e825A2264D890F9a1e68ed50E1526abCcacD", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xAd4f86a25bbc20FfB751f2FAC312A0B4d8F88c64/logo.png", - "symbol": "ROOM", - "name": "OptionRoom Token", - "contractAddress": "0xAd4f86a25bbc20FfB751f2FAC312A0B4d8F88c64", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5a666c7d92E5fA7Edcb6390E4efD6d0CDd69cF37/logo.png", - "symbol": "MARSH", - "name": "UnmarshalToken", - "contractAddress": "0x5a666c7d92E5fA7Edcb6390E4efD6d0CDd69cF37", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x69af81e73A73B40adF4f3d4223Cd9b1ECE623074/logo.png", - "symbol": "MASK", - "name": "Mask Network", - "contractAddress": "0x69af81e73A73B40adF4f3d4223Cd9b1ECE623074", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4e352cF164E64ADCBad318C3a1e222E9EBa4Ce42/logo.png", - "symbol": "MCB", - "name": "MCDEX Token", - "contractAddress": "0x4e352cF164E64ADCBad318C3a1e222E9EBa4Ce42", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0/logo.png", - "symbol": "MATIC", - "name": "Matic Token", - "contractAddress": "0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xD5525D397898e5502075Ea5E830d8914f6F0affe/logo.png", - "symbol": "MEME", - "name": "MEME", - "contractAddress": "0xD5525D397898e5502075Ea5E830d8914f6F0affe", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2/logo.png", - "symbol": "MKR", - "name": "MakerDAO", - "contractAddress": "0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa3BeD4E1c75D00fa6f4E5E6922DB7261B5E9AcD2/logo.png", - "symbol": "MTA", - "name": "Meta", - "contractAddress": "0xa3BeD4E1c75D00fa6f4E5E6922DB7261B5E9AcD2", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "symbol": "MTL", - "name": "Metal", - "contractAddress": "0xF433089366899D83a9f26A773D59ec7eCF30355e", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x72e364F2ABdC788b7E918bc238B21f109Cd634D7/logo.png", - "symbol": "MVI", - "name": "Metaverse Index", - "contractAddress": "0x72e364F2ABdC788b7E918bc238B21f109Cd634D7", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB26631c6dda06aD89B93C71400D25692de89c068/logo.png", - "symbol": "MINDS", - "name": "Minds", - "contractAddress": "0xB26631c6dda06aD89B93C71400D25692de89c068", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xEA1ea0972fa092dd463f2968F9bB51Cc4c981D71/logo.png", - "symbol": "MOD", - "name": "Modefi", - "contractAddress": "0xEA1ea0972fa092dd463f2968F9bB51Cc4c981D71", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "symbol": "MCO", - "name": "Monaco", - "contractAddress": "0xB63B606Ac810a52cCa15e44bB630fd42D8d1d83d", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "NAS", - "name": "Nebulas", - "contractAddress": "0x5d65D971895Edc438f465c17DB6992698a52318D", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xaAAf91D9b90dF800Df4F55c205fd6989c977E73a/logo.png", - "symbol": "TKN", - "name": "Monolith TKN", - "contractAddress": "0xaAAf91D9b90dF800Df4F55c205fd6989c977E73a", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa8c8CfB141A3bB59FEA1E2ea6B79b5ECBCD7b6ca/logo.png", - "symbol": "NOIA", - "name": "NOIA Token", - "contractAddress": "0xa8c8CfB141A3bB59FEA1E2ea6B79b5ECBCD7b6ca", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB62132e35a6c13ee1EE0f84dC5d40bad8d815206/logo.png", - "symbol": "NEXO", - "name": "Nexo", - "contractAddress": "0xB62132e35a6c13ee1EE0f84dC5d40bad8d815206", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1cBb83EbcD552D5EBf8131eF8c9CD9d9BAB342bC/logo.png", - "symbol": "NFY", - "name": "Non-Fungible Yearn", - "contractAddress": "0x1cBb83EbcD552D5EBf8131eF8c9CD9d9BAB342bC", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "NMR", - "name": "Numeraire", - "contractAddress": "0x1776e1F26f98b1A5dF9cD347953a26dd3Cb46671", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x967da4048cD07aB37855c090aAF366e4ce1b9F48/logo.png", - "symbol": "OCEAN", - "name": "Ocean Protocol", - "contractAddress": "0x967da4048cD07aB37855c090aAF366e4ce1b9F48", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets//logo.png", - "symbol": "OMG", - "name": "OMG Network", - "contractAddress": "0xd26114cd6EE289AccF82350c8d8487fedB8A0C07", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4c11249814f11b9346808179Cf06e71ac328c1b5/logo.png", - "symbol": "ORAI", - "name": "Oraichain Token", - "contractAddress": "0x4c11249814f11b9346808179Cf06e71ac328c1b5", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8207c1FfC5B6804F6024322CcF34F29c3541Ae26/logo.png", - "symbol": "OGN", - "name": "OriginToken", - "contractAddress": "0x8207c1FfC5B6804F6024322CcF34F29c3541Ae26", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0258F474786DdFd37ABCE6df6BBb1Dd5dfC4434a/logo.png", - "symbol": "ORN", - "name": "Orion Protocol", - "contractAddress": "0x0258F474786DdFd37ABCE6df6BBb1Dd5dfC4434a", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "OST", - "name": "OST", - "contractAddress": "0x2C4e8f2D746113d0696cE89B35F0d8bF88E0AEcA", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x21BfBDa47A0B4B5b1248c767Ee49F7caA9B23697/logo.png", - "symbol": "OVR", - "name": "OVR", - "contractAddress": "0x21BfBDa47A0B4B5b1248c767Ee49F7caA9B23697", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4575f41308EC1483f3d399aa9a2826d74Da13Deb/logo.png", - "symbol": "OXT", - "name": "Orchid", - "contractAddress": "0x4575f41308EC1483f3d399aa9a2826d74Da13Deb", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1614F18Fc94f47967A3Fbe5FfcD46d4e7Da3D787/logo.png", - "symbol": "PAID", - "name": "PAID Network", - "contractAddress": "0x1614F18Fc94f47967A3Fbe5FfcD46d4e7Da3D787", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8c8687fC965593DFb2F0b4EAeFD55E9D8df348df/logo.png", - "symbol": "PAID", - "name": "PAID Network", - "contractAddress": "0x8c8687fC965593DFb2F0b4EAeFD55E9D8df348df", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x45804880De22913dAFE09f4980848ECE6EcbAf78/logo.png", - "symbol": "PAXG", - "name": "Paxos Gold", - "contractAddress": "0x45804880De22913dAFE09f4980848ECE6EcbAf78", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "PAX", - "name": "Paxos Standard", - "contractAddress": "0x8e870d67f660d95d5be530380d0ec0bd388289e1", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xbC396689893D065F41bc2C6EcbeE5e0085233447/logo.png", - "symbol": "PERP", - "name": "Perpetual", - "contractAddress": "0xbC396689893D065F41bc2C6EcbeE5e0085233447", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x429881672B9AE42b8EbA0E26cD9C73711b891Ca5/logo.png", - "symbol": "PICKLE", - "name": "PickleToken", - "contractAddress": "0x429881672B9AE42b8EbA0E26cD9C73711b891Ca5", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x93ED3FBe21207Ec2E8f2d3c3de6e058Cb73Bc04d/logo.png", - "symbol": "PNK", - "name": "Pinakion", - "contractAddress": "0x93ED3FBe21207Ec2E8f2d3c3de6e058Cb73Bc04d", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "symbol": "POE", - "name": "Po.et", - "contractAddress": "0x0e0989b1f9b8a38983c2ba8053269ca62ec9b195", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "PAL", - "name": "PolicyPal Network", - "contractAddress": "0xfeDAE5642668f8636A11987Ff386bfd215F942EE", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8B39B70E39Aa811b69365398e0aACe9bee238AEb/logo.png", - "symbol": "PKF", - "name": "PolkaFoundry", - "contractAddress": "0x8B39B70E39Aa811b69365398e0aACe9bee238AEb", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xD478161C952357F05f0292B56012Cd8457F1cfbF/logo.png", - "symbol": "POLK", - "name": "Polkamarkets", - "contractAddress": "0xD478161C952357F05f0292B56012Cd8457F1cfbF", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x83e6f1E41cdd28eAcEB20Cb649155049Fac3D5Aa/logo.png", - "symbol": "POLS", - "name": "PolkastarterToken", - "contractAddress": "0x83e6f1E41cdd28eAcEB20Cb649155049Fac3D5Aa", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "PRL", - "name": "Oyster Pearl", - "contractAddress": "0x1844b21593262668b7248d0f57a220caaba46ab9", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x362bc847A3a9637d3af6624EeC853618a43ed7D2/logo.png", - "symbol": "PRQ", - "name": "Parsiq Token", - "contractAddress": "0x362bc847A3a9637d3af6624EeC853618a43ed7D2", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "symbol": "PTOY", - "name": "Patientory", - "contractAddress": "0x8Ae4BF2C33a8e667de34B54938B0ccD03Eb8CC06", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1796ae0b0fa4862485106a0de9b654eFE301D0b2/logo.png", - "symbol": "PMON", - "name": "Polkamon", - "contractAddress": "0x1796ae0b0fa4862485106a0de9b654eFE301D0b2", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "POLY", - "name": "Polymath", - "contractAddress": "0x9992eC3cF6A55b00978cdDF2b27BC6882d88D1eC", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "symbol": "PPT", - "name": "Populous", - "contractAddress": "0xd4fa1460F537bb9085d22C7bcCB5DD450Ef28e3a", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 6, - "symbol": "POWR", - "name": "Power Ledger", - "contractAddress": "0x595832f8fc6bf59c85c527fec3740a1b7a361269", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC57d533c50bC22247d49a368880fb49a1caA39F7/logo.png", - "symbol": "PTF", - "name": "PowerTrade Fuel Token", - "contractAddress": "0xC57d533c50bC22247d49a368880fb49a1caA39F7", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0CDF9acd87E940837ff21BB40c9fd55F68bba059/logo.png", - "symbol": "MINT", - "name": "Public Mint", - "contractAddress": "0x0CDF9acd87E940837ff21BB40c9fd55F68bba059", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 6, - "symbol": "QASH", - "name": "QASH", - "contractAddress": "0x618e75ac90b12c6049ba3b27f5d5f8651b0037f6", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4a220E6096B25EADb88358cb44068A3248254675/logo.png", - "symbol": "QNT", - "name": "Quant", - "contractAddress": "0x4a220E6096B25EADb88358cb44068A3248254675", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6e0daDE58D2d89eBBe7aFc384e3E4f15b70b14D8/logo.png", - "symbol": "QRX", - "name": "QuiverX", - "contractAddress": "0x6e0daDE58D2d89eBBe7aFc384e3E4f15b70b14D8", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x33D0568941C0C64ff7e0FB4fbA0B11BD37deEd9f/logo.png", - "symbol": "RAMP", - "name": "RAMP DEFI", - "contractAddress": "0x33D0568941C0C64ff7e0FB4fbA0B11BD37deEd9f", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 6, - "symbol": "RC1U03", - "name": "RentalCoin 1.0", - "contractAddress": "0x7384db1aaa06364d1e6e67f5434391d3eea2a038", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "RDN", - "name": "Raiden Network", - "contractAddress": "0x255aa6df07540cb5d3d297f0d0d4d84cb52bc8e6", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xf1f955016EcbCd7321c7266BccFB96c68ea5E49b/logo.png", - "symbol": "RLY", - "name": "Rally", - "contractAddress": "0xf1f955016EcbCd7321c7266BccFB96c68ea5E49b", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA4EED63db85311E22dF4473f87CcfC3DaDCFA3E3/logo.png", - "symbol": "RBC", - "name": "Rubic", - "contractAddress": "0xA4EED63db85311E22dF4473f87CcfC3DaDCFA3E3", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8f8221aFbB33998d8584A2B05749bA73c37a938a/logo.png", - "symbol": "REQ", - "name": "Request Token", - "contractAddress": "0x8f8221aFbB33998d8584A2B05749bA73c37a938a", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8762db106B2c2A0bccB3A80d1Ed41273552616E8/logo.png", - "symbol": "RSR", - "name": "Reserve Rights", - "contractAddress": "0x8762db106B2c2A0bccB3A80d1Ed41273552616E8", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 9, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7dE91B204C1C737bcEe6F000AAA6569Cf7061cb7/logo.png", - "symbol": "XRT", - "name": "Robonomics", - "contractAddress": "0x7dE91B204C1C737bcEe6F000AAA6569Cf7061cb7", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xCb5f72d37685C3D5aD0bB5F982443BC8FcdF570E/logo.png", - "symbol": "ROOT", - "name": "RootKit", - "contractAddress": "0xCb5f72d37685C3D5aD0bB5F982443BC8FcdF570E", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x3155BA85D5F96b2d030a4966AF206230e46849cb/logo.png", - "symbol": "RUNE", - "name": "THORChain ETH.RUNE", - "contractAddress": "0x3155BA85D5F96b2d030a4966AF206230e46849cb", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "SAN", - "name": "SAN", - "contractAddress": "0x7C5A0CE9267ED19B22F8cae653F198e3E8daf098", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE/logo.png", - "symbol": "SHIB", - "name": "SHIBA INU", - "contractAddress": "0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x72e9D9038cE484EE986FEa183f8d8Df93f9aDA13/logo.png", - "symbol": "SMARTCREDIT", - "name": "SMARTCREDIT Token", - "contractAddress": "0x72e9D9038cE484EE986FEa183f8d8Df93f9aDA13", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "SRN", - "name": "SIRIN", - "contractAddress": "0x68d57c9a1C35f63E2c83eE8e49A64e9d70528D25", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "SNM", - "name": "SONM", - "contractAddress": "0x983F6d60db79ea8cA4eB9968C6aFf8cfA04B3c63", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets//logo.png", - "symbol": "SNX", - "name": "Synthetix Network Token", - "contractAddress": "0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x23B608675a2B2fB1890d3ABBd85c5775c51691d5/logo.png", - "symbol": "SOCKS", - "name": "Unisocks Edition 0", - "contractAddress": "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0Ae055097C6d159879521C384F1D2123D1f195e6/logo.png", - "symbol": "STAKE", - "name": "xDai", - "contractAddress": "0x0Ae055097C6d159879521C384F1D2123D1f195e6", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "SUB", - "name": "Substratum", - "contractAddress": "0x8D75959f1E61EC2571aa72798237101F084DE63a", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xEa319e87Cf06203DAe107Dd8E5672175e3Ee976c/logo.png", - "symbol": "SURF", - "name": "SURF.Finance", - "contractAddress": "0xEa319e87Cf06203DAe107Dd8E5672175e3Ee976c", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xCC4304A31d09258b0029eA7FE63d032f52e44EFe/logo.png", - "symbol": "SWAP", - "name": "TrustSwap Token", - "contractAddress": "0xCC4304A31d09258b0029eA7FE63d032f52e44EFe", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB6ff96B8A8d214544Ca0dBc9B33f7AD6503eFD32/logo.png", - "symbol": "SYNC", - "name": "SYNC", - "contractAddress": "0xB6ff96B8A8d214544Ca0dBc9B33f7AD6503eFD32", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB987D48Ed8f2C468D52D6405624EADBa5e76d723/logo.png", - "symbol": "STBZ", - "name": "Stabilize Token", - "contractAddress": "0xB987D48Ed8f2C468D52D6405624EADBa5e76d723", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "SNT", - "name": "Status", - "contractAddress": "0x744d70FDBE2Ba4CF95131626614a1763DF805B9E", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "SEED", - "name": "Superbloom Network", - "contractAddress": "0x4e7bd88e3996f48e2a24d15e37ca4c02b4d134d2", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1C9922314ED1415c95b9FD453c3818fd41867d0B/logo.png", - "symbol": "TOWER", - "name": "TOWER", - "contractAddress": "0x1C9922314ED1415c95b9FD453c3818fd41867d0B", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xd084B83C305daFD76AE3E1b4E1F1fe2eCcCb3988/logo.png", - "symbol": "TVK", - "name": "Terra Virtua Kolect", - "contractAddress": "0xd084B83C305daFD76AE3E1b4E1F1fe2eCcCb3988", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xaA7a9CA87d3694B5755f213B5D04094b8d0F0A6F/logo.png", - "symbol": "TRAC", - "name": "Trace Token", - "contractAddress": "0xaA7a9CA87d3694B5755f213B5D04094b8d0F0A6F", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6F87D756DAf0503d08Eb8993686c7Fc01Dc44fB1/logo.png", - "symbol": "TRADE", - "name": "UniTrade", - "contractAddress": "0x6F87D756DAf0503d08Eb8993686c7Fc01Dc44fB1", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xc7283b66Eb1EB5FB86327f08e1B5816b0720212B/logo.png", - "symbol": "TRIBE", - "name": "Tribe", - "contractAddress": "0xc7283b66Eb1EB5FB86327f08e1B5816b0720212B", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0000000000085d4780B73119b644AE5ecd22b376/logo.png", - "symbol": "TUSD", - "name": "TrueUSD", - "contractAddress": "0x0000000000085d4780B73119b644AE5ecd22b376", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4C19596f5aAfF459fA38B0f7eD92F11AE6543784/logo.png", - "symbol": "TRU", - "name": "TrustToken", - "contractAddress": "0x4C19596f5aAfF459fA38B0f7eD92F11AE6543784", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8400D94A5cb0fa0D041a3788e395285d61c9ee5e/logo.png", - "symbol": "UBT", - "name": "Unibright", - "contractAddress": "0x8400D94A5cb0fa0D041a3788e395285d61c9ee5e", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "symbol": "UCASH", - "name": "UCASH", - "contractAddress": "0x92e52a1a235d9a103d970901066ce910aacefd37", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x04Fa0d235C4abf4BcF4787aF4CF447DE572eF828/logo.png", - "symbol": "UMA", - "name": "UMA Voting Token v1", - "contractAddress": "0x04Fa0d235C4abf4BcF4787aF4CF447DE572eF828", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6fC13EACE26590B80cCCAB1ba5d51890577D83B2/logo.png", - "symbol": "UMB", - "name": "Umbrella", - "contractAddress": "0x6fC13EACE26590B80cCCAB1ba5d51890577D83B2", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9Ed8e7C9604790F7Ec589F99b94361d8AAB64E5E/logo.png", - "symbol": "UNISTAKE", - "name": "Unistake", - "contractAddress": "0x9Ed8e7C9604790F7Ec589F99b94361d8AAB64E5E", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x10Be9a8dAe441d276a5027936c3aADEd2d82bC15/logo.png", - "symbol": "UMX", - "name": "https://unimex.network/", - "contractAddress": "0x10Be9a8dAe441d276a5027936c3aADEd2d82bC15", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x226f7b842E0F0120b7E194D05432b3fd14773a9D/logo.png", - "symbol": "UNN", - "name": "UNION Protocol Governance Token", - "contractAddress": "0x226f7b842E0F0120b7E194D05432b3fd14773a9D", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "VIB", - "name": "Viberate", - "contractAddress": "0x2C974B2d0BA1716E644c1FC59982a89DDD2fF724", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x3D3D35bb9bEC23b06Ca00fe472b50E7A4c692C30/logo.png", - "symbol": "VIDYA", - "name": "Vidya", - "contractAddress": "0x3D3D35bb9bEC23b06Ca00fe472b50E7A4c692C30", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xF938424F7210f31dF2Aee3011291b658f872e91e/logo.png", - "symbol": "VISR", - "name": "VISOR", - "contractAddress": "0xF938424F7210f31dF2Aee3011291b658f872e91e", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "symbol": "WAX", - "name": "WAX", - "contractAddress": "0x39Bb259F66E1C59d5ABEF88375979b4D20D98022", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7a2Bc711E19ba6aff6cE8246C546E8c4B4944DFD/logo.png", - "symbol": "WAXE", - "name": "WAX Economic Token", - "contractAddress": "0x7a2Bc711E19ba6aff6cE8246C546E8c4B4944DFD", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "WINGS", - "name": "Wings", - "contractAddress": "0x667088b212ce3d06a1b553a7221E1fD19000d9aF", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x66a0f676479Cee1d7373f3DC2e2952778BfF5bd6/logo.png", - "symbol": "WISE", - "name": "Wise Token", - "contractAddress": "0x66a0f676479Cee1d7373f3DC2e2952778BfF5bd6", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xBF494F02EE3FdE1F20BEE6242bCe2d1ED0c15e47/logo.png", - "symbol": "WORLD", - "name": "World Token", - "contractAddress": "0xBF494F02EE3FdE1F20BEE6242bCe2d1ED0c15e47", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 6, - "symbol": "WRC", - "name": "Worldcore", - "contractAddress": "0x72adadb447784dd7ab1f472467750fc485e4cb2d", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599/logo.png", - "symbol": "WBTC", - "name": "Wrapped BTC", - "contractAddress": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", - "symbol": "WETH", - "name": "Wrapped Ethereum", - "contractAddress": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x09a3EcAFa817268f77BE1283176B946C4ff2E608/logo.png", - "symbol": "MIR", - "name": "Wrapped MIR Token", - "contractAddress": "0x09a3EcAFa817268f77BE1283176B946C4ff2E608", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8888801aF4d980682e47f1A9036e589479e835C5/logo.png", - "symbol": "MPH", - "name": "88mph.app", - "contractAddress": "0x8888801aF4d980682e47f1A9036e589479e835C5", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9469D013805bFfB7D3DEBe5E7839237e535ec483/logo.png", - "symbol": "RING", - "name": "Evolution Land Global Token", - "contractAddress": "0x9469D013805bFfB7D3DEBe5E7839237e535ec483", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 0, - "symbol": "XJP", - "name": "Digital JPY", - "contractAddress": "0x39689fE671C01fcE173395f6BC45D4C332026666", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xF063fE1aB7a291c5d06a86e14730b00BF24cB589/logo.png", - "symbol": "SALE", - "name": "DxSale.Network", - "contractAddress": "0xF063fE1aB7a291c5d06a86e14730b00BF24cB589", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "XUC", - "name": "Exchange Union", - "contractAddress": "0xc324a2f6b05880503444451b8b27e6f9e63287cb", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xee573a945B01B788B9287CE062A0CFC15bE9fd86/logo.png", - "symbol": "XED", - "name": "Exeedme", - "contractAddress": "0xee573a945B01B788B9287CE062A0CFC15bE9fd86", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x2e1E15C44Ffe4Df6a0cb7371CD00d5028e571d14/logo.png", - "symbol": "MTLX", - "name": "Mettalex", - "contractAddress": "0x2e1E15C44Ffe4Df6a0cb7371CD00d5028e571d14", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "NCASH", - "name": "Nucleus Vision", - "contractAddress": "0x809826cceab68c387726af962713b64cb5cb3cca", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA15C7Ebe1f07CaF6bFF097D8a589fb8AC49Ae5B3/logo.png", - "symbol": "NPXS", - "name": "Pundi X Token", - "contractAddress": "0xA15C7Ebe1f07CaF6bFF097D8a589fb8AC49Ae5B3", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4fE83213D56308330EC302a8BD641f1d0113A4Cc/logo.png", - "symbol": "NU", - "name": "NuCypher", - "contractAddress": "0x4fE83213D56308330EC302a8BD641f1d0113A4Cc", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "symbol": "QRL", - "name": "QRL", - "contractAddress": "0x697beac28B09E122C4332D163985e8a73121b97F", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "PAY", - "name": "TenX", - "contractAddress": "0xB97048628DB6B661D4C2aA833e95Dbe1A905B280", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xe3818504c1B32bF1557b16C238B2E01Fd3149C17/logo.png", - "symbol": "PLR", - "name": "PILLAR", - "contractAddress": "0xe3818504c1B32bF1557b16C238B2E01Fd3149C17", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "PMTN", - "name": "Peer Mountain", - "contractAddress": "0xe91df2bb9bccd18c45f01843cab88768d64f2d32", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5BEfBB272290dD5b8521D4a938f6c4757742c430/logo.png", - "symbol": "XFI", - "name": "Xfinance", - "contractAddress": "0x5BEfBB272290dD5b8521D4a938f6c4757742c430", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0f7F961648aE6Db43C75663aC7E5414Eb79b5704/logo.png", - "symbol": "XIO", - "name": "XIO Network", - "contractAddress": "0x0f7F961648aE6Db43C75663aC7E5414Eb79b5704", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 9, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x607F4C5BB672230e8672085532f7e901544a7375/logo.png", - "symbol": "RLC", - "name": "iExec RLC", - "contractAddress": "0x607F4C5BB672230e8672085532f7e901544a7375", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x89Ab32156e46F46D02ade3FEcbe5Fc4243B9AAeD/logo.png", - "symbol": "PNT", - "name": "pNetwork Token", - "contractAddress": "0x89Ab32156e46F46D02ade3FEcbe5Fc4243B9AAeD", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x69A95185ee2a045CDC4bCd1b1Df10710395e4e23/logo.png", - "symbol": "POOLZ", - "name": "$Poolz Finance", - "contractAddress": "0x69A95185ee2a045CDC4bCd1b1Df10710395e4e23", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - }, - "symbol": "QCX", - "name": "QuickX Protocol", - "contractAddress": "0xf9e5af7b42d31d51677c75bbbd37c1986ec79aee", - "customIcon": "qcx" - }, - { - "decimalCount": 18, - "symbol": "QSP", - "name": "Quantstamp", - "contractAddress": "0x99ea4dB9EE77ACD40B119BD1dC4E33e1C070b80d", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x03ab458634910AaD20eF5f1C8ee96F1D6ac54919/logo.png", - "symbol": "RAI", - "name": "Rai Reflex Index", - "contractAddress": "0x03ab458634910AaD20eF5f1C8ee96F1D6ac54919", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xFca59Cd816aB1eaD66534D82bc21E7515cE441CF/logo.png", - "symbol": "RARI", - "name": "Rarible", - "contractAddress": "0xFca59Cd816aB1eaD66534D82bc21E7515cE441CF", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x50DE6856358Cc35f3A9a57eAAA34BD4cB707d2cd/logo.png", - "symbol": "RAZOR", - "name": "RAZOR", - "contractAddress": "0x50DE6856358Cc35f3A9a57eAAA34BD4cB707d2cd", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "RCN", - "name": "Ripio Credit", - "contractAddress": "0xf970b8e36e23f7fc3fd752eea86f8be8d83375a6", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xFE3E6a25e6b192A42a44ecDDCd13796471735ACf/logo.png", - "symbol": "REEF", - "name": "Reef.finance", - "contractAddress": "0xFE3E6a25e6b192A42a44ecDDCd13796471735ACf", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x408e41876cCCDC0F92210600ef50372656052a38/logo.png", - "symbol": "REN", - "name": "Republic Token", - "contractAddress": "0x408e41876cCCDC0F92210600ef50372656052a38", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1985365e9f78359a9B6AD760e32412f4a445E862/logo.png", - "symbol": "REP", - "name": "Reputation", - "contractAddress": "0x1985365e9f78359a9B6AD760e32412f4a445E862", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x557B933a7C2c45672B610F8954A3deB39a51A8Ca/logo.png", - "symbol": "REVV", - "name": "REVV", - "contractAddress": "0x557B933a7C2c45672B610F8954A3deB39a51A8Ca", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa1d6Df714F91DeBF4e0802A542E13067f31b8262/logo.png", - "symbol": "RFOX", - "name": "RFOX", - "contractAddress": "0xa1d6Df714F91DeBF4e0802A542E13067f31b8262", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "RUFF", - "name": "RUFF", - "contractAddress": "0xf278c1ca969095ffddded020290cf8b5c424ace2", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xf21661D0D1d76d3ECb8e1B9F1c923DBfffAe4097/logo.png", - "symbol": "RIO", - "name": "Realio Network", - "contractAddress": "0xf21661D0D1d76d3ECb8e1B9F1c923DBfffAe4097", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "RCC", - "name": "Reality Clash", - "contractAddress": "0x9b6443b0fb9c241a7fdac375595cea13e6b7807a", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x221657776846890989a759BA2973e427DfF5C9bB/logo.png", - "symbol": "REPv2", - "name": "Reputation", - "contractAddress": "0x221657776846890989a759BA2973e427DfF5C9bB", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 0, - "symbol": "REV", - "name": "Revain", - "contractAddress": "0x48f775efbe4f5ece6e0df2f7b5932df56823b990", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xaf9f549774ecEDbD0966C52f250aCc548D3F36E5/logo.png", - "symbol": "RFuel", - "name": "Rio Fuel Token", - "contractAddress": "0xaf9f549774ecEDbD0966C52f250aCc548D3F36E5", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "RVT", - "name": "Rivetz", - "contractAddress": "0x3d1ba9be9f66b8ee101911bc36d3fb562eac2244", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB4EFd85c19999D84251304bDA99E90B92300Bd93/logo.png", - "symbol": "RPL", - "name": "Rocket Pool", - "contractAddress": "0xB4EFd85c19999D84251304bDA99E90B92300Bd93", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x3845badAde8e6dFF049820680d1F14bD3903a5d0/logo.png", - "symbol": "SAND", - "name": "SAND", - "contractAddress": "0x3845badAde8e6dFF049820680d1F14bD3903a5d0", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "symbol": "SENT", - "name": "SENTinel", - "contractAddress": "0xa44e5137293e855b1b7bc7e2c6f8cd796ffcb037", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xD23Ac27148aF6A2f339BD82D0e3CFF380b5093de/logo.png", - "symbol": "SI", - "name": "SIREN", - "contractAddress": "0xD23Ac27148aF6A2f339BD82D0e3CFF380b5093de", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "STK", - "name": "STK", - "contractAddress": "0xae73b38d1c9a8b274127ec30160a4927c4d71824", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "SAI", - "name": "Sai", - "contractAddress": "0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "symbol": "SALT", - "name": "SALT", - "contractAddress": "0x4156D3342D5c385a87D264F90653733592000581", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9B02dD390a603Add5c07f9fd9175b7DABE8D63B7/logo.png", - "symbol": "SPI", - "name": "Shopping.io", - "contractAddress": "0x9B02dD390a603Add5c07f9fd9175b7DABE8D63B7", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 0, - "symbol": "SNGLS", - "name": "Singular DTV", - "contractAddress": "0xaeC2E87E0A235266D9C5ADc9DEb4b2E29b54D009", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 0, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x37236CD05b34Cc79d3715AF2383E96dd7443dCF1/logo.png", - "symbol": "SLP", - "name": "Small Love Potion", - "contractAddress": "0x37236CD05b34Cc79d3715AF2383E96dd7443dCF1", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x06A01a4d579479Dd5D884EBf61A31727A3d8D442/logo.png", - "symbol": "Skey", - "name": "SmartKey", - "contractAddress": "0x06A01a4d579479Dd5D884EBf61A31727A3d8D442", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0488401c3F535193Fa8Df029d9fFe615A06E74E6/logo.png", - "symbol": "SRK", - "name": "SparkPoint", - "contractAddress": "0x0488401c3F535193Fa8Df029d9fFe615A06E74E6", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xb753428af26E81097e7fD17f40c88aaA3E04902c/logo.png", - "symbol": "SFI", - "name": "Spice", - "contractAddress": "0xb753428af26E81097e7fD17f40c88aaA3E04902c", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x00c83aeCC790e8a4453e5dD3B0B4b3680501a7A7/logo.png", - "symbol": "SKL", - "name": "SKALE", - "contractAddress": "0x00c83aeCC790e8a4453e5dD3B0B4b3680501a7A7", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x40FD72257597aA14C7231A7B1aaa29Fce868F677/logo.png", - "symbol": "XOR", - "name": "Sora", - "contractAddress": "0x40FD72257597aA14C7231A7B1aaa29Fce868F677", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "symbol": "STORJ", - "name": "Storj", - "contractAddress": "0xB64ef51C888972c908CFacf59B47C1AfBC0Ab8aC", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xbE9375C6a420D2eEB258962efB95551A5b722803/logo.png", - "symbol": "STMX", - "name": "StormX", - "contractAddress": "0xbE9375C6a420D2eEB258962efB95551A5b722803", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "STQ", - "name": "Storiqa", - "contractAddress": "0x5c3a228510d246b78a3765c20221cbf3082b44a4", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "STORM", - "name": "Storm", - "contractAddress": "0xD0a4b8946Cb52f0661273bfbC6fD0E0C75Fc6433", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x990f341946A3fdB507aE7e52d17851B87168017c/logo.png", - "symbol": "STRONG", - "name": "Strong", - "contractAddress": "0x990f341946A3fdB507aE7e52d17851B87168017c", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "STX", - "name": "Stox", - "contractAddress": "0x006BeA43Baa3f7A6f765F14f10A1a1b08334EF45", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "SNC", - "name": "SunContract", - "contractAddress": "0xF4134146AF2d511Dd5EA8cDB1C4AC88C57D60404", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xe53EC727dbDEB9E2d5456c3be40cFF031AB40A55/logo.png", - "symbol": "SUPER", - "name": "SuperFarm", - "contractAddress": "0xe53EC727dbDEB9E2d5456c3be40cFF031AB40A55", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6B3595068778DD592e39A122f4f5a5cF09C90fE2/logo.png", - "symbol": "SUSHI", - "name": "SushiSwap", - "contractAddress": "0x6B3595068778DD592e39A122f4f5a5cF09C90fE2", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8CE9137d39326AD0cD6491fb5CC0CbA0e089b6A9/logo.png", - "symbol": "SXP", - "name": "Swipe", - "contractAddress": "0x8CE9137d39326AD0cD6491fb5CC0CbA0e089b6A9", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9f7229aF0c4b9740e207Ea283b9094983f78ba04/logo.png", - "symbol": "TAD", - "name": "Tadpole", - "contractAddress": "0x9f7229aF0c4b9740e207Ea283b9094983f78ba04", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa1eD0364D53394209D61aE8bfdb8Ff50484D8c91/logo.png", - "symbol": "TBC", - "name": "TeraBlock Token", - "contractAddress": "0xa1eD0364D53394209D61aE8bfdb8Ff50484D8c91", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "THETA", - "name": "Theta Token", - "contractAddress": "0x3883f5e181fccaf8410fa61e12b59bad963fb645", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x77777FeDdddFfC19Ff86DB637967013e6C6A116C/logo.png", - "symbol": "TORN", - "name": "TornadoCash", - "contractAddress": "0x77777FeDdddFfC19Ff86DB637967013e6C6A116C", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 6, - "symbol": "TRX", - "name": "TRON", - "contractAddress": "0xf230b790e05390fc8295f4d3f60332c93bed42e2", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0202Be363B8a4820f3F4DE7FaF5224fF05943AB1/logo.png", - "symbol": "UFT", - "name": "UniLend Finance Token", - "contractAddress": "0x0202Be363B8a4820f3F4DE7FaF5224fF05943AB1", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 6, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png", - "symbol": "USDC", - "name": "USDC", - "contractAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "STU", - "name": "bitJob", - "contractAddress": "0x0371a82e4a9d0a4312f3ee2ac9c6958512891372", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xBa21Ef4c9f433Ede00badEFcC2754B8E74bd538A/logo.png", - "symbol": "SWFL", - "name": "Swapfolio", - "contractAddress": "0xBa21Ef4c9f433Ede00badEFcC2754B8E74bd538A", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8dAEBADE922dF735c38C80C7eBD708Af50815fAa/logo.png", - "symbol": "TBTC", - "name": "tBTC", - "contractAddress": "0x8dAEBADE922dF735c38C80C7eBD708Af50815fAa", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x57Ab1ec28D129707052df4dF418D58a2D46d5f51/logo.png", - "symbol": "sUSD", - "name": "Synth sUSD", - "contractAddress": "0x57Ab1ec28D129707052df4dF418D58a2D46d5f51", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 2, - "symbol": "TEL", - "name": "Telcoin", - "contractAddress": "0x467bccd9d29f223bce8043b84e8c8b282827790f", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xDcB01cc464238396E213a6fDd933E36796eAfF9f/logo.png", - "symbol": "YLD", - "name": "Yield", - "contractAddress": "0xDcB01cc464238396E213a6fDd933E36796eAfF9f", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7F3EDcdD180Dbe4819Bd98FeE8929b5cEdB3AdEB/logo.png", - "symbol": "XTK", - "name": "xToken", - "contractAddress": "0x7F3EDcdD180Dbe4819Bd98FeE8929b5cEdB3AdEB", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xAE1eaAE3F627AAca434127644371b67B18444051/logo.png", - "symbol": "YOP", - "name": "YOP", - "contractAddress": "0xAE1eaAE3F627AAca434127644371b67B18444051", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xf4CD3d3Fda8d7Fd6C5a500203e38640A70Bf9577/logo.png", - "symbol": "Yf-DAI", - "name": "YfDAI.finance", - "contractAddress": "0xf4CD3d3Fda8d7Fd6C5a500203e38640A70Bf9577", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x2e2f3246b6c65CCc4239c9Ee556EC143a7E5DE2c/logo.png", - "symbol": "YFIM", - "name": "yfi.mobi", - "contractAddress": "0x2e2f3246b6c65CCc4239c9Ee556EC143a7E5DE2c", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB1e9157c2Fdcc5a856C8DA8b2d89b6C32b3c1229/logo.png", - "symbol": "ZEFU", - "name": "Zenfuse Trading Platform Token", - "contractAddress": "0xB1e9157c2Fdcc5a856C8DA8b2d89b6C32b3c1229", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 12, - "symbol": "ZIL", - "name": "Zilliqa", - "contractAddress": "0x05f4a42e251f2d52b8ed15E9FEdAacFcEF1FAD27", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "ZLA", - "name": "Zilla", - "contractAddress": "0xfd8971d5e8e1740ce2d0a84095fca4de729d0c16", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xE95A203B1a91a908F9B9CE46459d101078c2c3cb/logo.png", - "symbol": "aEth", - "name": "aEthereum", - "contractAddress": "0xE95A203B1a91a908F9B9CE46459d101078c2c3cb", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7b123f53421b1bF8533339BFBdc7C98aA94163db/logo.png", - "symbol": "buidl", - "name": "dfohub", - "contractAddress": "0x7b123f53421b1bF8533339BFBdc7C98aA94163db", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5218E472cFCFE0b64A064F055B43b4cdC9EfD3A6/logo.png", - "symbol": "eRSDL", - "name": "UnFederalReserveToken", - "contractAddress": "0x5218E472cFCFE0b64A064F055B43b4cdC9EfD3A6", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984/logo.png", - "symbol": "UNI", - "name": "Uniswap", - "contractAddress": "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 6, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xdAC17F958D2ee523a2206206994597C13D831ec7/logo.png", - "symbol": "USDT", - "name": "Tether USD", - "contractAddress": "0xdAC17F958D2ee523a2206206994597C13D831ec7", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "VEE", - "name": "BLOCKv", - "contractAddress": "0x340d2bde5eb28c1eed91b2f790723e3b160613b7", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xF411903cbC70a74d22900a5DE66A2dda66507255/logo.png", - "symbol": "VRA", - "name": "VERA", - "contractAddress": "0xF411903cbC70a74d22900a5DE66A2dda66507255", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1b40183EFB4Dd766f11bDa7A7c3AD8982e998421/logo.png", - "symbol": "VSP", - "name": "VesperToken", - "contractAddress": "0x1b40183EFB4Dd766f11bDa7A7c3AD8982e998421", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "symbol": "VGX", - "name": "Voyager", - "contractAddress": "0x5af2be193a6abca9c8817001f45744777db30756", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 4, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9355372396e3F6daF13359B7b607a3374cc638e0/logo.png", - "symbol": "WHALE", - "name": "WHALE", - "contractAddress": "0x9355372396e3F6daF13359B7b607a3374cc638e0", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "WTC", - "name": "Waltonchain", - "contractAddress": "0xb7cb1c96db6b22b0d3d9536e0108d062bd488f74", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4691937a7508860F876c9c0a2a617E7d9E945D4B/logo.png", - "symbol": "WOO", - "name": "Wootrade Network", - "contractAddress": "0x4691937a7508860F876c9c0a2a617E7d9E945D4B", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "symbol": "WPR", - "name": "WePower", - "contractAddress": "0x4CF488387F035FF08c371515562CBa712f9015d4", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5228a22e72ccC52d415EcFd199F99D0665E7733b/logo.png", - "symbol": "pBTC", - "name": "pTokens BTC", - "contractAddress": "0x5228a22e72ccC52d415EcFd199F99D0665E7733b", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 8, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D/logo.png", - "symbol": "renBTC", - "name": "renBTC", - "contractAddress": "0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x3383c5a8969Dc413bfdDc9656Eb80A1408E4bA20/logo.png", - "symbol": "wANATHA", - "name": "Wrapped ANATHA", - "contractAddress": "0x3383c5a8969Dc413bfdDc9656Eb80A1408E4bA20", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa0afAA285Ce85974c3C881256cB7F225e3A1178a/logo.png", - "symbol": "wCRES", - "name": "Wrapped CRES", - "contractAddress": "0xa0afAA285Ce85974c3C881256cB7F225e3A1178a", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e/logo.png", - "symbol": "YFI", - "name": "Yearn.Finance", - "contractAddress": "0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xF94b5C5651c888d928439aB6514B93944eEE6F48/logo.png", - "symbol": "YLD", - "name": "Yield", - "contractAddress": "0xF94b5C5651c888d928439aB6514B93944eEE6F48", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 10, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xb9EF770B6A5e12E45983C5D80545258aA38F3B78/logo.png", - "symbol": "ZCN", - "name": "0chain", - "contractAddress": "0xb9EF770B6A5e12E45983C5D80545258aA38F3B78", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x2eDf094dB69d6Dcd487f1B3dB9febE2eeC0dd4c5/logo.png", - "symbol": "ZEE", - "name": "ZeroSwapToken", - "contractAddress": "0x2eDf094dB69d6Dcd487f1B3dB9febE2eeC0dd4c5", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xE41d2489571d322189246DaFA5ebDe1F4699F498/logo.png", - "symbol": "ZRX", - "name": "0x Protocol Token", - "contractAddress": "0xE41d2489571d322189246DaFA5ebDe1F4699F498", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 18, - "customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1337DEF18C680aF1f9f45cBcab6309562975b1dD/logo.png", - "symbol": "arNXM", - "name": "Armor NXM", - "contractAddress": "0x1337DEF18C680aF1f9f45cBcab6309562975b1dD", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - }, - { - "decimalCount": 9, - "symbol": "ADT", - "name": "adToken", - "contractAddress": "0xD0D6D6C5Fe4a677D343cC433536BB717bAe167dD", - "blockchain": { - "key": "ethereum", - "curve": "secp256k1", - "testnet": false - } - } -] diff --git a/app/src/main/assets/ethereum_tokens.json b/app/src/main/assets/ethereum_tokens.json new file mode 100644 index 0000000000..d8a8c0cd97 --- /dev/null +++ b/app/src/main/assets/ethereum_tokens.json @@ -0,0 +1,4765 @@ +[ + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7FF4169a6B5122b664c51c95727d87750eC07c84/logo.png", + "symbol" : "10SET", + "name" : "10Set Token", + "contractAddress" : "0x7FF4169a6B5122b664c51c95727d87750eC07c84", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x111111111117dC0aa78b770fA6A738034120C302/logo.png", + "symbol" : "1INCH", + "name" : "1INCH Token", + "contractAddress" : "0x111111111117dC0aa78b770fA6A738034120C302", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xBBc2AE13b23d715c30720F079fcd9B4a74093505/logo.png", + "symbol" : "ERN", + "name" : "@EthernityChain $ERN Token", + "contractAddress" : "0xBBc2AE13b23d715c30720F079fcd9B4a74093505", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "1ST", + "name" : "FirstBlood", + "contractAddress" : "0xAf30D2a7E90d7DC361c8C4585e9BB7D2F6f15bc7", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9/logo.png", + "symbol" : "AAVE", + "name" : "Aave Token", + "contractAddress" : "0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "ABT", + "name" : "ArcBlock", + "contractAddress" : "0xb98d4c97425d9908e66e53a6fdf673acca0be986", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xADE00C28244d5CE17D72E40330B1c318cD12B7c3/logo.png", + "symbol" : "ADX", + "name" : "AdEx Network", + "contractAddress" : "0xADE00C28244d5CE17D72E40330B1c318cD12B7c3", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "AE", + "name" : "Aeternity", + "contractAddress" : "0x5ca9a71b1d01849c0a95490cc00559717fcf0d1d", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "symbol" : "AION", + "name" : "Aion", + "contractAddress" : "0x4CEdA7906a5Ed2179785Cd3A40A69ee8bc99C466", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8eB24319393716668D768dCEC29356ae9CfFe285/logo.png", + "symbol" : "AGI", + "name" : "SingularityNET Token", + "contractAddress" : "0x8eB24319393716668D768dCEC29356ae9CfFe285", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x626E8036dEB333b408Be468F951bdB42433cBF18/logo.png", + "symbol" : "AIOZ", + "name" : "AIOZ Network", + "contractAddress" : "0x626E8036dEB333b408Be468F951bdB42433cBF18", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x00a8b738E453fFd858a7edf03bcCfe20412f0Eb0/logo.png", + "symbol" : "ALBT", + "name" : "AllianceBlock Token", + "contractAddress" : "0x00a8b738E453fFd858a7edf03bcCfe20412f0Eb0", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "AMLT", + "name" : "AMLT by Coinfirm", + "contractAddress" : "0xca0e7269600d353f70b14ad118a49575455c0f2f", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x95a4492F028aa1fd432Ea71146b433E7B4446611/logo.png", + "symbol" : "APY", + "name" : "APY Governance Token", + "contractAddress" : "0x95a4492F028aa1fd432Ea71146b433E7B4446611", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "symbol" : "ARN", + "name" : "Aeron", + "contractAddress" : "0xBA5F11b16B155792Cf3B2E6880E8706859A8AEB6", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 4, + "symbol" : "AST", + "name" : "Airswap", + "contractAddress" : "0x27054b13b1b798b345b591a4d22e6562d47ea75a", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "AVM", + "name" : "AVM Ecosystem", + "contractAddress" : "0x74004a7227615fb52b82d17ffabfa376907d8a4d", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "ELF", + "name" : "Aelf", + "contractAddress" : "0xbf2179859fc6d5bee9bf9158632dc51678a4100e", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "AMB", + "name" : "Amber", + "contractAddress" : "0x4dc3643dbc642b72c158e7f3d2ff232df61cb6ce", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xfF20817765cB7f73d4bde2e66e067E58D11095C2/logo.png", + "symbol" : "AMP", + "name" : "Amp", + "contractAddress" : "0xfF20817765cB7f73d4bde2e66e067E58D11095C2", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 9, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets//logo.png", + "symbol" : "AMPL", + "name" : "Ampleforth", + "contractAddress" : "0xD46bA6D942050d489DBd938a2C909A5d5039A161", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa117000000f279D81A1D3cc75430fAA017FA5A2e/logo.png", + "symbol" : "ANT", + "name" : "Aragon Network Token", + "contractAddress" : "0xa117000000f279D81A1D3cc75430fAA017FA5A2e", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "APPC", + "name" : "AppCoins", + "contractAddress" : "0x1a7a8bd9106f2b8d977e08582dc7d24c723ab0db", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1337DEF16F9B486fAEd0293eb623Dc8395dFE46a/logo.png", + "symbol" : "ARMOR", + "name" : "Armor", + "contractAddress" : "0x1337DEF16F9B486fAEd0293eb623Dc8395dFE46a", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 0, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xdacD69347dE42baBfAEcD09dC88958378780FB62/logo.png", + "symbol" : "ATRI", + "name" : "AtariToken", + "contractAddress" : "0xdacD69347dE42baBfAEcD09dC88958378780FB62", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "ATMI", + "name" : "Atonomi", + "contractAddress" : "0x97aeb5066e1a590e868b511457beb6fe99d329f5", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x18aAA7115705e8be94bfFEBDE57Af9BFc265B998/logo.png", + "symbol" : "AUDIO", + "name" : "Audius", + "contractAddress" : "0x18aAA7115705e8be94bfFEBDE57Af9BFc265B998", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "AUTO", + "name" : "CUBE", + "contractAddress" : "0x622dFfCc4e83C64ba959530A5a5580687a57581b", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xF5D669627376EBd411E34b98F19C868c8ABA5ADA/logo.png", + "symbol" : "AXS", + "name" : "Axie Infinity", + "contractAddress" : "0xF5D669627376EBd411E34b98F19C868c8ABA5ADA", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1C9491865a1DE77C5b6e19d2E6a5F1D7a6F2b25F/logo.png", + "symbol" : "MATTER", + "name" : "Antimatter.Finance Governance Token", + "contractAddress" : "0x1C9491865a1DE77C5b6e19d2E6a5F1D7a6F2b25F", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x71F85B2E46976bD21302B64329868fd15eb0D127/logo.png", + "symbol" : "AXN", + "name" : "Axion", + "contractAddress" : "0x71F85B2E46976bD21302B64329868fd15eb0D127", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xba100000625a3754423978a60c9317c58a424e3D/logo.png", + "symbol" : "BAL", + "name" : "Balancer", + "contractAddress" : "0xba100000625a3754423978a60c9317c58a424e3D", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "BIX", + "name" : "BIX Token", + "contractAddress" : "0xb3104b4b9da82025e8b9f8fb28b3553ce2f67069", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5Dc02Ea99285E17656b8350722694c35154DB1E8/logo.png", + "symbol" : "BOND", + "name" : "BOND", + "contractAddress" : "0x5Dc02Ea99285E17656b8350722694c35154DB1E8", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "BRD", + "name" : "BRD Token", + "contractAddress" : "0x558ec3152e2eb2174905cd19aea4e34a23de9ad6", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "BNT", + "name" : "Bancor", + "contractAddress" : "0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xBA11D00c5f74255f56a5E366F4F77f5A186d7f55/logo.png", + "symbol" : "BAND", + "name" : "BandToken", + "contractAddress" : "0xBA11D00c5f74255f56a5E366F4F77f5A186d7f55", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x54C9EA2E9C9E8eD865Db4A4ce6711C2a0d5063Ba/logo.png", + "symbol" : "BART", + "name" : "BarterTrade", + "contractAddress" : "0x54C9EA2E9C9E8eD865Db4A4ce6711C2a0d5063Ba", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 9, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x07150e919B4De5fD6a63DE1F9384828396f25fDC/logo.png", + "symbol" : "BASE", + "name" : "Base Protocol", + "contractAddress" : "0x07150e919B4De5fD6a63DE1F9384828396f25fDC", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0D8775F648430679A709E98d2b0Cb6250d2887EF/logo.png", + "symbol" : "BAT", + "name" : "Basic Attention Token", + "contractAddress" : "0x0D8775F648430679A709E98d2b0Cb6250d2887EF", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA91ac63D040dEB1b7A5E4d4134aD23eb0ba07e14/logo.png", + "symbol" : "BEL", + "name" : "Bella", + "contractAddress" : "0xA91ac63D040dEB1b7A5E4d4134aD23eb0ba07e14", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "BNB", + "name" : "Binance Coin", + "contractAddress" : "0xb8c77482e45f1f44de1745f52c74426c631bdd52", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "BUSD", + "name" : "Binance USD", + "contractAddress" : "0x4fabb145d64652a948d72533023f6e7a623c7c53", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 9, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x67c597624B17b16fb77959217360B7cD18284253/logo.png", + "symbol" : "MARK", + "name" : "Benchmark", + "contractAddress" : "0x67c597624B17b16fb77959217360B7cD18284253", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7671904eed7f10808B664fc30BB8693FD7237abF/logo.png", + "symbol" : "BBR", + "name" : "BitberryToken", + "contractAddress" : "0x7671904eed7f10808B664fc30BB8693FD7237abF", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xCF3C8Be2e2C42331Da80EF210e9B1b307C03d36A/logo.png", + "symbol" : "BEPRO", + "name" : "BetProtocolToken", + "contractAddress" : "0xCF3C8Be2e2C42331Da80EF210e9B1b307C03d36A", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0c7D5ae016f806603CB1782bEa29AC69471CAb9c/logo.png", + "symbol" : "BFC", + "name" : "Bifrost", + "contractAddress" : "0x0c7D5ae016f806603CB1782bEa29AC69471CAb9c", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9BE89D2a4cd102D8Fecc6BF9dA793be995C22541/logo.png", + "symbol" : "BBTC", + "name" : "Binance Wrapped BTC", + "contractAddress" : "0x9BE89D2a4cd102D8Fecc6BF9dA793be995C22541", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x25e1474170c4c0aA64fa98123bdc8dB49D7802fa/logo.png", + "symbol" : "BID", + "name" : "Bidao", + "contractAddress" : "0x25e1474170c4c0aA64fa98123bdc8dB49D7802fa", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x70401dFD142A16dC7031c56E862Fc88Cb9537Ce0/logo.png", + "symbol" : "BIRD", + "name" : "Bird.Money", + "contractAddress" : "0x70401dFD142A16dC7031c56E862Fc88Cb9537Ce0", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xAec7e1f531Bb09115103C53ba76829910Ec48966/logo.png", + "symbol" : "BLANK", + "name" : "Blank Token", + "contractAddress" : "0xAec7e1f531Bb09115103C53ba76829910Ec48966", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "BLZ", + "name" : "Bluzelle", + "contractAddress" : "0x5732046a883704404f284ce41ffadd5b007fd668", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x725C263e32c72dDC3A19bEa12C5a0479a81eE688/logo.png", + "symbol" : "BMI", + "name" : "Bridge Mutual", + "contractAddress" : "0x725C263e32c72dDC3A19bEa12C5a0479a81eE688", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xD2dDa223b2617cB616c1580db421e4cFAe6a8a85/logo.png", + "symbol" : "BONDLY", + "name" : "Bondly Token", + "contractAddress" : "0xD2dDa223b2617cB616c1580db421e4cFAe6a8a85", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "symbol" : "BTM", + "name" : "Bytom", + "contractAddress" : "0xcb97e65f07da24d46bcdd078ebebd7c6e6e3d750", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "CANDY", + "name" : "Candy", + "contractAddress" : "0xf2eab3a2034d3f6b63734d2e08262040e3ff7b48", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x954b890704693af242613edEf1B603825afcD708/logo.png", + "symbol" : "CARD", + "name" : "Cardstack", + "contractAddress" : "0x954b890704693af242613edEf1B603825afcD708", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "CAS", + "name" : "Cashaa", + "contractAddress" : "0xe8780b48bdb05f928697a5e8155f672ed91462f7", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "CCC", + "name" : "Container Crypto Coin", + "contractAddress" : "0x9e3359f862b6c7f5c660cfd6d1aa6909b1d9504d", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x17aC188e09A7890a1844E5E65471fE8b0CcFadF3/logo.png", + "symbol" : "CC10", + "name" : "Cryptocurrency Top 10 Tokens Index", + "contractAddress" : "0x17aC188e09A7890a1844E5E65471fE8b0CcFadF3", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x26c8AFBBFE1EBaca03C2bB082E69D0476Bffe099/logo.png", + "symbol" : "CELL", + "name" : "Cellframe Token", + "contractAddress" : "0x26c8AFBBFE1EBaca03C2bB082E69D0476Bffe099", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 4, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xaaAEBE6Fe48E54f431b0C390CfaF0b017d09D42d/logo.png", + "symbol" : "CEL", + "name" : "Celsius", + "contractAddress" : "0xaaAEBE6Fe48E54f431b0C390CfaF0b017d09D42d", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "CFI", + "name" : "Cofound.it", + "contractAddress" : "0x12FEF5e57bF45873Cd9B62E9DBd7BFb99e32D73e", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC4C2614E694cF534D407Ee49F8E44D125E4681c4/logo.png", + "symbol" : "CHAIN", + "name" : "Chain Games", + "contractAddress" : "0xC4C2614E694cF534D407Ee49F8E44D125E4681c4", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x63b4f3e3fa4e438698CE330e365E831F7cCD1eF4/logo.png", + "symbol" : "CFi", + "name" : "CyberFi Token", + "contractAddress" : "0x63b4f3e3fa4e438698CE330e365E831F7cCD1eF4", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "CNN", + "name" : "CNN Token", + "contractAddress" : "0x8713d26637cf49e1b6b4a7ce57106aabc9325343", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xDDB3422497E61e13543BeA06989C0789117555c5/logo.png", + "symbol" : "COTI", + "name" : "COTI Token", + "contractAddress" : "0xDDB3422497E61e13543BeA06989C0789117555c5", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "CSP", + "name" : "Caspian", + "contractAddress" : "0xa6446d655a0c34bc4f05042ee88170d056cbaf45", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1fE24F25b1Cf609B9c4e7E12D802e3640dFA5e43/logo.png", + "symbol" : "CGG", + "name" : "ChainGuardians Governance Token", + "contractAddress" : "0x1fE24F25b1Cf609B9c4e7E12D802e3640dFA5e43", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "CMT", + "name" : "CyberMiles", + "contractAddress" : "0xf85fEea2FdD81d51177F6b8F35F0e6734Ce45F5F", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "CND", + "name" : "Cindicator", + "contractAddress" : "0xd4c435f5b09f855c3317c8524cb1f586e42795fa", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "symbol" : "CVC", + "name" : "Civic", + "contractAddress" : "0x41e5560054824ea6b0732e656e3ad64e20e94e45", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x69692D3345010a207b759a7D1af6fc7F38b35c5E/logo.png", + "symbol" : "CHADS", + "name" : "chads.vc", + "contractAddress" : "0x69692D3345010a207b759a7D1af6fc7F38b35c5E", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x514910771AF9Ca656af840dff83E8264EcF986CA/logo.png", + "symbol" : "Link", + "name" : "ChainLink", + "contractAddress" : "0x514910771AF9Ca656af840dff83E8264EcF986CA", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "symbol" : "TIME", + "name" : "Chronobank", + "contractAddress" : "0x6531f133e6DeeBe7F2dcE5A0441aA7ef330B4e53", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xE61fDAF474Fac07063f2234Fb9e60C1163Cfa850/logo.png", + "symbol" : "COIN", + "name" : "Coin Utility Token", + "contractAddress" : "0xE61fDAF474Fac07063f2234Fb9e60C1163Cfa850", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xc00e94Cb662C3520282E6f5717214004A7f26888/logo.png", + "symbol" : "COMP", + "name" : "Compound", + "contractAddress" : "0xc00e94Cb662C3520282E6f5717214004A7f26888", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x38e4adB44ef08F22F5B5b76A8f0c2d0dCbE7DcA1/logo.png", + "symbol" : "CVP", + "name" : "Concentrated Voting Power", + "contractAddress" : "0x38e4adB44ef08F22F5B5b76A8f0c2d0dCbE7DcA1", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xc834Fa996fA3BeC7aAD3693af486ae53D8aA8B50/logo.png", + "symbol" : "CONV", + "name" : "Convergence", + "contractAddress" : "0xc834Fa996fA3BeC7aAD3693af486ae53D8aA8B50", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x62359Ed7505Efc61FF1D56fEF82158CcaffA23D7/logo.png", + "symbol" : "CORE", + "name" : "cVault.finance", + "contractAddress" : "0x62359Ed7505Efc61FF1D56fEF82158CcaffA23D7", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x2ba592F78dB6436527729929AAf6c908497cB200/logo.png", + "symbol" : "CREAM", + "name" : "Cream", + "contractAddress" : "0x2ba592F78dB6436527729929AAf6c908497cB200", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 6, + "symbol" : "CS", + "name" : "Credits", + "contractAddress" : "0x46b9ad944d1059450da1163511069c718f699d31", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xaC0104Cca91D167873B8601d2e71EB3D4D8c33e0/logo.png", + "symbol" : "CWS", + "name" : "Crowns", + "contractAddress" : "0xaC0104Cca91D167873B8601d2e71EB3D4D8c33e0", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "CRPT", + "name" : "Crypterium", + "contractAddress" : "0x08389495d7456e1951ddf7c3a1314a4bfb646d8b", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0f51bb10119727a7e5eA3538074fb341F56B09Ad/logo.png", + "symbol" : "DAO", + "name" : "DAO Maker Token", + "contractAddress" : "0x0f51bb10119727a7e5eA3538074fb341F56B09Ad", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x26CE25148832C04f3d7F26F32478a9fe55197166/logo.png", + "symbol" : "DEXT", + "name" : "DEXTools", + "contractAddress" : "0x26CE25148832C04f3d7F26F32478a9fe55197166", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6B175474E89094C44Da98b954EedeAC495271d0F/logo.png", + "symbol" : "DAI", + "name" : "Dai Stablecoin", + "contractAddress" : "0x6B175474E89094C44Da98b954EedeAC495271d0F", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xFbEEa1C75E4c4465CB2FCCc9c6d6afe984558E20/logo.png", + "symbol" : "DDIM", + "name" : "DuckDaoDime", + "contractAddress" : "0xFbEEa1C75E4c4465CB2FCCc9c6d6afe984558E20", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xE1c7E30C42C24582888C758984f6e382096786bd/logo.png", + "symbol" : "XCUR", + "name" : "Curate", + "contractAddress" : "0xE1c7E30C42C24582888C758984f6e382096786bd", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xD533a949740bb3306d119CC777fa900bA034cd52/logo.png", + "symbol" : "CRV", + "name" : "Curve DAO Token", + "contractAddress" : "0xD533a949740bb3306d119CC777fa900bA034cd52", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x30f271C9E86D2B7d00a6376Cd96A1cFBD5F0b9b3/logo.png", + "symbol" : "DEC", + "name" : "Decentr", + "contractAddress" : "0x30f271C9E86D2B7d00a6376Cd96A1cFBD5F0b9b3", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9EA3b5b4EC044b70375236A281986106457b20EF/logo.png", + "symbol" : "DELTA", + "name" : "Delta Financial", + "contractAddress" : "0x9EA3b5b4EC044b70375236A281986106457b20EF", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "symbol" : "DENT", + "name" : "DENT", + "contractAddress" : "0x3597bfd533a99c9aa083587b074434e61eb0a258", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xd379700999F4805Ce80aa32DB46A94dF64561108/logo.png", + "symbol" : "DETS", + "name" : "Dextrust", + "contractAddress" : "0xd379700999F4805Ce80aa32DB46A94dF64561108", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5cAf454Ba92e6F2c929DF14667Ee360eD9fD5b26/logo.png", + "symbol" : "DEV", + "name" : "Dev", + "contractAddress" : "0x5cAf454Ba92e6F2c929DF14667Ee360eD9fD5b26", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5F64Ab1544D28732F0A24F4713c2C8ec0dA089f0/logo.png", + "symbol" : "DEXTF", + "name" : "DEXTF Token", + "contractAddress" : "0x5F64Ab1544D28732F0A24F4713c2C8ec0dA089f0", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xc719d010B63E5bbF2C0551872CD5316ED26AcD83/logo.png", + "symbol" : "DIP", + "name" : "Decentralized Insurance Protocol", + "contractAddress" : "0xc719d010B63E5bbF2C0551872CD5316ED26AcD83", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x26E43759551333e57F073bb0772F50329A957b30/logo.png", + "symbol" : "DGVC", + "name" : "DegenVC", + "contractAddress" : "0x26E43759551333e57F073bb0772F50329A957b30", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x152687Bc4A7FCC89049cF119F9ac3e5aCF2eE7ef/logo.png", + "symbol" : "DHC", + "name" : "DeltaHub Community", + "contractAddress" : "0x152687Bc4A7FCC89049cF119F9ac3e5aCF2eE7ef", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x431ad2ff6a9C365805eBaD47Ee021148d6f7DBe0/logo.png", + "symbol" : "DF", + "name" : "dForce", + "contractAddress" : "0x431ad2ff6a9C365805eBaD47Ee021148d6f7DBe0", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 9, + "symbol" : "DGD", + "name" : "DigixDAO", + "contractAddress" : "0xE0B7927c4aF23765Cb51314A0E0521A9645F0E2A", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x88EF27e69108B2633F8E1C184CC37940A075cC02/logo.png", + "symbol" : "DEGO", + "name" : "dego.finance", + "contractAddress" : "0x88EF27e69108B2633F8E1C184CC37940A075cC02", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x84cA8bc7997272c7CfB4D0Cd3D55cd942B3c9419/logo.png", + "symbol" : "DIA", + "name" : "DIA", + "contractAddress" : "0x84cA8bc7997272c7CfB4D0Cd3D55cd942B3c9419", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xEd91879919B71bB6905f23af0A68d231EcF87b14/logo.png", + "symbol" : "DMG", + "name" : "DMM: Governance", + "contractAddress" : "0xEd91879919B71bB6905f23af0A68d231EcF87b14", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9cEB84f92A0561fa3Cc4132aB9c0b76A59787544/logo.png", + "symbol" : "DOKI", + "name" : "DokiDokiFinance", + "contractAddress" : "0x9cEB84f92A0561fa3Cc4132aB9c0b76A59787544", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "DOV", + "name" : "Dovu", + "contractAddress" : "0xac3211a5025414af2866ff09c23fc18bc97e79b1", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "DRGN", + "name" : "Dragon", + "contractAddress" : "0x419c4db4b9e25d6db2ad9691ccb832c8d9fda05e", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "DNT", + "name" : "district0x", + "contractAddress" : "0x0abdace70d3790235af448c88547603b945604ea", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x3aFfCCa64c2A6f4e3B6Bd9c64CD2C969EFd1ECBe/logo.png", + "symbol" : "DSLA", + "name" : "DSLA", + "contractAddress" : "0x3aFfCCa64c2A6f4e3B6Bd9c64CD2C969EFd1ECBe", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC0bA369c8Db6eB3924965e5c4FD0b4C1B91e305F/logo.png", + "symbol" : "DUCK", + "name" : "DLP Duck Token", + "contractAddress" : "0xC0bA369c8Db6eB3924965e5c4FD0b4C1B91e305F", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x961C8c0B1aaD0c0b10a51FeF6a867E3091BCef17/logo.png", + "symbol" : "DYP", + "name" : "DeFiYieldProtocol", + "contractAddress" : "0x961C8c0B1aaD0c0b10a51FeF6a867E3091BCef17", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x740623d2c797b7D8D1EcB98e9b4Afcf99Ec31E14/logo.png", + "symbol" : "DYT", + "name" : "DoYourTip", + "contractAddress" : "0x740623d2c797b7D8D1EcB98e9b4Afcf99Ec31E14", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "EOS", + "name" : "EOS", + "contractAddress" : "0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xAa6E8127831c9DE45ae56bB1b0d4D4Da6e5665BD/logo.png", + "symbol" : "ETH2x-FLI", + "name" : "ETH 2x Flexible Leverage Index", + "contractAddress" : "0xAa6E8127831c9DE45ae56bB1b0d4D4Da6e5665BD", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 2, + "symbol" : "EUR.AVM", + "name" : "EUR AVM", + "contractAddress" : "0xF01Cd2f1c9E42c509d309aC8C5b29B6dA8E64b1a", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 0, + "symbol" : "EDG", + "name" : "Edgeless", + "contractAddress" : "0x08711D3B02C8758F2FB3ab4e80228418a7F8e39c", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "EDO", + "name" : "Eidoo", + "contractAddress" : "0xced4e93198734ddaff8492d525bd258d49eb388e", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x178c820f862B14f316509ec36b13123DA19A6054/logo.png", + "symbol" : "EWTB", + "name" : "Energy Web Token Bridged", + "contractAddress" : "0x178c820f862B14f316509ec36b13123DA19A6054", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xF629cBd94d3791C9250152BD8dfBDF380E2a3B9c/logo.png", + "symbol" : "ENJ", + "name" : "Enjin Coin", + "contractAddress" : "0xF629cBd94d3791C9250152BD8dfBDF380E2a3B9c", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa0246c9032bC3A600820415aE600c6388619A14D/logo.png", + "symbol" : "FARM", + "name" : "FARM Reward Token", + "contractAddress" : "0xa0246c9032bC3A600820415aE600c6388619A14D", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 6, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xE5CAeF4Af8780E59Df925470b050Fb23C43CA68C/logo.png", + "symbol" : "FRM", + "name" : "Ferrum Network Token", + "contractAddress" : "0xE5CAeF4Af8780E59Df925470b050Fb23C43CA68C", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "FXC", + "name" : "Flexacoin", + "contractAddress" : "0x4a57E687b9126435a9B19E4A802113e266AdeBde", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8a40c222996f9F3431f63Bf80244C36822060f12/logo.png", + "symbol" : "FXF", + "name" : "Finxflo", + "contractAddress" : "0x8a40c222996f9F3431f63Bf80244C36822060f12", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x853d955aCEf822Db058eb8505911ED77F175b99e/logo.png", + "symbol" : "FRAX", + "name" : "Frax", + "contractAddress" : "0x853d955aCEf822Db058eb8505911ED77F175b99e", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 9, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x29502fE4d233EF0b45C3647101Fa1252cE0634BD/logo.png", + "symbol" : "FROGE", + "name" : "Froge Finance", + "contractAddress" : "0x29502fE4d233EF0b45C3647101Fa1252cE0634BD", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b/logo.png", + "symbol" : "FUN", + "name" : "FunFair", + "contractAddress" : "0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8c15Ef5b4B21951d50E53E4fbdA8298FFAD25057/logo.png", + "symbol" : "FX", + "name" : "Function X", + "contractAddress" : "0x8c15Ef5b4B21951d50E53E4fbdA8298FFAD25057", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6BFf2fE249601ed0Db3a87424a2E923118BB0312/logo.png", + "symbol" : "FYZ", + "name" : "Fyooz", + "contractAddress" : "0x6BFf2fE249601ed0Db3a87424a2E923118BB0312", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8a854288a5976036A725879164Ca3e91d30c6A1B/logo.png", + "symbol" : "GET", + "name" : "GET", + "contractAddress" : "0x8a854288a5976036A725879164Ca3e91d30c6A1B", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 9, + "symbol" : "GNX", + "name" : "Genaro Network", + "contractAddress" : "0x6ec8a24cabdc339a06a172f8223ea557055adaa5", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9F9c8ec3534c3cE16F928381372BfbFBFb9F4D24/logo.png", + "symbol" : "GLQ", + "name" : "GraphLinq", + "contractAddress" : "0x9F9c8ec3534c3cE16F928381372BfbFBFb9F4D24", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6810e776880C02933D47DB1b9fc05908e5386b96/logo.png", + "symbol" : "GNO", + "name" : "Gnosis Token", + "contractAddress" : "0x6810e776880C02933D47DB1b9fc05908e5386b96", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "GNT", + "name" : "Golem", + "contractAddress" : "0xa74476443119A942dE498590Fe1f2454d7D4aC0d", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xc3771d47E2Ab5A519E2917E61e23078d0C05Ed7f/logo.png", + "symbol" : "GTH", + "name" : "Gather", + "contractAddress" : "0xc3771d47E2Ab5A519E2917E61e23078d0C05Ed7f", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 2, + "symbol" : "GUSD", + "name" : "Gemini Dollar", + "contractAddress" : "0x056fd409e1d7a124bd7017459dfea2f387b6d5cd", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "GVT", + "name" : "Genesis Vision", + "contractAddress" : "0x103c3A209da59d3E7C4A89307e66521e081CFDF0", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xbEa98c05eEAe2f3bC8c3565Db7551Eb738c8CCAb/logo.png", + "symbol" : "GYSR", + "name" : "Geyser", + "contractAddress" : "0xbEa98c05eEAe2f3bC8c3565Db7551Eb738c8CCAb", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 0, + "symbol" : "GD", + "name" : "Zilla GD", + "contractAddress" : "0xb4cebdb66b3a5183fd4764a25cad1fc109535016", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x09e64c2B61a5f1690Ee6fbeD9baf5D6990F8dFd0/logo.png", + "symbol" : "GRO", + "name" : "Growth", + "contractAddress" : "0x09e64c2B61a5f1690Ee6fbeD9baf5D6990F8dFd0", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xc944E90C64B2c07662A292be6244BDf05Cda44a7/logo.png", + "symbol" : "GRT", + "name" : "Graph Token", + "contractAddress" : "0xc944E90C64B2c07662A292be6244BDf05Cda44a7", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 3, + "symbol" : "GUP", + "name" : "Matchpool", + "contractAddress" : "0xf7B098298f7C69Fc14610bf71d5e02c60792894C", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39/logo.png", + "symbol" : "HEX", + "name" : "HEX", + "contractAddress" : "0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "HPB", + "name" : "HPBCoin", + "contractAddress" : "0x38c6a68304cdefb9bec48bbfaaba5c5b47818bb2", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x584bC13c7D411c00c01A62e8019472dE68768430/logo.png", + "symbol" : "HEGIC", + "name" : "Hegic", + "contractAddress" : "0x584bC13c7D411c00c01A62e8019472dE68768430", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xEEF9f339514298C6A857EfCfC1A762aF84438dEE/logo.png", + "symbol" : "HEZ", + "name" : "Hermez Network Token", + "contractAddress" : "0xEEF9f339514298C6A857EfCfC1A762aF84438dEE", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6c6EE5e31d828De241282B9606C8e98Ea48526E2/logo.png", + "symbol" : "HOT", + "name" : "HoloToken", + "contractAddress" : "0x6c6EE5e31d828De241282B9606C8e98Ea48526E2", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "HYDRO", + "name" : "Hydro", + "contractAddress" : "0xebbdf302c940c6bfd49c6b165f457fdb324649bc", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "ICX", + "name" : "ICON", + "contractAddress" : "0xb5a5f22694352c15b00323844ad545abb2b11028", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "ICN", + "name" : "ICONOMI", + "contractAddress" : "0x888666CA69E0f178DED6D75b5726Cee99A87D698", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xEBd9D99A3982d547C5Bb4DB7E3b1F9F14b67Eb83/logo.png", + "symbol" : "ID", + "name" : "Everest ID", + "contractAddress" : "0xEBd9D99A3982d547C5Bb4DB7E3b1F9F14b67Eb83", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x72630B1e3B42874bf335020Ba0249e3E9e47Bafc/logo.png", + "symbol" : "EPAN", + "name" : "Paypolitan Token", + "contractAddress" : "0x72630B1e3B42874bf335020Ba0249e3E9e47Bafc", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "symbol" : "GBX", + "name" : "Globitex", + "contractAddress" : "0x12fcd6463e66974cf7bbc24ffc4d40d6be458283", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xf6537FE0df7F0Cc0985Cf00792CC98249E73EFa0/logo.png", + "symbol" : "GIV", + "name" : "GIVToken", + "contractAddress" : "0xf6537FE0df7F0Cc0985Cf00792CC98249E73EFa0", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xeEAA40B28A2d1b0B08f6f97bB1DD4B75316c6107/logo.png", + "symbol" : "GOVI", + "name" : "GOVI", + "contractAddress" : "0xeEAA40B28A2d1b0B08f6f97bB1DD4B75316c6107", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 5, + "symbol" : "GTO", + "name" : "Gifto", + "contractAddress" : "0xc5bbae50781be1669306b9e001eff57a2957b09d", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "IOST", + "name" : "IOStoken", + "contractAddress" : "0xfa1a856cfa3409cfa145fa4e20eb270df3eb21ab", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "ITC", + "name" : "IOT Chain", + "contractAddress" : "0x5e6b6d9abad9093fdc861ea1600eba1b355cd940", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xe28b3B32B6c345A34Ff64674606124Dd5Aceca30/logo.png", + "symbol" : "INJ", + "name" : "Injective Token", + "contractAddress" : "0xe28b3B32B6c345A34Ff64674606124Dd5Aceca30", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4B1E80cAC91e2216EEb63e29B957eB91Ae9C2Be8/logo.png", + "symbol" : "JUP", + "name" : "Jupiter", + "contractAddress" : "0x4B1E80cAC91e2216EEb63e29B957eB91Ae9C2Be8", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x85Eee30c52B0b379b046Fb0F85F4f3Dc3009aFEC/logo.png", + "symbol" : "KEEP", + "name" : "KEEP Token", + "contractAddress" : "0x85Eee30c52B0b379b046Fb0F85F4f3Dc3009aFEC", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 6, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x16980b3B4a3f9D89E33311B5aa8f80303E5ca4F8/logo.png", + "symbol" : "KEX", + "name" : "KIRA Network", + "contractAddress" : "0x16980b3B4a3f9D89E33311B5aa8f80303E5ca4F8", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "KIN", + "name" : "Kin", + "contractAddress" : "0x818Fc6C2Ec5986bc6E2CBf00939d90556aB12ce5", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xCbfef8fdd706cde6F208460f2Bf39Aa9c785F05D/logo.png", + "symbol" : "KINE", + "name" : "Kine Governance Token", + "contractAddress" : "0xCbfef8fdd706cde6F208460f2Bf39Aa9c785F05D", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1cEB5cB57C4D4E2b2433641b95Dd330A33185A44/logo.png", + "symbol" : "KP3R", + "name" : "Keep3rV1", + "contractAddress" : "0x1cEB5cB57C4D4E2b2433641b95Dd330A33185A44", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1dD80016e3d4ae146Ee2EBB484e8edD92dacC4ce/logo.png", + "symbol" : "LEAD", + "name" : "Lead Token", + "contractAddress" : "0x1dD80016e3d4ae146Ee2EBB484e8edD92dacC4ce", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0fF6ffcFDa92c53F615a4A75D982f399C989366b/logo.png", + "symbol" : "LAYER", + "name" : "Unilayer", + "contractAddress" : "0x0fF6ffcFDa92c53F615a4A75D982f399C989366b", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xBBbbCA6A901c926F240b89EacB641d8Aec7AEafD/logo.png", + "symbol" : "LRC", + "name" : "LoopringCoin V2", + "contractAddress" : "0xBBbbCA6A901c926F240b89EacB641d8Aec7AEafD", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x3593D125a4f7849a1B059E64F4517A86Dd60c95d/logo.png", + "symbol" : "OM", + "name" : "MANTRA DAO", + "contractAddress" : "0x3593D125a4f7849a1B059E64F4517A86Dd60c95d", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "MAN", + "name" : "MATRIX AI Network", + "contractAddress" : "0xe25bcec5d3801ce3a794079bf94adf1b8ccd802d", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "symbol" : "MYST", + "name" : "Mysterium", + "contractAddress" : "0xa645264C5603E96c3b0B078cdab68733794B0A71", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "NULS", + "name" : "Nuls", + "contractAddress" : "0xb91318f35bdb262e9423bc7c7c2a3a93dd93c92c", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 9, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xfAd45E47083e4607302aa43c65fB3106F1cd7607/logo.png", + "symbol" : "HOGE", + "name" : "hoge.finance", + "contractAddress" : "0xfAd45E47083e4607302aa43c65fB3106F1cd7607", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "symbol" : "IXT", + "name" : "iXledger", + "contractAddress" : "0xfca47962d45adfdfd1ab2d972315db4ce7ccf094", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB9d99C33eA2d86EC5eC6b8A4dD816EBBA64404AF/logo.png", + "symbol" : "K21", + "name" : "k21.kanon.art", + "contractAddress" : "0xB9d99C33eA2d86EC5eC6b8A4dD816EBBA64404AF", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0F5D2fB29fb7d3CFeE444a200298f468908cC942/logo.png", + "symbol" : "MANA", + "name" : "Decentraland MANA", + "contractAddress" : "0x0F5D2fB29fb7d3CFeE444a200298f468908cC942", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1494CA1F11D487c2bBe4543E90080AeBa4BA3C2b/logo.png", + "symbol" : "DPI", + "name" : "DefiPulse Index", + "contractAddress" : "0x1494CA1F11D487c2bBe4543E90080AeBa4BA3C2b", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "symbol" : "ENG", + "name" : "Enigma", + "contractAddress" : "0xf0ee6b27b759c9893ce4f094b49ad28fd15a23e4", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 16, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xD6c67B93a7b248dF608a653d82a100556144c5DA/logo.png", + "symbol" : "EXNT", + "name" : "ExNetwork Community Token", + "contractAddress" : "0xD6c67B93a7b248dF608a653d82a100556144c5DA", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xfffffffFf15AbF397dA76f1dcc1A1604F45126DB/logo.png", + "symbol" : "FSW", + "name" : "FalconSwap Token", + "contractAddress" : "0xfffffffFf15AbF397dA76f1dcc1A1604F45126DB", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xF4d861575ecC9493420A3f5a14F85B13f0b50EB3/logo.png", + "symbol" : "FCL", + "name" : "Fractal Protocol Token", + "contractAddress" : "0xF4d861575ecC9493420A3f5a14F85B13f0b50EB3", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x956F47F50A910163D8BF957Cf5846D573E7f87CA/logo.png", + "symbol" : "FEI", + "name" : "Fei USD", + "contractAddress" : "0x956F47F50A910163D8BF957Cf5846D573E7f87CA", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "FSN", + "name" : "Fusion", + "contractAddress" : "0xd0352a019e9ab9d757776f532377aaebd36fd541", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xdd974D5C2e2928deA5F71b9825b8b646686BD200/logo.png", + "symbol" : "KNC", + "name" : "Kyber Network Crystal", + "contractAddress" : "0xdd974D5C2e2928deA5F71b9825b8b646686BD200", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x67B6D479c7bB412C54e03dCA8E1Bc6740ce6b99C/logo.png", + "symbol" : "KYL", + "name" : "Kylin Network", + "contractAddress" : "0x67B6D479c7bB412C54e03dCA8E1Bc6740ce6b99C", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xaE697F994Fc5eBC000F8e22EbFfeE04612f98A0d/logo.png", + "symbol" : "LGCY", + "name" : "LGCY Network", + "contractAddress" : "0xaE697F994Fc5eBC000F8e22EbFfeE04612f98A0d", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0a50C93c762fDD6E56D86215C24AaAD43aB629aa/logo.png", + "symbol" : "LGO", + "name" : "LGO Token", + "contractAddress" : "0x0a50C93c762fDD6E56D86215C24AaAD43aB629aa", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6149C26Cd2f7b5CCdb32029aF817123F6E37Df5B/logo.png", + "symbol" : "LPOOL", + "name" : "Launchpool token", + "contractAddress" : "0x6149C26Cd2f7b5CCdb32029aF817123F6E37Df5B", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x80fB784B7eD66730e8b1DBd9820aFD29931aab03/logo.png", + "symbol" : "LEND", + "name" : "Lend", + "contractAddress" : "0x80fB784B7eD66730e8b1DBd9820aFD29931aab03", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0000000000095413afC295d19EDeb1Ad7B71c952/logo.png", + "symbol" : "LON", + "name" : "Tokenlon", + "contractAddress" : "0x0000000000095413afC295d19EDeb1Ad7B71c952", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4de2573e27E648607B50e1Cfff921A33E4A34405/logo.png", + "symbol" : "LST", + "name" : "Lendroid Support Token", + "contractAddress" : "0x4de2573e27E648607B50e1Cfff921A33E4A34405", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "LOOM", + "name" : "Loom", + "contractAddress" : "0xa4e8c3ec456107ea67d3075bf9e3df3a75823db0", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x3DB6Ba6ab6F95efed1a6E794caD492fAAabF294D/logo.png", + "symbol" : "LTO", + "name" : "LTO Network Token", + "contractAddress" : "0x3DB6Ba6ab6F95efed1a6E794caD492fAAabF294D", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA8b919680258d369114910511cc87595aec0be6D/logo.png", + "symbol" : "LYXe", + "name" : "LUKSO Token", + "contractAddress" : "0xA8b919680258d369114910511cc87595aec0be6D", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa393473d64d2F9F026B60b6Df7859A689715d092/logo.png", + "symbol" : "LTX", + "name" : "Lattice Token", + "contractAddress" : "0xa393473d64d2F9F026B60b6Df7859A689715d092", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "LUN", + "name" : "Lunyr", + "contractAddress" : "0xfa05A73FfE78ef8f1a739473e462c54bae6567D9", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xd2877702675e6cEb975b4A1dFf9fb7BAF4C91ea9/logo.png", + "symbol" : "LUNA", + "name" : "Wrapped LUNA Token", + "contractAddress" : "0xd2877702675e6cEb975b4A1dFf9fb7BAF4C91ea9", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5f98805A4E8be255a32880FDeC7F6728C6568bA0/logo.png", + "symbol" : "LUSD", + "name" : "LUSD Stablecoin", + "contractAddress" : "0x5f98805A4E8be255a32880FDeC7F6728C6568bA0", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB4d930279552397bbA2ee473229f89Ec245bc365/logo.png", + "symbol" : "MAHA", + "name" : "MahaDAO", + "contractAddress" : "0xB4d930279552397bbA2ee473229f89Ec245bc365", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xfC98e825A2264D890F9a1e68ed50E1526abCcacD/logo.png", + "symbol" : "MCO2", + "name" : "Moss Carbon Credit", + "contractAddress" : "0xfC98e825A2264D890F9a1e68ed50E1526abCcacD", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xAd4f86a25bbc20FfB751f2FAC312A0B4d8F88c64/logo.png", + "symbol" : "ROOM", + "name" : "OptionRoom Token", + "contractAddress" : "0xAd4f86a25bbc20FfB751f2FAC312A0B4d8F88c64", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5a666c7d92E5fA7Edcb6390E4efD6d0CDd69cF37/logo.png", + "symbol" : "MARSH", + "name" : "UnmarshalToken", + "contractAddress" : "0x5a666c7d92E5fA7Edcb6390E4efD6d0CDd69cF37", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x69af81e73A73B40adF4f3d4223Cd9b1ECE623074/logo.png", + "symbol" : "MASK", + "name" : "Mask Network", + "contractAddress" : "0x69af81e73A73B40adF4f3d4223Cd9b1ECE623074", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4e352cF164E64ADCBad318C3a1e222E9EBa4Ce42/logo.png", + "symbol" : "MCB", + "name" : "MCDEX Token", + "contractAddress" : "0x4e352cF164E64ADCBad318C3a1e222E9EBa4Ce42", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0/logo.png", + "symbol" : "MATIC", + "name" : "Matic Token", + "contractAddress" : "0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xD5525D397898e5502075Ea5E830d8914f6F0affe/logo.png", + "symbol" : "MEME", + "name" : "MEME", + "contractAddress" : "0xD5525D397898e5502075Ea5E830d8914f6F0affe", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2/logo.png", + "symbol" : "MKR", + "name" : "MakerDAO", + "contractAddress" : "0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa3BeD4E1c75D00fa6f4E5E6922DB7261B5E9AcD2/logo.png", + "symbol" : "MTA", + "name" : "Meta", + "contractAddress" : "0xa3BeD4E1c75D00fa6f4E5E6922DB7261B5E9AcD2", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "symbol" : "MTL", + "name" : "Metal", + "contractAddress" : "0xF433089366899D83a9f26A773D59ec7eCF30355e", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x72e364F2ABdC788b7E918bc238B21f109Cd634D7/logo.png", + "symbol" : "MVI", + "name" : "Metaverse Index", + "contractAddress" : "0x72e364F2ABdC788b7E918bc238B21f109Cd634D7", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB26631c6dda06aD89B93C71400D25692de89c068/logo.png", + "symbol" : "MINDS", + "name" : "Minds", + "contractAddress" : "0xB26631c6dda06aD89B93C71400D25692de89c068", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xEA1ea0972fa092dd463f2968F9bB51Cc4c981D71/logo.png", + "symbol" : "MOD", + "name" : "Modefi", + "contractAddress" : "0xEA1ea0972fa092dd463f2968F9bB51Cc4c981D71", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "symbol" : "MCO", + "name" : "Monaco", + "contractAddress" : "0xB63B606Ac810a52cCa15e44bB630fd42D8d1d83d", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "NAS", + "name" : "Nebulas", + "contractAddress" : "0x5d65D971895Edc438f465c17DB6992698a52318D", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xaAAf91D9b90dF800Df4F55c205fd6989c977E73a/logo.png", + "symbol" : "TKN", + "name" : "Monolith TKN", + "contractAddress" : "0xaAAf91D9b90dF800Df4F55c205fd6989c977E73a", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa8c8CfB141A3bB59FEA1E2ea6B79b5ECBCD7b6ca/logo.png", + "symbol" : "NOIA", + "name" : "NOIA Token", + "contractAddress" : "0xa8c8CfB141A3bB59FEA1E2ea6B79b5ECBCD7b6ca", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB62132e35a6c13ee1EE0f84dC5d40bad8d815206/logo.png", + "symbol" : "NEXO", + "name" : "Nexo", + "contractAddress" : "0xB62132e35a6c13ee1EE0f84dC5d40bad8d815206", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1cBb83EbcD552D5EBf8131eF8c9CD9d9BAB342bC/logo.png", + "symbol" : "NFY", + "name" : "Non-Fungible Yearn", + "contractAddress" : "0x1cBb83EbcD552D5EBf8131eF8c9CD9d9BAB342bC", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "NMR", + "name" : "Numeraire", + "contractAddress" : "0x1776e1F26f98b1A5dF9cD347953a26dd3Cb46671", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x967da4048cD07aB37855c090aAF366e4ce1b9F48/logo.png", + "symbol" : "OCEAN", + "name" : "Ocean Protocol", + "contractAddress" : "0x967da4048cD07aB37855c090aAF366e4ce1b9F48", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets//logo.png", + "symbol" : "OMG", + "name" : "OMG Network", + "contractAddress" : "0xd26114cd6EE289AccF82350c8d8487fedB8A0C07", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4c11249814f11b9346808179Cf06e71ac328c1b5/logo.png", + "symbol" : "ORAI", + "name" : "Oraichain Token", + "contractAddress" : "0x4c11249814f11b9346808179Cf06e71ac328c1b5", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8207c1FfC5B6804F6024322CcF34F29c3541Ae26/logo.png", + "symbol" : "OGN", + "name" : "OriginToken", + "contractAddress" : "0x8207c1FfC5B6804F6024322CcF34F29c3541Ae26", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0258F474786DdFd37ABCE6df6BBb1Dd5dfC4434a/logo.png", + "symbol" : "ORN", + "name" : "Orion Protocol", + "contractAddress" : "0x0258F474786DdFd37ABCE6df6BBb1Dd5dfC4434a", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "OST", + "name" : "OST", + "contractAddress" : "0x2C4e8f2D746113d0696cE89B35F0d8bF88E0AEcA", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x21BfBDa47A0B4B5b1248c767Ee49F7caA9B23697/logo.png", + "symbol" : "OVR", + "name" : "OVR", + "contractAddress" : "0x21BfBDa47A0B4B5b1248c767Ee49F7caA9B23697", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4575f41308EC1483f3d399aa9a2826d74Da13Deb/logo.png", + "symbol" : "OXT", + "name" : "Orchid", + "contractAddress" : "0x4575f41308EC1483f3d399aa9a2826d74Da13Deb", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1614F18Fc94f47967A3Fbe5FfcD46d4e7Da3D787/logo.png", + "symbol" : "PAID", + "name" : "PAID Network", + "contractAddress" : "0x1614F18Fc94f47967A3Fbe5FfcD46d4e7Da3D787", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8c8687fC965593DFb2F0b4EAeFD55E9D8df348df/logo.png", + "symbol" : "PAID", + "name" : "PAID Network", + "contractAddress" : "0x8c8687fC965593DFb2F0b4EAeFD55E9D8df348df", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x45804880De22913dAFE09f4980848ECE6EcbAf78/logo.png", + "symbol" : "PAXG", + "name" : "Paxos Gold", + "contractAddress" : "0x45804880De22913dAFE09f4980848ECE6EcbAf78", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "PAX", + "name" : "Paxos Standard", + "contractAddress" : "0x8e870d67f660d95d5be530380d0ec0bd388289e1", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xbC396689893D065F41bc2C6EcbeE5e0085233447/logo.png", + "symbol" : "PERP", + "name" : "Perpetual", + "contractAddress" : "0xbC396689893D065F41bc2C6EcbeE5e0085233447", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x429881672B9AE42b8EbA0E26cD9C73711b891Ca5/logo.png", + "symbol" : "PICKLE", + "name" : "PickleToken", + "contractAddress" : "0x429881672B9AE42b8EbA0E26cD9C73711b891Ca5", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x93ED3FBe21207Ec2E8f2d3c3de6e058Cb73Bc04d/logo.png", + "symbol" : "PNK", + "name" : "Pinakion", + "contractAddress" : "0x93ED3FBe21207Ec2E8f2d3c3de6e058Cb73Bc04d", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "symbol" : "POE", + "name" : "Po.et", + "contractAddress" : "0x0e0989b1f9b8a38983c2ba8053269ca62ec9b195", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "PAL", + "name" : "PolicyPal Network", + "contractAddress" : "0xfeDAE5642668f8636A11987Ff386bfd215F942EE", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8B39B70E39Aa811b69365398e0aACe9bee238AEb/logo.png", + "symbol" : "PKF", + "name" : "PolkaFoundry", + "contractAddress" : "0x8B39B70E39Aa811b69365398e0aACe9bee238AEb", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xD478161C952357F05f0292B56012Cd8457F1cfbF/logo.png", + "symbol" : "POLK", + "name" : "Polkamarkets", + "contractAddress" : "0xD478161C952357F05f0292B56012Cd8457F1cfbF", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x83e6f1E41cdd28eAcEB20Cb649155049Fac3D5Aa/logo.png", + "symbol" : "POLS", + "name" : "PolkastarterToken", + "contractAddress" : "0x83e6f1E41cdd28eAcEB20Cb649155049Fac3D5Aa", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "PRL", + "name" : "Oyster Pearl", + "contractAddress" : "0x1844b21593262668b7248d0f57a220caaba46ab9", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x362bc847A3a9637d3af6624EeC853618a43ed7D2/logo.png", + "symbol" : "PRQ", + "name" : "Parsiq Token", + "contractAddress" : "0x362bc847A3a9637d3af6624EeC853618a43ed7D2", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "symbol" : "PTOY", + "name" : "Patientory", + "contractAddress" : "0x8Ae4BF2C33a8e667de34B54938B0ccD03Eb8CC06", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1796ae0b0fa4862485106a0de9b654eFE301D0b2/logo.png", + "symbol" : "PMON", + "name" : "Polkamon", + "contractAddress" : "0x1796ae0b0fa4862485106a0de9b654eFE301D0b2", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "POLY", + "name" : "Polymath", + "contractAddress" : "0x9992eC3cF6A55b00978cdDF2b27BC6882d88D1eC", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "symbol" : "PPT", + "name" : "Populous", + "contractAddress" : "0xd4fa1460F537bb9085d22C7bcCB5DD450Ef28e3a", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 6, + "symbol" : "POWR", + "name" : "Power Ledger", + "contractAddress" : "0x595832f8fc6bf59c85c527fec3740a1b7a361269", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC57d533c50bC22247d49a368880fb49a1caA39F7/logo.png", + "symbol" : "PTF", + "name" : "PowerTrade Fuel Token", + "contractAddress" : "0xC57d533c50bC22247d49a368880fb49a1caA39F7", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0CDF9acd87E940837ff21BB40c9fd55F68bba059/logo.png", + "symbol" : "MINT", + "name" : "Public Mint", + "contractAddress" : "0x0CDF9acd87E940837ff21BB40c9fd55F68bba059", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 6, + "symbol" : "QASH", + "name" : "QASH", + "contractAddress" : "0x618e75ac90b12c6049ba3b27f5d5f8651b0037f6", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4a220E6096B25EADb88358cb44068A3248254675/logo.png", + "symbol" : "QNT", + "name" : "Quant", + "contractAddress" : "0x4a220E6096B25EADb88358cb44068A3248254675", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6e0daDE58D2d89eBBe7aFc384e3E4f15b70b14D8/logo.png", + "symbol" : "QRX", + "name" : "QuiverX", + "contractAddress" : "0x6e0daDE58D2d89eBBe7aFc384e3E4f15b70b14D8", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x33D0568941C0C64ff7e0FB4fbA0B11BD37deEd9f/logo.png", + "symbol" : "RAMP", + "name" : "RAMP DEFI", + "contractAddress" : "0x33D0568941C0C64ff7e0FB4fbA0B11BD37deEd9f", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 6, + "symbol" : "RC1U03", + "name" : "RentalCoin 1.0", + "contractAddress" : "0x7384db1aaa06364d1e6e67f5434391d3eea2a038", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "RDN", + "name" : "Raiden Network", + "contractAddress" : "0x255aa6df07540cb5d3d297f0d0d4d84cb52bc8e6", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xf1f955016EcbCd7321c7266BccFB96c68ea5E49b/logo.png", + "symbol" : "RLY", + "name" : "Rally", + "contractAddress" : "0xf1f955016EcbCd7321c7266BccFB96c68ea5E49b", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA4EED63db85311E22dF4473f87CcfC3DaDCFA3E3/logo.png", + "symbol" : "RBC", + "name" : "Rubic", + "contractAddress" : "0xA4EED63db85311E22dF4473f87CcfC3DaDCFA3E3", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8f8221aFbB33998d8584A2B05749bA73c37a938a/logo.png", + "symbol" : "REQ", + "name" : "Request Token", + "contractAddress" : "0x8f8221aFbB33998d8584A2B05749bA73c37a938a", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8762db106B2c2A0bccB3A80d1Ed41273552616E8/logo.png", + "symbol" : "RSR", + "name" : "Reserve Rights", + "contractAddress" : "0x8762db106B2c2A0bccB3A80d1Ed41273552616E8", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 9, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7dE91B204C1C737bcEe6F000AAA6569Cf7061cb7/logo.png", + "symbol" : "XRT", + "name" : "Robonomics", + "contractAddress" : "0x7dE91B204C1C737bcEe6F000AAA6569Cf7061cb7", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xCb5f72d37685C3D5aD0bB5F982443BC8FcdF570E/logo.png", + "symbol" : "ROOT", + "name" : "RootKit", + "contractAddress" : "0xCb5f72d37685C3D5aD0bB5F982443BC8FcdF570E", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x3155BA85D5F96b2d030a4966AF206230e46849cb/logo.png", + "symbol" : "RUNE", + "name" : "THORChain ETH.RUNE", + "contractAddress" : "0x3155BA85D5F96b2d030a4966AF206230e46849cb", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "SAN", + "name" : "SAN", + "contractAddress" : "0x7C5A0CE9267ED19B22F8cae653F198e3E8daf098", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE/logo.png", + "symbol" : "SHIB", + "name" : "SHIBA INU", + "contractAddress" : "0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x72e9D9038cE484EE986FEa183f8d8Df93f9aDA13/logo.png", + "symbol" : "SMARTCREDIT", + "name" : "SMARTCREDIT Token", + "contractAddress" : "0x72e9D9038cE484EE986FEa183f8d8Df93f9aDA13", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "SRN", + "name" : "SIRIN", + "contractAddress" : "0x68d57c9a1C35f63E2c83eE8e49A64e9d70528D25", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "SNM", + "name" : "SONM", + "contractAddress" : "0x983F6d60db79ea8cA4eB9968C6aFf8cfA04B3c63", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets//logo.png", + "symbol" : "SNX", + "name" : "Synthetix Network Token", + "contractAddress" : "0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x23B608675a2B2fB1890d3ABBd85c5775c51691d5/logo.png", + "symbol" : "SOCKS", + "name" : "Unisocks Edition 0", + "contractAddress" : "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0Ae055097C6d159879521C384F1D2123D1f195e6/logo.png", + "symbol" : "STAKE", + "name" : "xDai", + "contractAddress" : "0x0Ae055097C6d159879521C384F1D2123D1f195e6", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "SUB", + "name" : "Substratum", + "contractAddress" : "0x8D75959f1E61EC2571aa72798237101F084DE63a", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xEa319e87Cf06203DAe107Dd8E5672175e3Ee976c/logo.png", + "symbol" : "SURF", + "name" : "SURF.Finance", + "contractAddress" : "0xEa319e87Cf06203DAe107Dd8E5672175e3Ee976c", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xCC4304A31d09258b0029eA7FE63d032f52e44EFe/logo.png", + "symbol" : "SWAP", + "name" : "TrustSwap Token", + "contractAddress" : "0xCC4304A31d09258b0029eA7FE63d032f52e44EFe", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB6ff96B8A8d214544Ca0dBc9B33f7AD6503eFD32/logo.png", + "symbol" : "SYNC", + "name" : "SYNC", + "contractAddress" : "0xB6ff96B8A8d214544Ca0dBc9B33f7AD6503eFD32", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB987D48Ed8f2C468D52D6405624EADBa5e76d723/logo.png", + "symbol" : "STBZ", + "name" : "Stabilize Token", + "contractAddress" : "0xB987D48Ed8f2C468D52D6405624EADBa5e76d723", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "SNT", + "name" : "Status", + "contractAddress" : "0x744d70FDBE2Ba4CF95131626614a1763DF805B9E", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "SEED", + "name" : "Superbloom Network", + "contractAddress" : "0x4e7bd88e3996f48e2a24d15e37ca4c02b4d134d2", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1C9922314ED1415c95b9FD453c3818fd41867d0B/logo.png", + "symbol" : "TOWER", + "name" : "TOWER", + "contractAddress" : "0x1C9922314ED1415c95b9FD453c3818fd41867d0B", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xd084B83C305daFD76AE3E1b4E1F1fe2eCcCb3988/logo.png", + "symbol" : "TVK", + "name" : "Terra Virtua Kolect", + "contractAddress" : "0xd084B83C305daFD76AE3E1b4E1F1fe2eCcCb3988", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xaA7a9CA87d3694B5755f213B5D04094b8d0F0A6F/logo.png", + "symbol" : "TRAC", + "name" : "Trace Token", + "contractAddress" : "0xaA7a9CA87d3694B5755f213B5D04094b8d0F0A6F", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6F87D756DAf0503d08Eb8993686c7Fc01Dc44fB1/logo.png", + "symbol" : "TRADE", + "name" : "UniTrade", + "contractAddress" : "0x6F87D756DAf0503d08Eb8993686c7Fc01Dc44fB1", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xc7283b66Eb1EB5FB86327f08e1B5816b0720212B/logo.png", + "symbol" : "TRIBE", + "name" : "Tribe", + "contractAddress" : "0xc7283b66Eb1EB5FB86327f08e1B5816b0720212B", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0000000000085d4780B73119b644AE5ecd22b376/logo.png", + "symbol" : "TUSD", + "name" : "TrueUSD", + "contractAddress" : "0x0000000000085d4780B73119b644AE5ecd22b376", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4C19596f5aAfF459fA38B0f7eD92F11AE6543784/logo.png", + "symbol" : "TRU", + "name" : "TrustToken", + "contractAddress" : "0x4C19596f5aAfF459fA38B0f7eD92F11AE6543784", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8400D94A5cb0fa0D041a3788e395285d61c9ee5e/logo.png", + "symbol" : "UBT", + "name" : "Unibright", + "contractAddress" : "0x8400D94A5cb0fa0D041a3788e395285d61c9ee5e", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "symbol" : "UCASH", + "name" : "UCASH", + "contractAddress" : "0x92e52a1a235d9a103d970901066ce910aacefd37", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x04Fa0d235C4abf4BcF4787aF4CF447DE572eF828/logo.png", + "symbol" : "UMA", + "name" : "UMA Voting Token v1", + "contractAddress" : "0x04Fa0d235C4abf4BcF4787aF4CF447DE572eF828", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6fC13EACE26590B80cCCAB1ba5d51890577D83B2/logo.png", + "symbol" : "UMB", + "name" : "Umbrella", + "contractAddress" : "0x6fC13EACE26590B80cCCAB1ba5d51890577D83B2", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9Ed8e7C9604790F7Ec589F99b94361d8AAB64E5E/logo.png", + "symbol" : "UNISTAKE", + "name" : "Unistake", + "contractAddress" : "0x9Ed8e7C9604790F7Ec589F99b94361d8AAB64E5E", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x10Be9a8dAe441d276a5027936c3aADEd2d82bC15/logo.png", + "symbol" : "UMX", + "name" : "https://unimex.network/", + "contractAddress" : "0x10Be9a8dAe441d276a5027936c3aADEd2d82bC15", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x226f7b842E0F0120b7E194D05432b3fd14773a9D/logo.png", + "symbol" : "UNN", + "name" : "UNION Protocol Governance Token", + "contractAddress" : "0x226f7b842E0F0120b7E194D05432b3fd14773a9D", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "VIB", + "name" : "Viberate", + "contractAddress" : "0x2C974B2d0BA1716E644c1FC59982a89DDD2fF724", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x3D3D35bb9bEC23b06Ca00fe472b50E7A4c692C30/logo.png", + "symbol" : "VIDYA", + "name" : "Vidya", + "contractAddress" : "0x3D3D35bb9bEC23b06Ca00fe472b50E7A4c692C30", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xF938424F7210f31dF2Aee3011291b658f872e91e/logo.png", + "symbol" : "VISR", + "name" : "VISOR", + "contractAddress" : "0xF938424F7210f31dF2Aee3011291b658f872e91e", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "symbol" : "WAX", + "name" : "WAX", + "contractAddress" : "0x39Bb259F66E1C59d5ABEF88375979b4D20D98022", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7a2Bc711E19ba6aff6cE8246C546E8c4B4944DFD/logo.png", + "symbol" : "WAXE", + "name" : "WAX Economic Token", + "contractAddress" : "0x7a2Bc711E19ba6aff6cE8246C546E8c4B4944DFD", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "WINGS", + "name" : "Wings", + "contractAddress" : "0x667088b212ce3d06a1b553a7221E1fD19000d9aF", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x66a0f676479Cee1d7373f3DC2e2952778BfF5bd6/logo.png", + "symbol" : "WISE", + "name" : "Wise Token", + "contractAddress" : "0x66a0f676479Cee1d7373f3DC2e2952778BfF5bd6", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xBF494F02EE3FdE1F20BEE6242bCe2d1ED0c15e47/logo.png", + "symbol" : "WORLD", + "name" : "World Token", + "contractAddress" : "0xBF494F02EE3FdE1F20BEE6242bCe2d1ED0c15e47", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 6, + "symbol" : "WRC", + "name" : "Worldcore", + "contractAddress" : "0x72adadb447784dd7ab1f472467750fc485e4cb2d", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599/logo.png", + "symbol" : "WBTC", + "name" : "Wrapped BTC", + "contractAddress" : "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", + "symbol" : "WETH", + "name" : "Wrapped Ethereum", + "contractAddress" : "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x09a3EcAFa817268f77BE1283176B946C4ff2E608/logo.png", + "symbol" : "MIR", + "name" : "Wrapped MIR Token", + "contractAddress" : "0x09a3EcAFa817268f77BE1283176B946C4ff2E608", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8888801aF4d980682e47f1A9036e589479e835C5/logo.png", + "symbol" : "MPH", + "name" : "88mph.app", + "contractAddress" : "0x8888801aF4d980682e47f1A9036e589479e835C5", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9469D013805bFfB7D3DEBe5E7839237e535ec483/logo.png", + "symbol" : "RING", + "name" : "Evolution Land Global Token", + "contractAddress" : "0x9469D013805bFfB7D3DEBe5E7839237e535ec483", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 0, + "symbol" : "XJP", + "name" : "Digital JPY", + "contractAddress" : "0x39689fE671C01fcE173395f6BC45D4C332026666", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xF063fE1aB7a291c5d06a86e14730b00BF24cB589/logo.png", + "symbol" : "SALE", + "name" : "DxSale.Network", + "contractAddress" : "0xF063fE1aB7a291c5d06a86e14730b00BF24cB589", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "XUC", + "name" : "Exchange Union", + "contractAddress" : "0xc324a2f6b05880503444451b8b27e6f9e63287cb", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xee573a945B01B788B9287CE062A0CFC15bE9fd86/logo.png", + "symbol" : "XED", + "name" : "Exeedme", + "contractAddress" : "0xee573a945B01B788B9287CE062A0CFC15bE9fd86", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x2e1E15C44Ffe4Df6a0cb7371CD00d5028e571d14/logo.png", + "symbol" : "MTLX", + "name" : "Mettalex", + "contractAddress" : "0x2e1E15C44Ffe4Df6a0cb7371CD00d5028e571d14", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "NCASH", + "name" : "Nucleus Vision", + "contractAddress" : "0x809826cceab68c387726af962713b64cb5cb3cca", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA15C7Ebe1f07CaF6bFF097D8a589fb8AC49Ae5B3/logo.png", + "symbol" : "NPXS", + "name" : "Pundi X Token", + "contractAddress" : "0xA15C7Ebe1f07CaF6bFF097D8a589fb8AC49Ae5B3", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4fE83213D56308330EC302a8BD641f1d0113A4Cc/logo.png", + "symbol" : "NU", + "name" : "NuCypher", + "contractAddress" : "0x4fE83213D56308330EC302a8BD641f1d0113A4Cc", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "symbol" : "QRL", + "name" : "QRL", + "contractAddress" : "0x697beac28B09E122C4332D163985e8a73121b97F", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "PAY", + "name" : "TenX", + "contractAddress" : "0xB97048628DB6B661D4C2aA833e95Dbe1A905B280", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xe3818504c1B32bF1557b16C238B2E01Fd3149C17/logo.png", + "symbol" : "PLR", + "name" : "PILLAR", + "contractAddress" : "0xe3818504c1B32bF1557b16C238B2E01Fd3149C17", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "PMTN", + "name" : "Peer Mountain", + "contractAddress" : "0xe91df2bb9bccd18c45f01843cab88768d64f2d32", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5BEfBB272290dD5b8521D4a938f6c4757742c430/logo.png", + "symbol" : "XFI", + "name" : "Xfinance", + "contractAddress" : "0x5BEfBB272290dD5b8521D4a938f6c4757742c430", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0f7F961648aE6Db43C75663aC7E5414Eb79b5704/logo.png", + "symbol" : "XIO", + "name" : "XIO Network", + "contractAddress" : "0x0f7F961648aE6Db43C75663aC7E5414Eb79b5704", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + + }, + { + "decimalCount" : 9, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x607F4C5BB672230e8672085532f7e901544a7375/logo.png", + "symbol" : "RLC", + "name" : "iExec RLC", + "contractAddress" : "0x607F4C5BB672230e8672085532f7e901544a7375", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x89Ab32156e46F46D02ade3FEcbe5Fc4243B9AAeD/logo.png", + "symbol" : "PNT", + "name" : "pNetwork Token", + "contractAddress" : "0x89Ab32156e46F46D02ade3FEcbe5Fc4243B9AAeD", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x69A95185ee2a045CDC4bCd1b1Df10710395e4e23/logo.png", + "symbol" : "POOLZ", + "name" : "$Poolz Finance", + "contractAddress" : "0x69A95185ee2a045CDC4bCd1b1Df10710395e4e23", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + }, + "symbol" : "QCX", + "name" : "QuickX Protocol", + "contractAddress" : "0xf9e5af7b42d31d51677c75bbbd37c1986ec79aee", + "customIcon" : "qcx" + }, + { + "decimalCount" : 18, + "symbol" : "QSP", + "name" : "Quantstamp", + "contractAddress" : "0x99ea4dB9EE77ACD40B119BD1dC4E33e1C070b80d", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x03ab458634910AaD20eF5f1C8ee96F1D6ac54919/logo.png", + "symbol" : "RAI", + "name" : "Rai Reflex Index", + "contractAddress" : "0x03ab458634910AaD20eF5f1C8ee96F1D6ac54919", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xFca59Cd816aB1eaD66534D82bc21E7515cE441CF/logo.png", + "symbol" : "RARI", + "name" : "Rarible", + "contractAddress" : "0xFca59Cd816aB1eaD66534D82bc21E7515cE441CF", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x50DE6856358Cc35f3A9a57eAAA34BD4cB707d2cd/logo.png", + "symbol" : "RAZOR", + "name" : "RAZOR", + "contractAddress" : "0x50DE6856358Cc35f3A9a57eAAA34BD4cB707d2cd", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "RCN", + "name" : "Ripio Credit", + "contractAddress" : "0xf970b8e36e23f7fc3fd752eea86f8be8d83375a6", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xFE3E6a25e6b192A42a44ecDDCd13796471735ACf/logo.png", + "symbol" : "REEF", + "name" : "Reef.finance", + "contractAddress" : "0xFE3E6a25e6b192A42a44ecDDCd13796471735ACf", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x408e41876cCCDC0F92210600ef50372656052a38/logo.png", + "symbol" : "REN", + "name" : "Republic Token", + "contractAddress" : "0x408e41876cCCDC0F92210600ef50372656052a38", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1985365e9f78359a9B6AD760e32412f4a445E862/logo.png", + "symbol" : "REP", + "name" : "Reputation", + "contractAddress" : "0x1985365e9f78359a9B6AD760e32412f4a445E862", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x557B933a7C2c45672B610F8954A3deB39a51A8Ca/logo.png", + "symbol" : "REVV", + "name" : "REVV", + "contractAddress" : "0x557B933a7C2c45672B610F8954A3deB39a51A8Ca", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa1d6Df714F91DeBF4e0802A542E13067f31b8262/logo.png", + "symbol" : "RFOX", + "name" : "RFOX", + "contractAddress" : "0xa1d6Df714F91DeBF4e0802A542E13067f31b8262", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "RUFF", + "name" : "RUFF", + "contractAddress" : "0xf278c1ca969095ffddded020290cf8b5c424ace2", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xf21661D0D1d76d3ECb8e1B9F1c923DBfffAe4097/logo.png", + "symbol" : "RIO", + "name" : "Realio Network", + "contractAddress" : "0xf21661D0D1d76d3ECb8e1B9F1c923DBfffAe4097", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "RCC", + "name" : "Reality Clash", + "contractAddress" : "0x9b6443b0fb9c241a7fdac375595cea13e6b7807a", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x221657776846890989a759BA2973e427DfF5C9bB/logo.png", + "symbol" : "REPv2", + "name" : "Reputation", + "contractAddress" : "0x221657776846890989a759BA2973e427DfF5C9bB", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 0, + "symbol" : "REV", + "name" : "Revain", + "contractAddress" : "0x48f775efbe4f5ece6e0df2f7b5932df56823b990", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xaf9f549774ecEDbD0966C52f250aCc548D3F36E5/logo.png", + "symbol" : "RFuel", + "name" : "Rio Fuel Token", + "contractAddress" : "0xaf9f549774ecEDbD0966C52f250aCc548D3F36E5", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "RVT", + "name" : "Rivetz", + "contractAddress" : "0x3d1ba9be9f66b8ee101911bc36d3fb562eac2244", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB4EFd85c19999D84251304bDA99E90B92300Bd93/logo.png", + "symbol" : "RPL", + "name" : "Rocket Pool", + "contractAddress" : "0xB4EFd85c19999D84251304bDA99E90B92300Bd93", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x3845badAde8e6dFF049820680d1F14bD3903a5d0/logo.png", + "symbol" : "SAND", + "name" : "SAND", + "contractAddress" : "0x3845badAde8e6dFF049820680d1F14bD3903a5d0", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "symbol" : "SENT", + "name" : "SENTinel", + "contractAddress" : "0xa44e5137293e855b1b7bc7e2c6f8cd796ffcb037", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xD23Ac27148aF6A2f339BD82D0e3CFF380b5093de/logo.png", + "symbol" : "SI", + "name" : "SIREN", + "contractAddress" : "0xD23Ac27148aF6A2f339BD82D0e3CFF380b5093de", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "STK", + "name" : "STK", + "contractAddress" : "0xae73b38d1c9a8b274127ec30160a4927c4d71824", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "SAI", + "name" : "Sai", + "contractAddress" : "0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "symbol" : "SALT", + "name" : "SALT", + "contractAddress" : "0x4156D3342D5c385a87D264F90653733592000581", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9B02dD390a603Add5c07f9fd9175b7DABE8D63B7/logo.png", + "symbol" : "SPI", + "name" : "Shopping.io", + "contractAddress" : "0x9B02dD390a603Add5c07f9fd9175b7DABE8D63B7", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 0, + "symbol" : "SNGLS", + "name" : "Singular DTV", + "contractAddress" : "0xaeC2E87E0A235266D9C5ADc9DEb4b2E29b54D009", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 0, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x37236CD05b34Cc79d3715AF2383E96dd7443dCF1/logo.png", + "symbol" : "SLP", + "name" : "Small Love Potion", + "contractAddress" : "0x37236CD05b34Cc79d3715AF2383E96dd7443dCF1", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x06A01a4d579479Dd5D884EBf61A31727A3d8D442/logo.png", + "symbol" : "Skey", + "name" : "SmartKey", + "contractAddress" : "0x06A01a4d579479Dd5D884EBf61A31727A3d8D442", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0488401c3F535193Fa8Df029d9fFe615A06E74E6/logo.png", + "symbol" : "SRK", + "name" : "SparkPoint", + "contractAddress" : "0x0488401c3F535193Fa8Df029d9fFe615A06E74E6", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xb753428af26E81097e7fD17f40c88aaA3E04902c/logo.png", + "symbol" : "SFI", + "name" : "Spice", + "contractAddress" : "0xb753428af26E81097e7fD17f40c88aaA3E04902c", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x00c83aeCC790e8a4453e5dD3B0B4b3680501a7A7/logo.png", + "symbol" : "SKL", + "name" : "SKALE", + "contractAddress" : "0x00c83aeCC790e8a4453e5dD3B0B4b3680501a7A7", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x40FD72257597aA14C7231A7B1aaa29Fce868F677/logo.png", + "symbol" : "XOR", + "name" : "Sora", + "contractAddress" : "0x40FD72257597aA14C7231A7B1aaa29Fce868F677", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "symbol" : "STORJ", + "name" : "Storj", + "contractAddress" : "0xB64ef51C888972c908CFacf59B47C1AfBC0Ab8aC", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xbE9375C6a420D2eEB258962efB95551A5b722803/logo.png", + "symbol" : "STMX", + "name" : "StormX", + "contractAddress" : "0xbE9375C6a420D2eEB258962efB95551A5b722803", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "STQ", + "name" : "Storiqa", + "contractAddress" : "0x5c3a228510d246b78a3765c20221cbf3082b44a4", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "STORM", + "name" : "Storm", + "contractAddress" : "0xD0a4b8946Cb52f0661273bfbC6fD0E0C75Fc6433", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x990f341946A3fdB507aE7e52d17851B87168017c/logo.png", + "symbol" : "STRONG", + "name" : "Strong", + "contractAddress" : "0x990f341946A3fdB507aE7e52d17851B87168017c", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "STX", + "name" : "Stox", + "contractAddress" : "0x006BeA43Baa3f7A6f765F14f10A1a1b08334EF45", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "SNC", + "name" : "SunContract", + "contractAddress" : "0xF4134146AF2d511Dd5EA8cDB1C4AC88C57D60404", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xe53EC727dbDEB9E2d5456c3be40cFF031AB40A55/logo.png", + "symbol" : "SUPER", + "name" : "SuperFarm", + "contractAddress" : "0xe53EC727dbDEB9E2d5456c3be40cFF031AB40A55", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6B3595068778DD592e39A122f4f5a5cF09C90fE2/logo.png", + "symbol" : "SUSHI", + "name" : "SushiSwap", + "contractAddress" : "0x6B3595068778DD592e39A122f4f5a5cF09C90fE2", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8CE9137d39326AD0cD6491fb5CC0CbA0e089b6A9/logo.png", + "symbol" : "SXP", + "name" : "Swipe", + "contractAddress" : "0x8CE9137d39326AD0cD6491fb5CC0CbA0e089b6A9", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9f7229aF0c4b9740e207Ea283b9094983f78ba04/logo.png", + "symbol" : "TAD", + "name" : "Tadpole", + "contractAddress" : "0x9f7229aF0c4b9740e207Ea283b9094983f78ba04", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa1eD0364D53394209D61aE8bfdb8Ff50484D8c91/logo.png", + "symbol" : "TBC", + "name" : "TeraBlock Token", + "contractAddress" : "0xa1eD0364D53394209D61aE8bfdb8Ff50484D8c91", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "THETA", + "name" : "Theta Token", + "contractAddress" : "0x3883f5e181fccaf8410fa61e12b59bad963fb645", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x77777FeDdddFfC19Ff86DB637967013e6C6A116C/logo.png", + "symbol" : "TORN", + "name" : "TornadoCash", + "contractAddress" : "0x77777FeDdddFfC19Ff86DB637967013e6C6A116C", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 6, + "symbol" : "TRX", + "name" : "TRON", + "contractAddress" : "0xf230b790e05390fc8295f4d3f60332c93bed42e2", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0202Be363B8a4820f3F4DE7FaF5224fF05943AB1/logo.png", + "symbol" : "UFT", + "name" : "UniLend Finance Token", + "contractAddress" : "0x0202Be363B8a4820f3F4DE7FaF5224fF05943AB1", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 6, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png", + "symbol" : "USDC", + "name" : "USDC", + "contractAddress" : "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "STU", + "name" : "bitJob", + "contractAddress" : "0x0371a82e4a9d0a4312f3ee2ac9c6958512891372", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xBa21Ef4c9f433Ede00badEFcC2754B8E74bd538A/logo.png", + "symbol" : "SWFL", + "name" : "Swapfolio", + "contractAddress" : "0xBa21Ef4c9f433Ede00badEFcC2754B8E74bd538A", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8dAEBADE922dF735c38C80C7eBD708Af50815fAa/logo.png", + "symbol" : "TBTC", + "name" : "tBTC", + "contractAddress" : "0x8dAEBADE922dF735c38C80C7eBD708Af50815fAa", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x57Ab1ec28D129707052df4dF418D58a2D46d5f51/logo.png", + "symbol" : "sUSD", + "name" : "Synth sUSD", + "contractAddress" : "0x57Ab1ec28D129707052df4dF418D58a2D46d5f51", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 2, + "symbol" : "TEL", + "name" : "Telcoin", + "contractAddress" : "0x467bccd9d29f223bce8043b84e8c8b282827790f", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xDcB01cc464238396E213a6fDd933E36796eAfF9f/logo.png", + "symbol" : "YLD", + "name" : "Yield", + "contractAddress" : "0xDcB01cc464238396E213a6fDd933E36796eAfF9f", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7F3EDcdD180Dbe4819Bd98FeE8929b5cEdB3AdEB/logo.png", + "symbol" : "XTK", + "name" : "xToken", + "contractAddress" : "0x7F3EDcdD180Dbe4819Bd98FeE8929b5cEdB3AdEB", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xAE1eaAE3F627AAca434127644371b67B18444051/logo.png", + "symbol" : "YOP", + "name" : "YOP", + "contractAddress" : "0xAE1eaAE3F627AAca434127644371b67B18444051", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xf4CD3d3Fda8d7Fd6C5a500203e38640A70Bf9577/logo.png", + "symbol" : "Yf-DAI", + "name" : "YfDAI.finance", + "contractAddress" : "0xf4CD3d3Fda8d7Fd6C5a500203e38640A70Bf9577", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x2e2f3246b6c65CCc4239c9Ee556EC143a7E5DE2c/logo.png", + "symbol" : "YFIM", + "name" : "yfi.mobi", + "contractAddress" : "0x2e2f3246b6c65CCc4239c9Ee556EC143a7E5DE2c", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB1e9157c2Fdcc5a856C8DA8b2d89b6C32b3c1229/logo.png", + "symbol" : "ZEFU", + "name" : "Zenfuse Trading Platform Token", + "contractAddress" : "0xB1e9157c2Fdcc5a856C8DA8b2d89b6C32b3c1229", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 12, + "symbol" : "ZIL", + "name" : "Zilliqa", + "contractAddress" : "0x05f4a42e251f2d52b8ed15E9FEdAacFcEF1FAD27", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "ZLA", + "name" : "Zilla", + "contractAddress" : "0xfd8971d5e8e1740ce2d0a84095fca4de729d0c16", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xE95A203B1a91a908F9B9CE46459d101078c2c3cb/logo.png", + "symbol" : "aEth", + "name" : "aEthereum", + "contractAddress" : "0xE95A203B1a91a908F9B9CE46459d101078c2c3cb", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7b123f53421b1bF8533339BFBdc7C98aA94163db/logo.png", + "symbol" : "buidl", + "name" : "dfohub", + "contractAddress" : "0x7b123f53421b1bF8533339BFBdc7C98aA94163db", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5218E472cFCFE0b64A064F055B43b4cdC9EfD3A6/logo.png", + "symbol" : "eRSDL", + "name" : "UnFederalReserveToken", + "contractAddress" : "0x5218E472cFCFE0b64A064F055B43b4cdC9EfD3A6", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984/logo.png", + "symbol" : "UNI", + "name" : "Uniswap", + "contractAddress" : "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 6, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xdAC17F958D2ee523a2206206994597C13D831ec7/logo.png", + "symbol" : "USDT", + "name" : "Tether USD", + "contractAddress" : "0xdAC17F958D2ee523a2206206994597C13D831ec7", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "VEE", + "name" : "BLOCKv", + "contractAddress" : "0x340d2bde5eb28c1eed91b2f790723e3b160613b7", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xF411903cbC70a74d22900a5DE66A2dda66507255/logo.png", + "symbol" : "VRA", + "name" : "VERA", + "contractAddress" : "0xF411903cbC70a74d22900a5DE66A2dda66507255", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1b40183EFB4Dd766f11bDa7A7c3AD8982e998421/logo.png", + "symbol" : "VSP", + "name" : "VesperToken", + "contractAddress" : "0x1b40183EFB4Dd766f11bDa7A7c3AD8982e998421", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "symbol" : "VGX", + "name" : "Voyager", + "contractAddress" : "0x5af2be193a6abca9c8817001f45744777db30756", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 4, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9355372396e3F6daF13359B7b607a3374cc638e0/logo.png", + "symbol" : "WHALE", + "name" : "WHALE", + "contractAddress" : "0x9355372396e3F6daF13359B7b607a3374cc638e0", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "WTC", + "name" : "Waltonchain", + "contractAddress" : "0xb7cb1c96db6b22b0d3d9536e0108d062bd488f74", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4691937a7508860F876c9c0a2a617E7d9E945D4B/logo.png", + "symbol" : "WOO", + "name" : "Wootrade Network", + "contractAddress" : "0x4691937a7508860F876c9c0a2a617E7d9E945D4B", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "symbol" : "WPR", + "name" : "WePower", + "contractAddress" : "0x4CF488387F035FF08c371515562CBa712f9015d4", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5228a22e72ccC52d415EcFd199F99D0665E7733b/logo.png", + "symbol" : "pBTC", + "name" : "pTokens BTC", + "contractAddress" : "0x5228a22e72ccC52d415EcFd199F99D0665E7733b", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 8, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D/logo.png", + "symbol" : "renBTC", + "name" : "renBTC", + "contractAddress" : "0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x3383c5a8969Dc413bfdDc9656Eb80A1408E4bA20/logo.png", + "symbol" : "wANATHA", + "name" : "Wrapped ANATHA", + "contractAddress" : "0x3383c5a8969Dc413bfdDc9656Eb80A1408E4bA20", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa0afAA285Ce85974c3C881256cB7F225e3A1178a/logo.png", + "symbol" : "wCRES", + "name" : "Wrapped CRES", + "contractAddress" : "0xa0afAA285Ce85974c3C881256cB7F225e3A1178a", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e/logo.png", + "symbol" : "YFI", + "name" : "Yearn.Finance", + "contractAddress" : "0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xF94b5C5651c888d928439aB6514B93944eEE6F48/logo.png", + "symbol" : "YLD", + "name" : "Yield", + "contractAddress" : "0xF94b5C5651c888d928439aB6514B93944eEE6F48", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 10, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xb9EF770B6A5e12E45983C5D80545258aA38F3B78/logo.png", + "symbol" : "ZCN", + "name" : "0chain", + "contractAddress" : "0xb9EF770B6A5e12E45983C5D80545258aA38F3B78", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x2eDf094dB69d6Dcd487f1B3dB9febE2eeC0dd4c5/logo.png", + "symbol" : "ZEE", + "name" : "ZeroSwapToken", + "contractAddress" : "0x2eDf094dB69d6Dcd487f1B3dB9febE2eeC0dd4c5", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xE41d2489571d322189246DaFA5ebDe1F4699F498/logo.png", + "symbol" : "ZRX", + "name" : "0x Protocol Token", + "contractAddress" : "0xE41d2489571d322189246DaFA5ebDe1F4699F498", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 18, + "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1337DEF18C680aF1f9f45cBcab6309562975b1dD/logo.png", + "symbol" : "arNXM", + "name" : "Armor NXM", + "contractAddress" : "0x1337DEF18C680aF1f9f45cBcab6309562975b1dD", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "decimalCount" : 9, + "symbol" : "ADT", + "name" : "adToken", + "contractAddress" : "0xD0D6D6C5Fe4a677D343cC433536BB717bAe167dD", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : false + } + } +] diff --git a/app/src/main/assets/ethereum_tokens_testnet.json b/app/src/main/assets/ethereum_tokens_testnet.json index 5222bbe37d..8cd1d5a47e 100644 --- a/app/src/main/assets/ethereum_tokens_testnet.json +++ b/app/src/main/assets/ethereum_tokens_testnet.json @@ -3,24 +3,44 @@ "decimalCount" : 18, "symbol" : "WEENUS", "name" : "Weenus", - "contractAddress" : "0xaFF4481D10270F50f203E0763e2597776068CBc5" + "contractAddress" : "0xaFF4481D10270F50f203E0763e2597776068CBc5", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : true + } }, { "decimalCount" : 18, "symbol" : "XEENUS", "name" : "Xeenus", - "contractAddress" : "0x022E292b44B5a146F2e8ee36Ff44D3dd863C915c" + "contractAddress" : "0x022E292b44B5a146F2e8ee36Ff44D3dd863C915c", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : true + } }, { "decimalCount" : 8, "symbol" : "YEENUS", "name" : "Yeenus", - "contractAddress" : "0xc6fDe3FD2Cc2b173aEC24cc3f267cb3Cd78a26B7" + "contractAddress" : "0xc6fDe3FD2Cc2b173aEC24cc3f267cb3Cd78a26B7", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : true + } }, { "decimalCount" : 0, "symbol" : "ZEENUS", "name" : "Zeenus", - "contractAddress" : "0x1f9061B953bBa0E36BF50F21876132DcF276fC6e" + "contractAddress" : "0x1f9061B953bBa0E36BF50F21876132DcF276fC6e", + "blockchain" : { + "key" : "ethereum", + "curve" : "secp256k1", + "testnet" : true + } } ] 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 d0f72cd47b..9ae9105a46 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -69,7 +69,7 @@ class TapWalletManager { suspend fun loadFiatRate(fiatCurrency: FiatCurrencyName, wallet: Wallet) { Timber.d(wallet.getTokens().toString()) val currencies = wallet.getTokens() - .map { Currency.Token(it, wallet.blockchain) } + .map { Currency.Token(it) } .plus(Currency.Blockchain(wallet.blockchain)) loadFiatRate(fiatCurrency, currencies) } @@ -186,18 +186,18 @@ class TapWalletManager { private fun loadMultiWalletData( card: Card, primaryBlockchain: Blockchain?, primaryWalletManager: WalletManager? ) { - val presetTokens = primaryWalletManager?.presetTokens ?: emptySet() + val primaryTokens = primaryWalletManager?.cardTokens ?: emptySet() val savedCurrencies = currenciesRepository.loadCardCurrencies(card.cardId) if (savedCurrencies == null) { if (primaryBlockchain != null && primaryWalletManager != null) { store.dispatch(WalletAction.MultiWallet.SaveCurrencies( CardCurrencies( - blockchains = setOf(primaryBlockchain), tokens = presetTokens + blockchains = setOf(primaryBlockchain), tokens = primaryTokens ))) store.dispatch(WalletAction.MultiWallet.AddWalletManagers(primaryWalletManager)) store.dispatch(WalletAction.MultiWallet.AddBlockchains(listOf(primaryBlockchain))) - store.dispatch(WalletAction.MultiWallet.AddTokens(presetTokens.toList())) + store.dispatch(WalletAction.MultiWallet.AddTokens(primaryTokens.toList())) } else { val blockchains = setOf(Blockchain.Bitcoin, Blockchain.Ethereum) store.dispatch(WalletAction.MultiWallet.SaveCurrencies( @@ -213,7 +213,7 @@ class TapWalletManager { } else { val blockchains = savedCurrencies.blockchains.toList() val walletManagers = if ( - presetTokens.isNotEmpty() && + primaryTokens.isNotEmpty() && primaryWalletManager != null && primaryBlockchain != null ) { val blockchainsWithoutPrimary = blockchains.filterNot { it == primaryBlockchain } 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 69c5f0a8b0..d0f99ccabc 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 @@ -15,7 +15,7 @@ class ConfigValueModel( val coinMarketCapKey: String, val moonPayApiKey: String, val moonPayApiSecretKey: String, - val blockchairApiKey: String?, + val blockchairAuthorizationToken: String?, val blockcypherTokens: Set?, val infuraProjectId: String?, ) diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt b/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt index 9e805a29de..83a6f41967 100644 --- a/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt +++ b/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt @@ -13,8 +13,9 @@ fun Card.getToken(): Token? { val symbol = cardData?.tokenSymbol ?: return null val contractAddress = cardData?.tokenContractAddress ?: return null val decimals = cardData?.tokenDecimal ?: return null + val blockchain = cardData?.blockchainName?.let { Blockchain.fromId(it) } ?: return null if (symbol.isBlank() || contractAddress.isBlank()) return null - return Token(symbol, contractAddress, decimals) + return Token(symbol, symbol, contractAddress, decimals, blockchain) // TODO: change to constructor without tokenName } fun Card.getBlockchain(): Blockchain? { 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 af149d9af5..9b07025880 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 @@ -2,6 +2,7 @@ package com.tangem.tap.domain.tokens import android.app.Application import android.content.Context +import com.squareup.moshi.Json import com.squareup.moshi.JsonAdapter import com.squareup.moshi.JsonClass import com.squareup.moshi.Types @@ -13,11 +14,14 @@ import com.tangem.tap.network.createMoshi class CurrenciesRepository(val context: Application) { private val moshi = createMoshi() + private val blockchainsAdapter: JsonAdapter> = moshi.adapter( + Types.newParameterizedType(Set::class.java, Blockchain::class.java) + ) private val tokensAdapter: JsonAdapter> = moshi.adapter( Types.newParameterizedType(Set::class.java, TokenDao::class.java) ) - private val blockchainsAdapter: JsonAdapter> = moshi.adapter( - Types.newParameterizedType(Set::class.java, Blockchain::class.java) + private val obsoleteTokensAdapter: JsonAdapter> = moshi.adapter( + Types.newParameterizedType(Set::class.java, ObsoleteTokenDao::class.java) ) fun loadCardCurrencies(cardId: String): CardCurrencies? { @@ -57,11 +61,20 @@ class CurrenciesRepository(val context: Application) { } private fun loadSavedTokens(cardId: String): Set { + val json = try { + context.readFileText(getFileNameForTokens(cardId)) + } catch (exception: Exception) { + return emptySet() + } + return try { - val json = context.readFileText(getFileNameForTokens(cardId)) tokensAdapter.fromJson(json)!!.map { it.toToken() }.toSet() } catch (exception: Exception) { - emptySet() + try { + obsoleteTokensAdapter.fromJson(json)!!.map { it.toToken() }.toSet() + } catch (exception: Exception) { + emptySet() + } } } @@ -94,12 +107,20 @@ class CurrenciesRepository(val context: Application) { } fun getPopularTokens(isTestNet: Boolean = false): List { - val fileName = if (isTestNet) TESTNET_TOKENS_FILE_NAME else POPULAR_TOKENS_FILE_NAME - val json = context.assets.readJsonFileToString(fileName) - return tokensAdapter.fromJson(json)!!.map { it.toToken() } + val ethereumTokensFileName = + if (isTestNet) ETHEREUM_TESTNET_TOKENS_FILE_NAME else ETHEREUM_TOKENS_FILE_NAME + val bscTokensFileName = + if (isTestNet) BSC_TESTNET_TOKENS_FILE_NAME else BSC_TOKENS_FILE_NAME + val ethereumTokensJson = context.assets.readJsonFileToString(ethereumTokensFileName) + val bscTokensJson = context.assets.readJsonFileToString(bscTokensFileName) + return tokensAdapter.fromJson(ethereumTokensJson)!!.map { it.toToken() } + + tokensAdapter.fromJson(bscTokensJson)!!.map { it.toToken() } } - fun getBlockchains(cardFirmware: FirmwareVersion?, isTestNet: Boolean = false): List { + fun getBlockchains( + cardFirmware: FirmwareVersion?, + isTestNet: Boolean = false + ): List { return if (cardFirmware == null || cardFirmware.major < 4) { Blockchain.secp256k1Blockchains(isTestNet) } else { @@ -108,8 +129,10 @@ class CurrenciesRepository(val context: Application) { } companion object { - private const val POPULAR_TOKENS_FILE_NAME = "erc20_tokens" - private const val TESTNET_TOKENS_FILE_NAME = "ethereum_tokens_testnet" + private const val ETHEREUM_TOKENS_FILE_NAME = "ethereum_tokens" + private const val ETHEREUM_TESTNET_TOKENS_FILE_NAME = "ethereum_tokens_testnet" + private const val BSC_TOKENS_FILE_NAME = "bsc_tokens" + private const val BSC_TESTNET_TOKENS_FILE_NAME = "bsc_tokens_testnet" private const val FILE_NAME_PREFIX_TOKENS = "tokens" private const val FILE_NAME_PREFIX_BLOCKCHAINS = "blockchains" @@ -119,40 +142,88 @@ class CurrenciesRepository(val context: Application) { } } - @JsonClass(generateAdapter = true) data class TokenDao( val name: String, val symbol: String, val contractAddress: String, val decimalCount: Int, + @Json(name = "blockchain") + val blockchainDao: BlockchainDao ) { fun toToken(): Token { - return Token(name = name, + return Token( + name = name, symbol = symbol, contractAddress = contractAddress, - decimals = decimalCount) + decimals = decimalCount, + blockchain = blockchainDao.toBlockchain() + ) } companion object { fun fromToken(token: Token): TokenDao { - return TokenDao(name = token.name, + return TokenDao( + name = token.name, symbol = token.symbol, contractAddress = token.contractAddress, - decimalCount = token.decimals) - + decimalCount = token.decimals, + blockchainDao = BlockchainDao.fromBlockchain(token.blockchain) + ) } } } +@JsonClass(generateAdapter = true) +data class BlockchainDao( + @Json(name = "key") + val name: String, + @Json(name = "testnet") + val isTestNet: Boolean +) { + fun toBlockchain(): Blockchain { + val blockchain = Blockchain.values().find { it.name.lowercase() == name.lowercase() } + ?: throw Exception("Invalid BlockchainDao") + return if (!isTestNet) blockchain else blockchain.getTestnetVersion() + ?: throw Exception("Invalid 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) + } + } +} + +@JsonClass(generateAdapter = true) +data class ObsoleteTokenDao( + val name: String, + val symbol: String, + val contractAddress: String, + val decimalCount: Int, +) { + fun toToken(): Token { + return Token( + name = name, + symbol = symbol, + contractAddress = contractAddress, + decimals = decimalCount + ) + } +} + @JsonClass(generateAdapter = true) data class CardCurrenciesDao( val tokens: Set, val blockchains: Set, ) { fun toCardCurrencies(): CardCurrencies { - return CardCurrencies(tokens = tokens.map { it.toToken() }.toSet(), - blockchains = blockchains) + return CardCurrencies( + tokens = tokens.map { it.toToken() }.toSet(), + blockchains = blockchains + ) } companion object { diff --git a/app/src/main/java/com/tangem/tap/features/tokens/ui/adapters/CurrenciesAdapter.kt b/app/src/main/java/com/tangem/tap/features/tokens/ui/adapters/CurrenciesAdapter.kt index 7767d74b32..63ed1521b9 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/ui/adapters/CurrenciesAdapter.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/ui/adapters/CurrenciesAdapter.kt @@ -11,7 +11,6 @@ import com.squareup.picasso.Picasso import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token import com.tangem.tap.common.extensions.* -import com.tangem.tap.common.redux.global.CryptoCurrencyName import com.tangem.tap.domain.tokens.CardCurrencies import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.store @@ -175,11 +174,17 @@ sealed class CurrencyListItem { tokens: List ): List { val blockchainsTitle = R.string.add_tokens_subtitle_blockchains - val tokensTitle = R.string.add_tokens_subtitle_tokens + val ethereumTokensTitle = R.string.add_tokens_subtitle_ethereum_tokens + val bscTokensTitle = R.string.add_tokens_subtitle_bsc_tokens + + val ethereumTokens = tokens.filter { it.blockchain == Blockchain.Ethereum } + val bscTokens = tokens.filter { it.blockchain == Blockchain.BSC } return listOf(TitleListItem(blockchainsTitle)) + blockchains.map { BlockchainListItem(it) } + - listOf(TitleListItem(tokensTitle)) + - tokens.map { TokenListItem(it) } + listOf(TitleListItem(ethereumTokensTitle)) + + ethereumTokens.map { TokenListItem(it) } + + listOf(TitleListItem(bscTokensTitle)) + + bscTokens.map { TokenListItem(it) } } } } 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 808201e795..afa3936fb4 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 @@ -50,19 +50,7 @@ data class WalletState( fun getWalletManager(token: Token?): WalletManager? { if (token == null) return null - val ethereumWalletManager = walletManagers - .find { it.wallet.blockchain == Blockchain.Ethereum || - it.wallet.blockchain == Blockchain.EthereumTestnet } - return if (ethereumWalletManager?.presetTokens?.contains(token) == true) { - ethereumWalletManager - } else { - val primaryWalletManager = walletManagers.find { it.wallet.blockchain == primaryBlockchain } - if (primaryWalletManager?.presetTokens?.contains(token) == true) { - primaryWalletManager - } else { - ethereumWalletManager - } - } + return walletManagers.find { it.wallet.blockchain == token.blockchain } } fun getWalletManager(currency: Currency?) : WalletManager? { @@ -101,7 +89,7 @@ data class WalletState( ?: return true if (walletData.currency is Currency.Blockchain && - walletManager.presetTokens.isNotEmpty() + walletManager.cardTokens.isNotEmpty() ) { return false } @@ -233,9 +221,9 @@ sealed interface Currency { val currencySymbol: CryptoCurrencyName data class Token( - val token: com.tangem.blockchain.common.Token, - override val blockchain: com.tangem.blockchain.common.Blockchain + val token: com.tangem.blockchain.common.Token ) : Currency { + override val blockchain = token.blockchain override val currencySymbol: CryptoCurrencyName = token.symbol } 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 853b1fb45b..fe2fb9430f 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 @@ -66,8 +66,10 @@ class MultiWalletMiddleware { when (val currency = action.walletData.currency) { is Currency.Blockchain -> { cardId?.let { - currenciesRepository.removeBlockchain(it, - currency.blockchain) + currenciesRepository.removeBlockchain( + it, + currency.blockchain + ) } } is Currency.Token -> { @@ -94,11 +96,16 @@ class MultiWalletMiddleware { if (coinAmount != null && !coinAmount.isZero()) { scope.launch(Dispatchers.Main) { if (walletState?.getWalletData(wallet.blockchain) == null) { - store.dispatch(WalletAction.MultiWallet.AddWalletManagers( - listOfNotNull(walletManager))) - store.dispatch(WalletAction.MultiWallet.AddBlockchain( - wallet.blockchain - )) + store.dispatch( + WalletAction.MultiWallet.AddWalletManagers( + listOfNotNull(walletManager) + ) + ) + store.dispatch( + WalletAction.MultiWallet.AddBlockchain( + wallet.blockchain + ) + ) store.dispatch(WalletAction.LoadWallet.Success(wallet)) } } @@ -136,7 +143,7 @@ class MultiWalletMiddleware { ) store.dispatch( WalletAction.MultiWallet.AddTokens( - walletManager.presetTokens.toList() + walletManager.cardTokens.toList() ) ) } @@ -160,12 +167,7 @@ class MultiWalletMiddleware { } store.dispatch( - WalletAction.LoadFiatRate( - currency = Currency.Token( - token = token, - blockchain = walletManager?.wallet?.blockchain ?: Blockchain.Ethereum - ) - ) + WalletAction.LoadFiatRate(currency = Currency.Token(token)) ) scope.launch { @@ -189,15 +191,14 @@ class MultiWalletMiddleware { val walletManager = walletState?.getWalletManager(token) ?: globalState.tapWalletManager.walletManagerFactory.makeWalletManagerForApp( card = card, - blockchain = if (card.isTestCard) Blockchain.EthereumTestnet else Blockchain.Ethereum + blockchain = token.blockchain )?.also { walletManager -> store.dispatch(WalletAction.MultiWallet.AddWalletManagers(walletManager)) store.dispatch(WalletAction.MultiWallet.AddBlockchain(walletManager.wallet.blockchain)) } - store.dispatch(WalletAction.LoadFiatRate(currency = Currency.Token( - token = token, - blockchain = walletManager?.wallet?.blockchain ?: Blockchain.Ethereum - ))) + store.dispatch( + WalletAction.LoadFiatRate(currency = Currency.Token(token)) + ) TokenWithManager(token, walletManager) } scope.launch { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt index 2f90f29e0f..52a557ead6 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt @@ -166,9 +166,6 @@ fun Token.toWallet(state: WalletState): WalletData? { walletAddresses = walletAddresses, mainButton = WalletMainButton.SendButton(false), topUpState = TopUpState(allowed = false), - currency = Currency.Token( - token = this, - blockchain = walletManager?.blockchain ?: Blockchain.Ethereum - ) + currency = Currency.Token(this) ) } \ No newline at end of file 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 ddb3276dea..b39e928706 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 @@ -45,35 +45,42 @@ private fun internalReduce(action: Action, state: AppState): WalletState { newState.twinCardsState?.isCreatingTwinCardsAllowed != true) newState = newState.copy( - state = ProgressState.Done, - wallets = listOf(WalletData( - currencyData = BalanceWidgetData(BalanceStatus.EmptyCard), - mainButton = WalletMainButton.CreateWalletButton(creatingWalletAllowed), - topUpState = TopUpState(false) - ))) + state = ProgressState.Done, + wallets = listOf( + WalletData( + currencyData = BalanceWidgetData(BalanceStatus.EmptyCard), + mainButton = WalletMainButton.CreateWalletButton(creatingWalletAllowed), + topUpState = TopUpState(false) + ) + ) + ) } is WalletAction.LoadData.Failure -> { when (action.error) { is TapError.NoInternetConnection -> { val wallets = newState.wallets - .map { - it.copy(currencyData = it.currencyData.copy( - status = BalanceStatus.Unreachable - )) - } + .map { + it.copy( + currencyData = it.currencyData.copy( + status = BalanceStatus.Unreachable + ) + ) + } newState = newState.copy( - state = ProgressState.Error, - error = ErrorType.NoInternetConnection, - wallets = wallets + state = ProgressState.Error, + error = ErrorType.NoInternetConnection, + wallets = wallets ) } is TapError.UnknownBlockchain -> { newState = newState.copy( - state = ProgressState.Done, - wallets = listOf(WalletData( - currencyData = BalanceWidgetData(BalanceStatus.UnknownBlockchain), - topUpState = TopUpState(false) - )) + state = ProgressState.Done, + wallets = listOf( + WalletData( + currencyData = BalanceWidgetData(BalanceStatus.UnknownBlockchain), + topUpState = TopUpState(false) + ) + ) ) } } @@ -81,73 +88,76 @@ private fun internalReduce(action: Action, state: AppState): WalletState { is WalletAction.LoadData -> { newState = newState.copy( - state = ProgressState.Loading, - error = null, + state = ProgressState.Loading, + error = null, ) } is WalletAction.LoadWallet -> { if (action.blockchain == null) { val wallets = newState.wallets.map { wallet -> wallet.copy( - currencyData = wallet.currencyData.copy( - status = BalanceStatus.Loading, - currency = wallet.currencyData.currency, - currencySymbol = wallet.currencyData.currencySymbol, - ), - mainButton = WalletMainButton.SendButton(false), - topUpState = TopUpState(allowed = action.allowTopUp - ?: wallet.topUpState.allowed) + currencyData = wallet.currencyData.copy( + status = BalanceStatus.Loading, + currency = wallet.currencyData.currency, + currencySymbol = wallet.currencyData.currencySymbol, + ), + mainButton = WalletMainButton.SendButton(false), + topUpState = TopUpState( + allowed = action.allowTopUp + ?: wallet.topUpState.allowed + ) ) } newState = newState.copy( - state = ProgressState.Loading, - wallets = wallets + state = ProgressState.Loading, + wallets = wallets ) } else { val walletManager = newState.getWalletManager(action.blockchain) ?: return newState val blockchain = walletManager.wallet.blockchain - val currencies = listOf(Currency.Blockchain(blockchain)) + walletManager.presetTokens.map { - Currency.Token(token = it, blockchain = blockchain) - } + val currencies = listOf(Currency.Blockchain(blockchain)) + + walletManager.cardTokens.map { Currency.Token(it) } val newWallets = newState.wallets.filter { currencies.contains(it.currency) } - .map { wallet -> - wallet.copy( - currencyData = wallet.currencyData.copy( - status = BalanceStatus.Loading, - currency = wallet.currencyData.currency, - currencySymbol = wallet.currencyData.currencySymbol, - ), - mainButton = WalletMainButton.SendButton(false), - topUpState = TopUpState( - allowed = action.allowTopUp ?: wallet.topUpState.allowed - ) + .map { wallet -> + wallet.copy( + currencyData = wallet.currencyData.copy( + status = BalanceStatus.Loading, + currency = wallet.currencyData.currency, + currencySymbol = wallet.currencyData.currencySymbol, + ), + mainButton = WalletMainButton.SendButton(false), + topUpState = TopUpState( + allowed = action.allowTopUp ?: wallet.topUpState.allowed ) - } + ) + } val wallets = newState.replaceSomeWallets(newWallets) newState = newState.copy(wallets = wallets) } } - is WalletAction.LoadWallet.Success -> newState = onWalletLoadedReducer.reduce(action.wallet, newState) + is WalletAction.LoadWallet.Success -> newState = + onWalletLoadedReducer.reduce(action.wallet, newState) is WalletAction.UpdateWallet.Success -> { newState = onWalletLoadedReducer.reduce(action.wallet, newState) } is WalletAction.LoadWallet.NoAccount -> { val walletData = newState.getWalletData(action.wallet.blockchain)?.copy( - currencyData = BalanceWidgetData( - BalanceStatus.NoAccount, action.wallet.blockchain.fullName, - currencySymbol = action.wallet.blockchain.currency, - amountToCreateAccount = action.amountToCreateAccount - ) + currencyData = BalanceWidgetData( + BalanceStatus.NoAccount, action.wallet.blockchain.fullName, + currencySymbol = action.wallet.blockchain.currency, + amountToCreateAccount = action.amountToCreateAccount + ) ) val wallets = newState.replaceWalletInWallets(walletData) - val progressState = if (wallets.any { it.currencyData.status == BalanceStatus.Loading }) { - ProgressState.Loading - } else { - ProgressState.Done - } + val progressState = + if (wallets.any { it.currencyData.status == BalanceStatus.Loading }) { + ProgressState.Loading + } else { + ProgressState.Done + } newState = newState.copy( - state = progressState, - wallets = wallets + state = progressState, + wallets = wallets ) } is WalletAction.LoadWallet.Failure -> { @@ -158,28 +168,31 @@ private fun internalReduce(action: Action, state: AppState): WalletState { } val walletData = newState.getWalletData(action.wallet.blockchain) val newWalletData = walletData?.copy( - currencyData = walletData.currencyData.copy( - status = BalanceStatus.Unreachable, - errorMessage = message - ), - topUpState = TopUpState(false) + currencyData = walletData.currencyData.copy( + status = BalanceStatus.Unreachable, + errorMessage = message + ), + topUpState = TopUpState(false) ) val tokenWallets = action.wallet.getTokens() - .mapNotNull { newState.getWalletData(it) } - .map { - it.copy(currencyData = it.currencyData.copy( - status = BalanceStatus.Unreachable, errorMessage = message - )) - } + .mapNotNull { newState.getWalletData(it) } + .map { + it.copy( + currencyData = it.currencyData.copy( + status = BalanceStatus.Unreachable, errorMessage = message + ) + ) + } val wallets = newState.replaceSomeWallets(listOfNotNull(newWalletData) + tokenWallets) - val progressState = if (wallets.any { it.currencyData.status == BalanceStatus.Loading }) { - ProgressState.Loading - } else { - ProgressState.Done - } + val progressState = + if (wallets.any { it.currencyData.status == BalanceStatus.Loading }) { + ProgressState.Loading + } else { + ProgressState.Done + } newState = newState.copy( - state = progressState, wallets = wallets + state = progressState, wallets = wallets ) } is WalletAction.SetArtworkId -> { @@ -216,11 +229,11 @@ private fun internalReduce(action: Action, state: AppState): WalletState { is WalletAction.ShowDialog.QrCode -> { val selectedWalletData = newState.getWalletData(newState.selectedWallet) newState = newState.copy( - walletDialog = WalletDialog.QrDialog( - selectedWalletData?.walletAddresses?.selectedAddress?.shareUrl?.toQrCode(), - selectedWalletData?.walletAddresses?.selectedAddress?.shareUrl, - selectedWalletData?.currencyData?.currency - ) + walletDialog = WalletDialog.QrDialog( + selectedWalletData?.walletAddresses?.selectedAddress?.shareUrl?.toQrCode(), + selectedWalletData?.walletAddresses?.selectedAddress?.shareUrl, + selectedWalletData?.currencyData?.currency + ) ) } is WalletAction.ShowDialog.ScanFails -> { @@ -234,7 +247,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState { } is WalletAction.Send.ChooseCurrency -> { newState = newState.copy( - walletDialog = WalletDialog.SelectAmountToSendDialog(action.amounts) + walletDialog = WalletDialog.SelectAmountToSendDialog(action.amounts) ) } is WalletAction.Send.Cancel -> newState = newState.copy(walletDialog = null) @@ -242,12 +255,18 @@ private fun internalReduce(action: Action, state: AppState): WalletState { is WalletAction.ChangeSelectedAddress -> { val selectedWalletData = newState.getWalletData(newState.selectedWallet) - val walletAddresses = newState.getWalletData(selectedWalletData?.currency)?.walletAddresses + val walletAddresses = + newState.getWalletData(selectedWalletData?.currency)?.walletAddresses ?: return newState val address = walletAddresses.list.firstOrNull { it.type == action.type } - ?: return newState + ?: return newState val wallets = newState.replaceWalletInWallets( - selectedWalletData?.copy(walletAddresses = WalletAddresses(address, walletAddresses.list)) + selectedWalletData?.copy( + walletAddresses = WalletAddresses( + address, + walletAddresses.list + ) + ) ) newState = newState.copy(wallets = wallets) } @@ -261,7 +280,12 @@ fun createAddressList(wallet: Wallet?, walletAddresses: WalletAddresses? = null) val listOfAddressData = mutableListOf() // put a defaultAddress at the first place wallet.addresses.forEach { - val addressData = AddressData(it.value, it.type, wallet.getShareUri(it.value), wallet.getExploreUrl(it.value)) + val addressData = AddressData( + it.value, + it.type, + wallet.getShareUri(it.value), + wallet.getExploreUrl(it.value) + ) if (it.type == wallet.blockchain.defaultAddressType()) { listOfAddressData.add(0, addressData) } else { @@ -272,16 +296,22 @@ fun createAddressList(wallet: Wallet?, walletAddresses: WalletAddresses? = null) // restore a selected wallet address var indexOfSelectedWallet = 0 walletAddresses?.let { - val index = listOfAddressData.indexOfFirst { it.address == walletAddresses.selectedAddress.address } + val index = + listOfAddressData.indexOfFirst { it.address == walletAddresses.selectedAddress.address } if (index != -1) indexOfSelectedWallet = index } return WalletAddresses(listOfAddressData[indexOfSelectedWallet], listOfAddressData) } -private fun handleCheckSignedHashesActions(action: WalletAction.Warnings, state: WalletState): WalletState { +private fun handleCheckSignedHashesActions( + action: WalletAction.Warnings, + state: WalletState +): WalletState { return when (action) { WalletAction.Warnings.CheckHashesCount.ConfirmHashesCount -> state.copy(hashesCountVerified = true) - WalletAction.Warnings.CheckHashesCount.NeedToCheckHashesCountOnline -> state.copy(hashesCountVerified = false) + WalletAction.Warnings.CheckHashesCount.NeedToCheckHashesCountOnline -> state.copy( + hashesCountVerified = false + ) is WalletAction.Warnings.Set -> state.copy(mainWarningsList = action.warningList) else -> state } @@ -289,8 +319,8 @@ private fun handleCheckSignedHashesActions(action: WalletAction.Warnings, state: private fun setNewFiatRate( - fiatRate: Pair, - appCurrency: FiatCurrencyName, state: WalletState + fiatRate: Pair, + appCurrency: FiatCurrencyName, state: WalletState ): WalletState { val rate = fiatRate.second ?: return state val rateFormatted = rate.toFormattedCurrencyString(2, appCurrency, RoundingMode.HALF_UP) @@ -327,33 +357,34 @@ private fun setMultiWalletFiatRate( } private fun setSingeWalletFiatRate( - rate: BigDecimal, rateFormatted: String, currency: Currency, - appCurrency: FiatCurrencyName, state: WalletState + rate: BigDecimal, rateFormatted: String, currency: Currency, + appCurrency: FiatCurrencyName, state: WalletState ): WalletState { val wallet = state.walletManagers[0].wallet val token = wallet.getFirstToken() if (currency == state.primaryWallet?.currency) { val fiatAmount = wallet.amounts[AmountType.Coin]?.value - ?.toFiatString(rate, appCurrency) + ?.toFiatString(rate, appCurrency) val walletData = state.primaryWallet.copy( - currencyData = state.primaryWallet.currencyData.copy(fiatAmountFormatted = fiatAmount), - fiatRate = rate, - fiatRateString = rateFormatted + currencyData = state.primaryWallet.currencyData.copy(fiatAmountFormatted = fiatAmount), + fiatRate = rate, + fiatRateString = rateFormatted ) return state.copy(wallets = listOf(walletData)) } else if (currency is Currency.Token && currency.token == token) { val tokenFiatAmount = wallet.getTokenAmount(token)?.value?.toFiatString(rate, appCurrency) val tokenData = state.primaryWallet?.currencyData?.token?.copy( - fiatAmount = tokenFiatAmount, - fiatRate = rate, - fiatRateString = rateFormatted + fiatAmount = tokenFiatAmount, + fiatRate = rate, + fiatRateString = rateFormatted ) val walletData = state.primaryWallet?.copy( - currencyData = state.primaryWallet.currencyData.copy( - token = tokenData - )) + currencyData = state.primaryWallet.currencyData.copy( + token = tokenData + ) + ) val wallets = walletData?.let { listOf(walletData) } ?: emptyList() return state.copy(wallets = wallets) } diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml index 1653689fc7..a4a2aecc25 100644 --- a/app/src/main/res/values/strings_untranslated.xml +++ b/app/src/main/res/values/strings_untranslated.xml @@ -93,7 +93,8 @@ this wallet. Manage tokens Blockchains - Ethereum Tokens + Ethereum tokens + Binance Smart Chain tokens Copy Share + Add tokens From 099f0b9ad04217d9e4b6bf0dc27f099e2380b029 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 6 Jul 2021 11:57:12 +0300 Subject: [PATCH 011/189] Updated on 2026-08-14 --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index e188911f47..8be6049bd6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,13 +6,13 @@ apply plugin: 'com.google.firebase.crashlytics' apply plugin: 'com.google.gms.google-services' android { - compileSdkVersion 29 + compileSdkVersion 30 defaultConfig { applicationId "com.tangem.wallet" versionCode project.hasProperty('versionCode') ? project.property('versionCode') as int : 1 versionName project.hasProperty('versionName') ? project.property('versionName') : "1.0.0.-SNAPSHOT" minSdkVersion 21 - targetSdkVersion 29 + targetSdkVersion 30 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } androidExtensions { From 36c7d05f27c2e790e872bc88e22a5f4d81378ba7 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 6 Jul 2021 12:01:16 +0300 Subject: [PATCH 012/189] Updated on 2026-08-14 --- app/build.gradle | 11 +++++------ build.gradle | 5 ++--- dependencies.gradle | 4 ++-- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 8be6049bd6..14e4a65177 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -57,7 +57,6 @@ android { exclude 'lib/x86_64/freebsd/libscrypt.so' exclude 'lib/x86_64/linux/libscrypt.so' } - buildToolsVersion '29.0.3' } repositories { @@ -67,12 +66,12 @@ repositories { dependencies { implementation fileTree(include: ['*.aar'], dir: 'libs') - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin" - implementation 'androidx.core:core-ktx:1.3.2' - implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'androidx.core:core-ktx:1.6.0' + implementation 'androidx.appcompat:appcompat:1.3.0' + implementation 'androidx.fragment:fragment-ktx:1.3.5' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' - implementation 'com.google.android.material:material:1.3.0' - implementation "androidx.core:core-ktx:1.3.2" + implementation 'com.google.android.material:material:1.4.0' + implementation "androidx.core:core-ktx:1.6.0" implementation 'com.google.android.play:core:1.10.0' implementation 'com.google.android.play:core-ktx:1.8.1' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' diff --git a/build.gradle b/build.gradle index f2dcf4b2fe..0ac373259b 100644 --- a/build.gradle +++ b/build.gradle @@ -10,9 +10,8 @@ buildscript { classpath "com.android.tools.build:gradle:${versions.build_gradle}" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlin" classpath "com.github.dcendents:android-maven-gradle-plugin:2.1" - classpath 'com.google.gms:google-services:4.3.5' - classpath 'com.google.firebase:firebase-crashlytics-gradle:2.4.1' -// classpath 'com.google.firebase:perf-plugin:1.3.1' + classpath 'com.google.gms:google-services:4.3.8' + classpath 'com.google.firebase:firebase-crashlytics-gradle:2.7.1' } } diff --git a/dependencies.gradle b/dependencies.gradle index 32b0ea5280..e4beaa5aaa 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -1,4 +1,4 @@ ext.versions = [ - kotlin : '1.5.10', - build_gradle: '4.2.1', + kotlin : '1.5.20', + build_gradle: '4.2.2', ] From 8495cdbeda2c0f658992939e4464dda878804300 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 13 Jul 2021 01:13:00 +0300 Subject: [PATCH 013/189] Updated on 2026-08-14 --- app/build.gradle | 2 +- .../com/tangem/tap/domain/extensions/Card.kt | 2 +- .../tap/domain/tokens/CurrenciesRepository.kt | 10 ++++++- .../tokens/ui/adapters/CurrenciesAdapter.kt | 28 ++++++++++++------- .../middlewares/MultiWalletMiddleware.kt | 2 +- .../main/res/values/strings_untranslated.xml | 1 + 6 files changed, 31 insertions(+), 14 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 0654603a77..55910447e1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -76,7 +76,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-33' + implementation 'com.tangem:blockchain:develop-34' implementation 'com.tangem:core:fixes_for_app_release-44' implementation 'com.tangem:sdk:fixes_for_app_release-44' diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt b/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt index 83a6f41967..0c8ed31dae 100644 --- a/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt +++ b/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt @@ -15,7 +15,7 @@ fun Card.getToken(): Token? { val decimals = cardData?.tokenDecimal ?: return null val blockchain = cardData?.blockchainName?.let { Blockchain.fromId(it) } ?: return null if (symbol.isBlank() || contractAddress.isBlank()) return null - return Token(symbol, symbol, contractAddress, decimals, blockchain) // TODO: change to constructor without tokenName + return Token(symbol, contractAddress, decimals, blockchain) } fun Card.getBlockchain(): Blockchain? { 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 9b07025880..054698b51b 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 @@ -111,10 +111,16 @@ class CurrenciesRepository(val context: Application) { if (isTestNet) ETHEREUM_TESTNET_TOKENS_FILE_NAME else ETHEREUM_TOKENS_FILE_NAME val bscTokensFileName = 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 ethereumTokensJson = context.assets.readJsonFileToString(ethereumTokensFileName) val bscTokensJson = context.assets.readJsonFileToString(bscTokensFileName) + val binanceTokensJson = context.assets.readJsonFileToString(binanceTokensFileName) + return tokensAdapter.fromJson(ethereumTokensJson)!!.map { it.toToken() } + - tokensAdapter.fromJson(bscTokensJson)!!.map { it.toToken() } + tokensAdapter.fromJson(bscTokensJson)!!.map { it.toToken() } + + tokensAdapter.fromJson(binanceTokensJson)!!.map { it.toToken() } } fun getBlockchains( @@ -133,6 +139,8 @@ class CurrenciesRepository(val context: Application) { private const val ETHEREUM_TESTNET_TOKENS_FILE_NAME = "ethereum_tokens_testnet" private const val BSC_TOKENS_FILE_NAME = "bsc_tokens" 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 FILE_NAME_PREFIX_TOKENS = "tokens" private const val FILE_NAME_PREFIX_BLOCKCHAINS = "blockchains" diff --git a/app/src/main/java/com/tangem/tap/features/tokens/ui/adapters/CurrenciesAdapter.kt b/app/src/main/java/com/tangem/tap/features/tokens/ui/adapters/CurrenciesAdapter.kt index 63ed1521b9..918dd2916a 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/ui/adapters/CurrenciesAdapter.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/ui/adapters/CurrenciesAdapter.kt @@ -10,7 +10,10 @@ import androidx.recyclerview.widget.RecyclerView import com.squareup.picasso.Picasso import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token -import com.tangem.tap.common.extensions.* +import com.tangem.tap.common.extensions.getString +import com.tangem.tap.common.extensions.hide +import com.tangem.tap.common.extensions.loadCurrenciesIcon +import com.tangem.tap.common.extensions.show import com.tangem.tap.domain.tokens.CardCurrencies import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.store @@ -83,17 +86,18 @@ class CurrenciesAdapter : ListAdapter when (element) { is CurrencyListItem.BlockchainListItem -> { element.blockchain.currency.toLowerCase(Locale.US) - .contains(queryNormalized) - || element.blockchain.fullName.toLowerCase(Locale.US) - .contains(queryNormalized) + .contains(queryNormalized) || + element.blockchain.fullName.toLowerCase(Locale.US) + .contains(queryNormalized) } is CurrencyListItem.TokenListItem -> { - element.token.name.toLowerCase(Locale.US).contains(queryNormalized) - || element.token.symbol.toLowerCase(Locale.US) - .contains(queryNormalized) + element.token.name.toLowerCase(Locale.US) + .contains(queryNormalized) || + element.token.symbol.toLowerCase(Locale.US) + .contains(queryNormalized) } - else -> false + is CurrencyListItem.TitleListItem -> true } }) } else { @@ -142,7 +146,7 @@ class CurrenciesAdapter : ListAdapter Picasso.get().loadCurrenciesIcon( imageView = view.iv_currency, textView = view.tv_token_letter, - token = token, blockchain = Blockchain.Ethereum + token = token, blockchain = token.blockchain ) view.btn_add_token.setOnClickListener { store.dispatch(WalletAction.MultiWallet.AddToken(token)) @@ -176,15 +180,19 @@ sealed class CurrencyListItem { 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 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)) + ethereumTokens.map { TokenListItem(it) } + listOf(TitleListItem(bscTokensTitle)) + - bscTokens.map { TokenListItem(it) } + bscTokens.map { TokenListItem(it) } + + listOf(TitleListItem(binanceTokensTitle)) + + binanceTokens.map { TokenListItem(it) } } } } 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 fe2fb9430f..b5c15c945c 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 @@ -160,7 +160,7 @@ class MultiWalletMiddleware { val walletManager = walletState?.getWalletManager(token) ?: globalState.tapWalletManager.walletManagerFactory.makeWalletManagerForApp( card = card, - blockchain = Blockchain.Ethereum + blockchain = token.blockchain )?.also { walletManager -> store.dispatch(WalletAction.MultiWallet.AddWalletManagers(walletManager)) store.dispatch(WalletAction.MultiWallet.AddBlockchain(walletManager.wallet.blockchain)) diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml index a4a2aecc25..062a7439ae 100644 --- a/app/src/main/res/values/strings_untranslated.xml +++ b/app/src/main/res/values/strings_untranslated.xml @@ -95,6 +95,7 @@ this wallet. Blockchains Ethereum tokens Binance Smart Chain tokens + Binance Chain tokens Copy Share + Add tokens From fbbd94051a052e83fc3205c261cb1ecde0d7cda1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 19 Jul 2021 10:47:28 +0300 Subject: [PATCH 014/189] Updated on 2026-08-14 --- fastlane/Fastfile | 2 +- fastlane/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index f987e8eb91..06543032c9 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -46,7 +46,7 @@ platform :android do 'versionName' => options[:versionName], }) gradle( - task: "assemble", + task: "bundle", build_type: "Release", properties: { 'versionCode' => options[:versionCode], diff --git a/fastlane/README.md b/fastlane/README.md index 7ef939cccf..5a974e90ae 100644 --- a/fastlane/README.md +++ b/fastlane/README.md @@ -12,7 +12,7 @@ Install _fastlane_ using ``` [sudo] gem install fastlane -NV ``` -or alternatively using `brew cask install fastlane` +or alternatively using `brew install fastlane` # Available Actions ## Android From 2abbc1a36585e625c603667f650615329691b70f Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 23 Jul 2021 13:02:20 +0300 Subject: [PATCH 015/189] Updated on 2026-08-14 --- app/src/main/AndroidManifest.xml | 26 ++++- .../tangem/tap/common/CustomTabsManager.kt | 21 ++++ .../com/tangem/tap/common/IntentHandler.kt | 26 +++++ .../com/tangem/tap/common/extensions/UI.kt | 5 + .../tap/common/redux/global/GlobalAction.kt | 5 + .../common/redux/global/GlobalMidlleware.kt | 18 ++++ .../tap/common/redux/global/GlobalReducer.kt | 3 + .../tap/common/redux/global/GlobalState.kt | 4 +- .../redux/navigation/NavigationAction.kt | 2 + .../redux/navigation/NavigationMiddleware.kt | 15 ++- .../redux/navigation/NavigationReducer.kt | 1 + .../com/tangem/tap/domain/TapWalletManager.kt | 12 ++- .../com/tangem/tap/domain/TapWorkarounds.kt | 2 +- .../tangem/tap/domain/topup/TopUpHelper.kt | 55 ---------- .../tap/domain/topup/TradeCryptoHelper.kt | 102 ++++++++++++++++++ .../tangem/tap/features/home/HomeFragment.kt | 3 + .../tap/features/send/BaseStoreFragment.kt | 12 ++- .../features/send/redux/SendScreenAction.kt | 8 ++ .../send/redux/middlewares/SendMiddleware.kt | 32 +++++- .../send/redux/reducers/SendScreenReducer.kt | 21 ++++ .../send/redux/states/AddressPayIdState.kt | 3 +- .../features/send/redux/states/SendState.kt | 14 ++- .../tap/features/send/ui/SendFragment.kt | 3 +- .../stateSubscribers/SendStateSubscriber.kt | 14 +++ .../tap/features/wallet/redux/WalletAction.kt | 31 ++++-- .../tap/features/wallet/redux/WalletState.kt | 38 ++++--- .../redux/middlewares/TopUpMiddleware.kt | 53 --------- .../middlewares/TradeCryptoMiddleware.kt | 94 ++++++++++++++++ .../redux/middlewares/WalletMiddleware.kt | 4 +- .../redux/reducers/MultiWalletReducer.kt | 25 +++-- .../wallet/redux/reducers/WalletReducer.kt | 31 ++++-- .../wallet/ui/WalletDetailsFragment.kt | 16 +-- .../ui/dialogs/ChooseTradeActionDialog.kt | 32 ++++++ .../wallet/ui/wallet/SingleWalletView.kt | 37 +++++-- .../tangem/tap/network/moonpay/MoonpayApi.kt | 16 +++ .../tap/network/moonpay/MoonpayService.kt | 24 +++++ .../drawable/ic_arrows_up_down_short_btn.xml | 24 +++++ .../main/res/layout/dialog_wallet_trade.xml | 47 ++++++++ app/src/main/res/layout/fragment_topup.xml | 2 +- app/src/main/res/layout/fragment_wallet.xml | 2 +- .../res/layout/fragment_wallet_details.xml | 53 ++++++--- .../res/layout/layout_send_address_payid.xml | 3 + .../main/res/layout/layout_send_amount.xml | 1 + .../layout/layout_wallet_short_buttons.xml | 4 +- app/src/main/res/values/colors.xml | 2 + .../main/res/values/strings_untranslated.xml | 7 +- 46 files changed, 747 insertions(+), 206 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/common/CustomTabsManager.kt delete mode 100644 app/src/main/java/com/tangem/tap/domain/topup/TopUpHelper.kt create mode 100644 app/src/main/java/com/tangem/tap/domain/topup/TradeCryptoHelper.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TopUpMiddleware.kt create mode 100644 app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt create mode 100644 app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ChooseTradeActionDialog.kt create mode 100644 app/src/main/java/com/tangem/tap/network/moonpay/MoonpayApi.kt create mode 100644 app/src/main/java/com/tangem/tap/network/moonpay/MoonpayService.kt create mode 100644 app/src/main/res/drawable/ic_arrows_up_down_short_btn.xml create mode 100644 app/src/main/res/layout/dialog_wallet_trade.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 97e4157f97..c2d4929889 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -81,9 +81,33 @@ + - + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/tangem/tap/common/CustomTabsManager.kt b/app/src/main/java/com/tangem/tap/common/CustomTabsManager.kt new file mode 100644 index 0000000000..a30af0f5d6 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/CustomTabsManager.kt @@ -0,0 +1,21 @@ +package com.tangem.tap.common + +import android.content.Context +import android.net.Uri +import androidx.browser.customtabs.CustomTabColorSchemeParams +import androidx.browser.customtabs.CustomTabsIntent +import com.tangem.tap.common.extensions.getColorCompat +import com.tangem.wallet.R + +class CustomTabsManager { + fun openUrl(url: String, context: Context) { + val customTabsIntent = CustomTabsIntent.Builder() + .setDefaultColorSchemeParams( + CustomTabColorSchemeParams.Builder() + .setNavigationBarColor(context.getColorCompat(R.color.toolbarColor)) + .build() + ) + .build() + customTabsIntent.launchUrl(context, Uri.parse(url)) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/IntentHandler.kt b/app/src/main/java/com/tangem/tap/common/IntentHandler.kt index f30a481c98..a804d20624 100644 --- a/app/src/main/java/com/tangem/tap/common/IntentHandler.kt +++ b/app/src/main/java/com/tangem/tap/common/IntentHandler.kt @@ -5,16 +5,20 @@ import android.nfc.NfcAdapter import android.nfc.Tag import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction +import com.tangem.tap.domain.topup.TradeCryptoHelper import com.tangem.tap.domain.walletconnect.WalletConnectManager import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction import com.tangem.tap.features.home.redux.HomeAction +import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.store +import timber.log.Timber class IntentHandler { fun handleIntent(intent: Intent?) { handleBackgroundScan(intent) handleWalletConnectLink(intent) + handleSellCurrencyCallback(intent) } private fun handleBackgroundScan(intent: Intent?) { @@ -38,4 +42,26 @@ class IntentHandler { } } + private fun handleSellCurrencyCallback(intent: Intent?) { + val transactionID = + intent?.data?.getQueryParameter(TradeCryptoHelper.TRANSACTION_ID_PARAM) ?: return + val currency = + intent.data?.getQueryParameter(TradeCryptoHelper.CURRENCY_CODE_PARAM) ?: return + val amount = + intent.data?.getQueryParameter(TradeCryptoHelper.CURRENCY_AMOUNT_PARAM) ?: return + val destinationAddress = + intent.data?.getQueryParameter(TradeCryptoHelper.DEPOSIT_WALLET_ADDRESS_PARAM) + ?: return + + Timber.d("MoonPay Sell: $amount $currency to $destinationAddress") + + store.dispatch(WalletAction.TradeCryptoAction.SendCrypto( + currencyId = currency, + amount = amount, + destinationAddress = destinationAddress, + transactionId = transactionID + )) + } + + } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/UI.kt b/app/src/main/java/com/tangem/tap/common/extensions/UI.kt index b43c49b307..d29b032ef3 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/UI.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/UI.kt @@ -30,6 +30,11 @@ fun Fragment.getColor(@ColorRes colorRes: Int): Int { return ContextCompat.getColor(requireContext(), colorRes) } +@ColorInt +fun Context.getColorCompat(@ColorRes colorRes: Int): Int { + return ContextCompat.getColor(this, colorRes) +} + fun View.getString(@StringRes id: Int): String { return context.getString(id) } 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 419fa5737d..bb32bfa7b4 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 @@ -9,6 +9,7 @@ import com.tangem.tap.domain.tasks.ScanNoteResponse import com.tangem.tap.features.details.redux.SecurityOption import com.tangem.tap.features.feedback.EmailData import com.tangem.tap.features.feedback.FeedbackManager +import com.tangem.tap.network.moonpay.MoonPayUserStatus import org.rekotlin.Action sealed class GlobalAction : Action { @@ -43,4 +44,8 @@ sealed class GlobalAction : Action { data class ShowDialog(val stateDialog: StateDialog) : GlobalAction() object HideDialog : GlobalAction() + + object GetMoonPayUserStatus : GlobalAction() { + data class Success(val moonPayUserStatus: MoonPayUserStatus) : GlobalAction() + } } \ No newline at end of file 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 2f79a5bbcc..e499b320c2 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 @@ -1,14 +1,19 @@ package com.tangem.tap.common.redux.global import com.tangem.TangemSdkError +import com.tangem.commands.common.network.Result import com.tangem.common.CompletionResult +import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.redux.AppState import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager import com.tangem.tap.features.home.redux.HomeAction import com.tangem.tap.features.send.redux.SendAction import com.tangem.tap.features.wallet.redux.WalletAction +import com.tangem.tap.network.moonpay.MoonpayService import com.tangem.tap.preferencesStorage +import com.tangem.tap.scope import com.tangem.tap.store +import kotlinx.coroutines.launch import org.rekotlin.Middleware val globalMiddleware: Middleware = { dispatch, appState -> @@ -60,6 +65,19 @@ val globalMiddleware: Middleware = { dispatch, appState -> store.state.globalState.feedbackManager?.infoHolder ?.setWalletsInfo(action.walletManagers) } + is GlobalAction.GetMoonPayUserStatus -> { + val apiKey = appState()?.globalState?.configManager?.config?.moonPayApiKey + if (apiKey != null) { + scope.launch { + val userStatusResponse = MoonpayService().getUserStatus(apiKey) + if (userStatusResponse is Result.Success) { + store.dispatchOnMain( + GlobalAction.GetMoonPayUserStatus.Success(userStatusResponse.data) + ) + } + } + } + } } nextDispatch(action) } 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 b7208034f7..7dbbe6f40e 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 @@ -73,6 +73,9 @@ fun globalReducer(action: Action, state: AppState): GlobalState { is GlobalAction.HideDialog -> { globalState.copy(dialog = null) } + is GlobalAction.GetMoonPayUserStatus.Success -> { + globalState.copy(moonPayUserStatus = action.moonPayUserStatus) + } else -> globalState } } \ No newline at end of file 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 c7e173310b..77b02d5e8c 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 @@ -9,6 +9,7 @@ import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager import com.tangem.tap.domain.tasks.ScanNoteResponse import com.tangem.tap.features.feedback.FeedbackManager import com.tangem.tap.network.coinmarketcap.CoinMarketCapService +import com.tangem.tap.network.moonpay.MoonPayUserStatus import org.rekotlin.StateType data class GlobalState( @@ -22,7 +23,8 @@ data class GlobalState( val feedbackManager: FeedbackManager? = null, val appCurrency: FiatCurrencyName = DEFAULT_FIAT_CURRENCY, val scanCardFailsCounter: Int = 0, - val dialog: StateDialog? = null + val dialog: StateDialog? = null, + val moonPayUserStatus: MoonPayUserStatus? = null ) : StateType typealias CryptoCurrencyName = String diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt index 5cbdb915f6..6da57dfec8 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt @@ -10,6 +10,8 @@ sealed class NavigationAction : Action { data class PopBackTo(val screen: AppScreen? = null) : NavigationAction() + data class OpenUrl(val url: String) : NavigationAction() + data class ActivityCreated(val activity: WeakReference) : NavigationAction() object ActivityDestroyed : NavigationAction() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt index 8c4d5e9fea..43c8ed406e 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt @@ -1,25 +1,30 @@ package com.tangem.tap.common.redux.navigation +import com.tangem.tap.common.CustomTabsManager import com.tangem.tap.common.extensions.openFragment import com.tangem.tap.common.extensions.popBackTo import com.tangem.tap.common.redux.AppState -import com.tangem.tap.store import org.rekotlin.Middleware val navigationMiddleware: Middleware = { dispatch, state -> { next -> { action -> if (action is NavigationAction) { - val navState = store.state.navigationState + val navState = state()?.navigationState when (action) { is NavigationAction.NavigateTo -> { - navState.activity?.get()?.openFragment(action.screen, action.addToBackstack) + navState?.activity?.get()?.openFragment(action.screen, action.addToBackstack) } is NavigationAction.PopBackTo -> { if (action.screen == AppScreen.Home) { - navState.activity?.get()?.popBackTo(null, true) + navState?.activity?.get()?.popBackTo(null, true) } else { - navState.activity?.get()?.popBackTo(action.screen) + navState?.activity?.get()?.popBackTo(action.screen) + } + } + is NavigationAction.OpenUrl -> { + navState?.activity?.get()?.let { + CustomTabsManager().openUrl(action.url, it) } } } diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationReducer.kt index a4ce784435..80cd5e3260 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationReducer.kt @@ -27,5 +27,6 @@ private fun internalReduce(action: Action, state: AppState): NavigationState { } is NavigationAction.ActivityCreated -> navState.copy(activity = navigationAction.activity) is NavigationAction.ActivityDestroyed -> navState.copy(activity = null) + else -> navState } } \ No newline at end of file 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 9ae9105a46..992ddbc606 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -174,7 +174,11 @@ class TapWalletManager { } } store.dispatch(WalletAction.SetArtworkId(data.verifyResponse?.artworkInfo?.id)) - store.dispatch(WalletAction.LoadWallet(config.isTopUpEnabled)) + val moonPayUserStatus = store.state.globalState.moonPayUserStatus + store.dispatch(WalletAction.LoadWallet( + allowToBuy = config.isTopUpEnabled && moonPayUserStatus?.isBuyAllowed == true, + allowToSell = config.isTopUpEnabled && moonPayUserStatus?.isSellAllowed == true, + )) store.dispatch(WalletAction.LoadArtwork(data.card, artworkId)) store.dispatch(WalletAction.LoadFiatRate()) } @@ -245,7 +249,11 @@ class TapWalletManager { return@withContext } val config = store.state.globalState.configManager?.config ?: return@withContext - store.dispatch(WalletAction.LoadWallet(config.isTopUpEnabled)) + val moonpayUserStatus = store.state.globalState.moonPayUserStatus + store.dispatch(WalletAction.LoadWallet( + allowToBuy = config.isTopUpEnabled && moonpayUserStatus?.isBuyAllowed == true, + allowToSell = config.isTopUpEnabled && moonpayUserStatus?.isSellAllowed == true, + )) store.dispatch(WalletAction.LoadFiatRate()) } } diff --git a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt index 0c1a379a3c..6a6723cd83 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt @@ -20,7 +20,7 @@ object TapWorkarounds { val cardData = this.cardData ?: return false val productMask = cardData.productMask val excludedBatch = excludedBatches.contains(cardData.batchId) - val excludedIssuerName = excludedIssuers.contains(cardData.issuerName?.toUpperCase(Locale.US)) + val excludedIssuerName = excludedIssuers.contains(cardData.issuerName?.uppercase(Locale.ROOT)) val excludedProductMask = (productMask != null && // product mask is on cards v2.30 and later !productMask.contains(Product.Note) && !productMask.contains(Product.TwinCard)) return excludedBatch || excludedIssuerName || excludedProductMask diff --git a/app/src/main/java/com/tangem/tap/domain/topup/TopUpHelper.kt b/app/src/main/java/com/tangem/tap/domain/topup/TopUpHelper.kt deleted file mode 100644 index 2ab6b843c8..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/topup/TopUpHelper.kt +++ /dev/null @@ -1,55 +0,0 @@ -package com.tangem.tap.domain.topup - -import android.net.Uri -import android.util.Base64 -import com.tangem.blockchain.common.Blockchain -import com.tangem.tap.common.redux.global.CryptoCurrencyName -import javax.crypto.Mac -import javax.crypto.spec.SecretKeySpec - -class TopUpHelper { - - companion object { -// const val REDIRECT_URL = "https://success.tangem.com" - - private const val BASE_URL = "https://buy.moonpay.io" - private const val API_KEY_PATH = "?apiKey=" - private const val CURRENCY_PATH = "¤cyCode=" - private const val WALLET_ADDRESS_PATH = "&walletAddress=" - - // private const val REDIRECT_URL_PATH = "&redirectUrl=" - private const val SIGNATURE_PATH = "&signature=" - - fun getUrl( - blockchain: Blockchain?, - cryptoCurrencyName: CryptoCurrencyName, - walletAddress: String, - apiKey: String, - secretKey: String, - ): String { - if (blockchain?.isTestnet() == true) { - return blockchain.getTestnetTopUpUrl() ?: "" - } - val originalQuery = API_KEY_PATH + apiKey.urlEncode() + - CURRENCY_PATH + cryptoCurrencyName.urlEncode() + - WALLET_ADDRESS_PATH + walletAddress.urlEncode() -// REDIRECT_URL_PATH + REDIRECT_URL.urlEncode() - val signature = createSignature(originalQuery, secretKey) - - return BASE_URL + originalQuery + SIGNATURE_PATH + signature.urlEncode() - } - - private fun String.urlEncode(): String { - return Uri.encode(this) - } - - - private fun createSignature(data: String, key: String): String { - val sha256Hmac = Mac.getInstance("HmacSHA256") - val secretKey = SecretKeySpec(key.toByteArray(), "HmacSHA256") - sha256Hmac.init(secretKey) - val sha256encoded = sha256Hmac.doFinal(data.toByteArray()) - return Base64.encodeToString(sha256encoded, Base64.NO_WRAP) - } - } -} diff --git a/app/src/main/java/com/tangem/tap/domain/topup/TradeCryptoHelper.kt b/app/src/main/java/com/tangem/tap/domain/topup/TradeCryptoHelper.kt new file mode 100644 index 0000000000..f16a2591b2 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/topup/TradeCryptoHelper.kt @@ -0,0 +1,102 @@ +package com.tangem.tap.domain.topup + +import android.net.Uri +import android.util.Base64 +import com.tangem.blockchain.common.Blockchain +import com.tangem.tap.common.redux.global.CryptoCurrencyName +import javax.crypto.Mac +import javax.crypto.spec.SecretKeySpec + +class TradeCryptoHelper { + + companion object { + + const val TRANSACTION_ID_PARAM = "transactionId" + const val CURRENCY_CODE_PARAM = "baseCurrencyCode" + const val CURRENCY_AMOUNT_PARAM = "baseCurrencyAmount" + const val DEPOSIT_WALLET_ADDRESS_PARAM = "depositWalletAddress" + + private const val REDIRECT_URL_BUY = "tangem://success.tangem.com" + private const val REDIRECT_URL_SELL_REQUEST = "tangem://sell-request.tangem.com" + + private const val BASE_URL_BUY = + "https://buy.moonpay.io" +// "https://buy-staging.moonpay.io" // TESTNET + private const val BASE_URL_SELL = + "https://sell.moonpay.com" +// "https://sell-staging.moonpay.com" // TESTNET + + private const val API_KEY_PATH = "?apiKey=" + private const val CURRENCY_PATH = "¤cyCode=" + private const val WALLET_ADDRESS_PATH = "&walletAddress=" + + private const val REFUND_WALLET_ADDRESS_PATH = "&refundWalletAddress=" + private const val BASE_CURRENCY_PATH = "&baseCurrencyCode=" + + private const val REDIRECT_URL_PATH = "&redirectURL=" + private const val SIGNATURE_PATH = "&signature=" + + private const val TRANSACTION_RECEIPT_PATH = "transaction_receipt?transactionId=" + + val AVAILABLE_TO_BUY: Set = setOf( + "ZRX", "AAVE", "ALGO", "AXS", "BAT", "BNB", "BUSD", "BTC", "BCH", "BTT", "ADA", "CELO", "CUSD", "LINK", "CHZ", "COMP", "ATOM", "DAI", "DASH", "MANA", "DGB", "DOGE", "EGLD", + "ENJ", "EOS", "ETC", "ETH", "KETH", "RINKETH", "FIL", "HBAR", "MIOTA", "KAVA", "KLAY", "LBC", "LTC", "LUNA", "MKR", "OM", "MATIC", "NANO", "NEAR", "XEM", "NEO", "NIM", "OKB", + "OMG", "ONG", "ONT", "DOT", "QTUM", "RVN", "RFUEL", "KEY", "SRM", "SOL", "XLM", "STMX", "SNX", "KRT", "UST", "USDT", "XTZ", "RUNE", "SAND", "TOMO", "AVA", "TRX", "TUSD", "UNI", + "USDC", "UTK", "VET", "WAXP", "WBTC", "XRP", "ZEC", "ZIL" + ) + + val AVAILABLE_TO_SELL: Set = setOf("BTC", "ETH", "BCH") + + fun getUrl( + action: Action, + blockchain: Blockchain?, + cryptoCurrencyName: CryptoCurrencyName, + walletAddress: String, + apiKey: String, + secretKey: String, + ): String { + val originalQuery: String + val baseUrl: String + when (action) { + Action.Buy -> { + if (blockchain?.isTestnet() == true) { + return blockchain.getTestnetTopUpUrl() ?: "" + } + baseUrl = BASE_URL_BUY + originalQuery = API_KEY_PATH + apiKey.urlEncode() + + CURRENCY_PATH + cryptoCurrencyName.urlEncode() + + WALLET_ADDRESS_PATH + walletAddress.urlEncode() + + REDIRECT_URL_PATH + REDIRECT_URL_BUY.urlEncode() + } + Action.Sell -> { + baseUrl = BASE_URL_SELL + originalQuery = API_KEY_PATH + apiKey.urlEncode() + + BASE_CURRENCY_PATH + cryptoCurrencyName.urlEncode() + + REFUND_WALLET_ADDRESS_PATH + walletAddress.urlEncode() + + REDIRECT_URL_PATH + REDIRECT_URL_SELL_REQUEST.urlEncode() + } + } + val signature = createSignature(originalQuery, secretKey) + return baseUrl + originalQuery + SIGNATURE_PATH + signature.urlEncode() + + } + + fun getSellCryptoReceiptUrl(transactionId: String): String { + return BASE_URL_SELL + TRANSACTION_RECEIPT_PATH + transactionId + } + + private fun String.urlEncode(): String { + return Uri.encode(this) + } + + private fun createSignature(data: String, key: String): String { + val sha256Hmac = Mac.getInstance("HmacSHA256") + val secretKey = SecretKeySpec(key.toByteArray(), "HmacSHA256") + sha256Hmac.init(secretKey) + val sha256encoded = sha256Hmac.doFinal(data.toByteArray()) + return Base64.encodeToString(sha256encoded, Base64.NO_WRAP) + } + } + + enum class Action { Buy, Sell } +} diff --git a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt index c93543613e..29cd04dc73 100644 --- a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt @@ -5,6 +5,7 @@ import android.os.Bundle import android.view.View import androidx.fragment.app.Fragment import androidx.transition.TransitionInflater +import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.global.StateDialog import com.tangem.tap.features.home.redux.HomeAction import com.tangem.tap.features.home.redux.HomeDialog @@ -66,6 +67,8 @@ class HomeFragment : Fragment(R.layout.fragment_home), StoreSubscriber) : SendAction() } + + data class SendSpecificTransaction( + val sendAmount: String, + val destinationAddress: String, + val transactionId: String + ) : SendAction() + } \ No newline at end of file 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 9ca7c9ce1c..d24bfa3949 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,6 +9,7 @@ import com.tangem.blockchain.extensions.SimpleResult import com.tangem.commands.common.card.Card import com.tangem.tap.common.analytics.AnalyticsEvent import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler +import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.stripZeroPlainString import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction @@ -20,6 +21,8 @@ import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.extensions.minimalAmount import com.tangem.tap.features.send.redux.* import com.tangem.tap.features.send.redux.FeeAction.RequestFee +import com.tangem.tap.features.send.redux.states.ExternalTransactionData +import com.tangem.tap.features.send.redux.states.MainCurrencyType import com.tangem.tap.features.send.redux.states.SendButtonState import com.tangem.tap.features.send.redux.states.TransactionExtrasState import com.tangem.tap.features.wallet.redux.WalletAction @@ -49,6 +52,18 @@ val sendMiddleware: Middleware = { dispatch, appState -> verifyAndSendTransaction(action, appState(), dispatch) is PrepareSendScreen -> setIfSendingToPayIdEnabled(appState(), dispatch) is SendAction.Warnings.Update -> updateWarnings(dispatch) + is SendActionUi.CheckIfTransactionDataWasProvided -> { + val transactionData = appState()?.sendState?.externalTransactionData + if (transactionData != null) { + store.dispatchOnMain(AddressPayIdVerifyAction.AddressVerification.SetWalletAddress( + transactionData.destinationAddress, false + )) + store.dispatchOnMain(AmountActionUi.SetMainCurrency(MainCurrencyType.CRYPTO)) + store.dispatchOnMain(AmountActionUi.HandleUserInput(transactionData.amount)) + store.dispatchOnMain(AmountAction.SetAmount(transactionData.amount.toBigDecimal(), + false)) + } + } } nextDispatch(action) } @@ -76,8 +91,11 @@ private fun verifyAndSendTransaction( dispatch(AmountAction.SetAmount(typedAmount.value!!.minus(reduceAmount), false)) dispatch(AmountActionUi.CheckAmountToSend) }, sendAllCallback = { - sendTransaction(action, walletManager, amountToSend, feeAmount, destinationAddress, - sendState.transactionExtrasState, card, dispatch) + sendTransaction( + action, walletManager, amountToSend, feeAmount, destinationAddress, + sendState.transactionExtrasState, card, sendState.externalTransactionData, + dispatch + ) }, reduceAmount)) } transactionErrors.isNotEmpty() -> { @@ -85,7 +103,7 @@ private fun verifyAndSendTransaction( } else -> { sendTransaction(action, walletManager, amountToSend, feeAmount, destinationAddress, - sendState.transactionExtrasState, card, dispatch) + sendState.transactionExtrasState, card, sendState.externalTransactionData, dispatch) } } } @@ -98,6 +116,7 @@ private fun sendTransaction( destinationAddress: String, transactionExtras: TransactionExtrasState, card: Card, + externalTransactionData: ExternalTransactionData?, dispatch: (Action) -> Unit, ) { dispatch(SendAction.ChangeSendButtonState(SendButtonState.PROGRESS)) @@ -134,7 +153,12 @@ private fun sendTransaction( blockchain = walletManager.wallet.blockchain ) dispatch(SendAction.SendSuccess) - dispatch(NavigationAction.PopBackTo()) + + if (externalTransactionData != null) { + dispatch(WalletAction.TradeCryptoAction.FinishSelling(externalTransactionData.transactionId)) + } else { + dispatch(NavigationAction.PopBackTo()) + } scope.launch(Dispatchers.IO) { withContext(Dispatchers.Main) { dispatch(WalletAction.UpdateWallet(walletManager.wallet.blockchain)) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt index 8cad24d39c..58b36afe00 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt @@ -3,6 +3,7 @@ package com.tangem.tap.features.send.redux.reducers import com.tangem.blockchain.common.AmountType import com.tangem.tap.common.CurrencyConverter import com.tangem.tap.features.send.redux.* +import com.tangem.tap.features.send.redux.states.ExternalTransactionData import com.tangem.tap.features.send.redux.states.IdStateHolder import com.tangem.tap.features.send.redux.states.SendState import org.rekotlin.Action @@ -45,11 +46,31 @@ private class SendReducer : SendInternalReducer { is SendAction.Dialog.SendTransactionFails -> sendState.copy(dialog = action) is SendAction.Dialog.Hide -> sendState.copy(dialog = null) is SendAction.Warnings.Set -> sendState.copy(sendWarningsList = action.warningList) + is SendAction.SendSpecificTransaction -> + handleSendSpecificTransactionAction(action, sendState) else -> return sendState } return updateLastState(result, result) } + + private fun handleSendSpecificTransactionAction( + action: SendAction.SendSpecificTransaction, state: SendState + ): SendState { + return state.copy( + externalTransactionData = + ExternalTransactionData(action.sendAmount, action.destinationAddress, action.transactionId), + feeState = state.feeState.copy( + includeFeeSwitcherIsEnabled = false + ), + amountState = state.amountState.copy( + inputIsEnabled = false + ), + addressPayIdState = state.addressPayIdState.copy( + inputIsEnabled = false + ), + ) + } } private class EmptyReducer : SendInternalReducer { diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt index d17531a84a..4f2e4e1a6d 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt @@ -12,7 +12,8 @@ data class AddressPayIdState( val error: AddressPayIdVerifyAction.Error? = null, val truncateHandler: ((String) -> String)? = null, val sendingToPayIdEnabled: Boolean = false, - val pasteIsEnabled: Boolean = false + val pasteIsEnabled: Boolean = false, + val inputIsEnabled: Boolean = true ) : SendScreenState { override val stateId: StateId = StateId.ADDRESS_PAY_ID diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt index 41d2fd79c5..eed6fa793c 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt @@ -40,7 +40,8 @@ data class SendState( val receiptState: ReceiptState = ReceiptState(), val sendWarningsList: List = listOf(), val sendButtonState: SendButtonState = SendButtonState.DISABLED, - val dialog: StateDialog? = null + val dialog: StateDialog? = null, + val externalTransactionData: ExternalTransactionData? = null ) : SendScreenState { override val stateId: StateId = StateId.SEND_SCREEN @@ -131,7 +132,8 @@ data class AmountState( val cursorAtTheSamePosition: Boolean = true, val maxLengthOfAmount: Int = 2, val decimalSeparator: String = ".", - val error: TapError? = null + val error: TapError? = null, + val inputIsEnabled: Boolean = true, ) : SendScreenState { override val stateId: StateId = StateId.AMOUNT @@ -166,4 +168,12 @@ data class MainCurrency( val type: MainCurrencyType, val currencySymbol: String, val isEnabled: Boolean = true +) + +data class ExternalTransactionData( + val amount: String, + val destinationAddress: String, + val transactionId: String, + val canAmountBeModified: Boolean = false, + val canDestinationAddressBeModified: Boolean = false ) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt index 2b2b682294..9736cc16e0 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt @@ -71,6 +71,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { setupAmountLayout() setupFeeLayout() setupWarningMessages() + store.dispatch(SendActionUi.CheckIfTransactionDataWasProvided) } private fun initSendButtonStates() { @@ -250,7 +251,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { val sp = requireContext().getSharedPreferences("SendScreen", Context.MODE_PRIVATE) val mainCurrency = sp.getString("mainCurrency", TapCurrency.DEFAULT_FIAT_CURRENCY) val foundType = MainCurrencyType.values() - .firstOrNull { it.name.toLowerCase() == mainCurrency!!.toLowerCase() } + .firstOrNull { it.name.equals(mainCurrency!!, ignoreCase = true) } ?: MainCurrencyType.CRYPTO return foundType } diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt index 933b1580d7..05c7af7d18 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt @@ -156,6 +156,12 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber val til = fg.tilAddressOrPayId val parsedError = parseError(til.context, state.error) + til.isEnabled = state.inputIsEnabled + fg.imvPaste.show(state.inputIsEnabled) + fg.imvQrCode.show(state.inputIsEnabled) + fg.flPaste.show(state.inputIsEnabled) + fg.flQrCode.show(state.inputIsEnabled) + val hintResId = if (state.sendingToPayIdEnabled) { R.string.send_destination_hint_address_payid } else { @@ -198,6 +204,14 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber state.mainCurrency.currencySymbol, state.viewBalanceValue) fg.tvBalance.update(balanceText) + + fg.tilAmountToSend.isEnabled = state.inputIsEnabled + + val imageRes = if (state.inputIsEnabled) R.drawable.ic_arrows_up_down else 0 + fg.tvAmountCurrency.setCompoundDrawablesWithIntrinsicBounds(0, 0, imageRes, 0) + val textColor = if (state.inputIsEnabled) R.color.blue else R.color.textGray + fg.tvAmountCurrency.setTextColor(fg.getColor(textColor)) + } private fun handleFeeState(fg: BaseStoreFragment, state: FeeState) { 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 43617cb446..cff630a1a8 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 @@ -25,7 +25,11 @@ sealed class WalletAction : Action { } - data class LoadWallet(val allowTopUp: Boolean? = null, val blockchain: Blockchain? = null) : WalletAction() { + data class LoadWallet( + val allowToSell: Boolean? = null, val allowToBuy: Boolean? = null, + val blockchain: Blockchain? = null, + ) : + WalletAction() { data class Success(val wallet: Wallet) : WalletAction() data class NoAccount(val wallet: Wallet, val amountToCreateAccount: String) : WalletAction() data class Failure(val wallet: Wallet, val errorMessage: String? = null) : WalletAction() @@ -39,13 +43,16 @@ sealed class WalletAction : Action { data class AddWalletManagers(val walletManagers: List) : MultiWallet() { constructor(walletManager: WalletManager) : this(listOf(walletManager)) } + data class AddBlockchain(val blockchain: Blockchain) : MultiWallet() data class AddBlockchains(val blockchains: List) : MultiWallet() data class AddTokens(val tokens: List) : MultiWallet() data class AddToken(val token: Token) : MultiWallet() data class SaveCurrencies(val cardCurrencies: CardCurrencies) : MultiWallet() object FindTokensInUse : MultiWallet() - data class FindBlockchainsInUse(val card: Card, val factory: WalletManagerFactory) : MultiWallet() + data class FindBlockchainsInUse(val card: Card, val factory: WalletManagerFactory) : + MultiWallet() + data class TokenLoaded(val amount: Amount, val token: Token) : MultiWallet() data class SelectWallet(val walletData: WalletData?) : MultiWallet() data class RemoveWallet(val walletData: WalletData) : MultiWallet() @@ -69,6 +76,7 @@ sealed class WalletAction : Action { object SetNeverToShow : Warnings() object RemindLater : Warnings() } + class CheckRemainingSignatures(val remainingSignatures: Int?) : Warnings() } @@ -79,7 +87,7 @@ sealed class WalletAction : Action { } data class LoadFiatRate( - val wallet: Wallet? = null, val currency: Currency? = null + val wallet: Wallet? = null, val currency: Currency? = null, ) : WalletAction() { data class Success(val fiatRate: Pair) : WalletAction() object Failure : WalletAction() @@ -114,6 +122,7 @@ sealed class WalletAction : Action { object QrCode : WalletAction() object ScanFails : WalletAction() object SignedHashesMultiWalletDialog : WalletAction() + object ChooseTradeActionDialog : WalletAction() } object HideDialog : WalletAction() @@ -123,8 +132,16 @@ sealed class WalletAction : Action { object CreateWallet : WalletAction() object EmptyWallet : WalletAction() - sealed class TopUpAction : WalletAction() { - data class TopUp(val context: Context, val toolbarColor: Int) : TopUpAction() + sealed class TradeCryptoAction : WalletAction() { + object Buy : TradeCryptoAction() + object Sell : TradeCryptoAction() + data class FinishSelling(val transactionId: String) : TradeCryptoAction() + data class SendCrypto( + val currencyId: String, + val amount: String, + val destinationAddress: String, + val transactionId: String + ) : TradeCryptoAction() } data class ChangeSelectedAddress(val type: AddressType) : WalletAction() @@ -133,8 +150,8 @@ sealed class WalletAction : Action { object ShowOnboarding : TwinsAction() object SetOnboardingShown : TwinsAction() data class SetTwinCard( - val secondCardId: String, val number: TwinCardNumber, - val isCreatingTwinCardsAllowed: Boolean, + val secondCardId: String, val number: TwinCardNumber, + val isCreatingTwinCardsAllowed: Boolean, ) : TwinsAction() } } \ 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 afa3936fb4..d08f8caecf 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 @@ -9,6 +9,7 @@ import com.tangem.tap.common.redux.global.CryptoCurrencyName import com.tangem.tap.common.redux.global.StateDialog import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.extensions.toSendableAmounts +import com.tangem.tap.domain.topup.TradeCryptoHelper import com.tangem.tap.domain.twins.TwinCardNumber import com.tangem.tap.features.wallet.models.PendingTransaction import com.tangem.tap.features.wallet.models.toPendingTransactions @@ -34,6 +35,7 @@ data class WalletState( val primaryBlockchain: Blockchain? = null, val primaryToken: Token? = null, val isTestnet: Boolean = false, + val tradeCryptoAllowed: TradeCryptoAvailability = TradeCryptoAvailability() ) : StateType { val primaryWallet = if (wallets.isNotEmpty()) wallets[0] else null @@ -141,6 +143,7 @@ sealed class WalletDialog: StateDialog { data class SelectAmountToSendDialog(val amounts: List?) : WalletDialog() object ScanFailsDialog : WalletDialog() object SignedHashesMultiWalletDialog : WalletDialog() + object ChooseTradeActionDialog : WalletDialog() } enum class ProgressState { Loading, Done, Error } @@ -180,10 +183,15 @@ data class Artwork( } } -data class TopUpState( - val allowed: Boolean = true, - val url: String? = null, - val redirectUrl: String? = null +data class TradeCryptoState( + val sellingAllowed: Boolean = false, + val buyingAllowed: Boolean = false, +) + +data class TradeCryptoAvailability( + val sellingAllowed: Boolean = false, + val buyingAllowed: Boolean = false, + val availableToSell: Set = TradeCryptoHelper.AVAILABLE_TO_SELL, ) data class TwinCardsState( @@ -194,17 +202,17 @@ data class TwinCardsState( ) data class WalletData( - val pendingTransactions: List = emptyList(), - val hashesCountVerified: Boolean? = null, - val walletAddresses: WalletAddresses? = null, - val currencyData: BalanceWidgetData = BalanceWidgetData(), - val updatingWallet: Boolean = false, - val topUpState: TopUpState = TopUpState(), - val allowToSend: Boolean = true, - val fiatRateString: String? = null, - val fiatRate: BigDecimal? = null, - val mainButton: WalletMainButton = WalletMainButton.SendButton(false), - val currency: Currency? = null + val pendingTransactions: List = emptyList(), + val hashesCountVerified: Boolean? = null, + val walletAddresses: WalletAddresses? = null, + val currencyData: BalanceWidgetData = BalanceWidgetData(), + val updatingWallet: Boolean = false, + val tradeCryptoState: TradeCryptoState = TradeCryptoState(), + val allowToSend: Boolean = true, + val fiatRateString: String? = null, + val fiatRate: BigDecimal? = null, + val mainButton: WalletMainButton = WalletMainButton.SendButton(false), + val currency: Currency? = null ) { fun shouldShowMultipleAddress(): Boolean { val listOfAddresses = walletAddresses?.list ?: return false diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TopUpMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TopUpMiddleware.kt deleted file mode 100644 index 98b63904ba..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TopUpMiddleware.kt +++ /dev/null @@ -1,53 +0,0 @@ -package com.tangem.tap.features.wallet.redux.middlewares - -import android.net.Uri -import androidx.browser.customtabs.CustomTabsIntent -import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager -import com.tangem.tap.domain.topup.TopUpHelper -import com.tangem.tap.domain.topup.TopUpManager -import com.tangem.tap.features.wallet.redux.Currency -import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.scope -import com.tangem.tap.store -import kotlinx.coroutines.launch - - -class TopUpMiddleware { - fun handle(action: WalletAction.TopUpAction) { - when (action) { - is WalletAction.TopUpAction.TopUp -> { - val selectedWalletData = store.state.walletState.getSelectedWalletData() - val config = store.state.globalState.configManager?.config ?: return - val addresses = selectedWalletData?.walletAddresses ?: return - if (addresses.list.isEmpty()) return - - val defaultAddress = addresses.list[0].address - val currency = selectedWalletData.currency - - if (currency is Currency.Token && currency.blockchain.isTestnet()) { - val walletManager = store.state.walletState.getWalletManager(currency.token) - if (walletManager !is EthereumWalletManager) return - - scope.launch { - TopUpManager().topUpTestErc20Tokens( - walletManager = walletManager, token = currency.token - ) - } - } else { - val url = TopUpHelper.getUrl( - currency?.blockchain, - selectedWalletData.currencyData.currencySymbol!!, - defaultAddress, - config.moonPayApiKey, - config.moonPayApiSecretKey - ) - - val customTabsIntent = CustomTabsIntent.Builder() - .setToolbarColor(action.toolbarColor) - .build() - customTabsIntent.launchUrl(action.context, Uri.parse(url)) - } - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt new file mode 100644 index 0000000000..2b2087dd25 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt @@ -0,0 +1,94 @@ +package com.tangem.tap.features.wallet.redux.middlewares + +import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager +import com.tangem.blockchain.common.AmountType +import com.tangem.tap.common.extensions.dispatchOnMain +import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.common.redux.navigation.NavigationAction +import com.tangem.tap.domain.topup.TopUpManager +import com.tangem.tap.domain.topup.TradeCryptoHelper +import com.tangem.tap.features.send.redux.PrepareSendScreen +import com.tangem.tap.features.send.redux.SendAction +import com.tangem.tap.features.wallet.redux.Currency +import com.tangem.tap.features.wallet.redux.WalletAction +import com.tangem.tap.scope +import com.tangem.tap.store +import kotlinx.coroutines.launch +import timber.log.Timber + + +class TradeCryptoMiddleware { + fun handle(action: WalletAction.TradeCryptoAction) { + when (action) { + is WalletAction.TradeCryptoAction.Buy -> startExchange(action) + is WalletAction.TradeCryptoAction.Sell -> startExchange(action) + is WalletAction.TradeCryptoAction.SendCrypto -> preconfigureAndOpenSendScreen(action) + is WalletAction.TradeCryptoAction.FinishSelling -> openReceiptUrl(action.transactionId) + } + } + + private fun startExchange(action: WalletAction.TradeCryptoAction) { + val selectedWalletData = store.state.walletState.getSelectedWalletData() + val config = store.state.globalState.configManager?.config ?: return + val addresses = selectedWalletData?.walletAddresses ?: return + if (addresses.list.isEmpty()) return + + val defaultAddress = addresses.list[0].address + val currency = selectedWalletData.currency + val currencySymbol = selectedWalletData.currency?.currencySymbol ?: return + + val exchangeAction = if (action is WalletAction.TradeCryptoAction.Buy) { + TradeCryptoHelper.Action.Buy + } else { + TradeCryptoHelper.Action.Sell + } + + if (exchangeAction == TradeCryptoHelper.Action.Buy && + currency is Currency.Token && currency.blockchain.isTestnet() + ) { + val walletManager = store.state.walletState.getWalletManager(currency.token) + if (walletManager !is EthereumWalletManager) return + + scope.launch { + TopUpManager().topUpTestErc20Tokens( + walletManager = walletManager, token = currency.token + ) + } + return + } + + val url = TradeCryptoHelper.getUrl( + exchangeAction, + currency?.blockchain, + currencySymbol, + defaultAddress, + config.moonPayApiKey, + config.moonPayApiSecretKey + ) + Timber.d("Moonpay $exchangeAction URL: $url") + store.dispatchOnMain(NavigationAction.OpenUrl(url)) + } + + private fun preconfigureAndOpenSendScreen(action: WalletAction.TradeCryptoAction.SendCrypto) { + val selectedWalletData = store.state.walletState.getSelectedWalletData() ?: return + val walletManager = + store.state.walletState.getWalletManager(selectedWalletData.currency) + store.dispatchOnMain(PrepareSendScreen( + coinAmount = walletManager?.wallet?.amounts?.get(AmountType.Coin), + coinRate = selectedWalletData.fiatRate, + walletManager = walletManager + )) + store.dispatchOnMain(SendAction.SendSpecificTransaction( + sendAmount = action.amount, + destinationAddress = action.destinationAddress, + transactionId = action.transactionId + )) + store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Send)) + } + + private fun openReceiptUrl(transactionId: String) { + store.dispatchOnMain(NavigationAction.PopBackTo()) + val url = TradeCryptoHelper.getSellCryptoReceiptUrl(transactionId) + store.dispatchOnMain(NavigationAction.OpenUrl(url)) + } +} \ No newline at end of file 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 a03fbe9c16..8dc207048b 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 @@ -28,7 +28,7 @@ import org.rekotlin.Action import org.rekotlin.Middleware class WalletMiddleware { - private val topUpMiddleware = TopUpMiddleware() + private val tradeCryptoMiddleware = TradeCryptoMiddleware() private val twinsMiddleware = TwinsMiddleware() private val warningsMiddleware = WarningsMiddleware() private val multiWalletMiddleware = MultiWalletMiddleware() @@ -39,7 +39,7 @@ class WalletMiddleware { val globalState = state()?.globalState val walletState = state()?.walletState when (action) { - is WalletAction.TopUpAction -> topUpMiddleware.handle(action) + is WalletAction.TradeCryptoAction -> tradeCryptoMiddleware.handle(action) is WalletAction.TwinsAction -> twinsMiddleware.handle(action) is WalletAction.Warnings -> warningsMiddleware.handle(action, globalState) is WalletAction.MultiWallet -> diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt index 52a557ead6..030c2e2ab6 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt @@ -1,7 +1,6 @@ package com.tangem.tap.features.wallet.redux.reducers import com.tangem.blockchain.common.AmountType -import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token import com.tangem.common.extensions.isZero import com.tangem.tap.common.extensions.toFiatString @@ -38,8 +37,12 @@ class MultiWalletReducer { ), walletAddresses = createAddressList(wallet), mainButton = WalletMainButton.SendButton(false), - topUpState = TopUpState(allowed = false), - currency = Currency.Blockchain(blockchain) + currency = Currency.Blockchain(blockchain), + tradeCryptoState = TradeCryptoState( + sellingAllowed = state.tradeCryptoAllowed.sellingAllowed && + state.tradeCryptoAllowed.availableToSell.contains(blockchain.currency), + buyingAllowed = state.tradeCryptoAllowed.buyingAllowed + ) ) } @@ -63,8 +66,12 @@ class MultiWalletReducer { ), walletAddresses = createAddressList(wallet), mainButton = WalletMainButton.SendButton(false), - topUpState = TopUpState(allowed = false), - currency = Currency.Blockchain(action.blockchain) + currency = Currency.Blockchain(action.blockchain), + tradeCryptoState = TradeCryptoState( + sellingAllowed = state.tradeCryptoAllowed.sellingAllowed && + state.tradeCryptoAllowed.availableToSell.contains(action.blockchain.currency), + buyingAllowed = state.tradeCryptoAllowed.buyingAllowed + ) ) val newState = state.copy(wallets = state.replaceWalletInWallets(walletData)) if (wallet != null && wallet.amounts[AmountType.Coin]?.value != null) { @@ -165,7 +172,11 @@ fun Token.toWallet(state: WalletState): WalletData? { ), walletAddresses = walletAddresses, mainButton = WalletMainButton.SendButton(false), - topUpState = TopUpState(allowed = false), - currency = Currency.Token(this) + currency = Currency.Token(this), + tradeCryptoState = TradeCryptoState( + sellingAllowed = state.tradeCryptoAllowed.sellingAllowed && + state.tradeCryptoAllowed.availableToSell.contains(this.symbol), + buyingAllowed = state.tradeCryptoAllowed.buyingAllowed + ) ) } \ No newline at end of file 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 b39e928706..3298b8a9cc 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 @@ -50,7 +50,6 @@ private fun internalReduce(action: Action, state: AppState): WalletState { WalletData( currencyData = BalanceWidgetData(BalanceStatus.EmptyCard), mainButton = WalletMainButton.CreateWalletButton(creatingWalletAllowed), - topUpState = TopUpState(false) ) ) ) @@ -78,7 +77,6 @@ private fun internalReduce(action: Action, state: AppState): WalletState { wallets = listOf( WalletData( currencyData = BalanceWidgetData(BalanceStatus.UnknownBlockchain), - topUpState = TopUpState(false) ) ) ) @@ -95,6 +93,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState { is WalletAction.LoadWallet -> { if (action.blockchain == null) { val wallets = newState.wallets.map { wallet -> + wallet.copy( currencyData = wallet.currencyData.copy( status = BalanceStatus.Loading, @@ -102,15 +101,22 @@ private fun internalReduce(action: Action, state: AppState): WalletState { currencySymbol = wallet.currencyData.currencySymbol, ), mainButton = WalletMainButton.SendButton(false), - topUpState = TopUpState( - allowed = action.allowTopUp - ?: wallet.topUpState.allowed + tradeCryptoState = TradeCryptoState( + sellingAllowed = action.allowToSell ?: wallet.tradeCryptoState.sellingAllowed && + state.walletState.tradeCryptoAllowed.availableToSell.contains(wallet.currencyData.currencySymbol), + buyingAllowed = action.allowToBuy + ?: wallet.tradeCryptoState.buyingAllowed ) ) } + val tradeCryptoAllowed = TradeCryptoAvailability( + sellingAllowed = action.allowToSell ?: false, + buyingAllowed = action.allowToBuy ?: false + ) newState = newState.copy( state = ProgressState.Loading, - wallets = wallets + wallets = wallets, + tradeCryptoAllowed = tradeCryptoAllowed ) } else { val walletManager = newState.getWalletManager(action.blockchain) ?: return newState @@ -126,8 +132,11 @@ private fun internalReduce(action: Action, state: AppState): WalletState { currencySymbol = wallet.currencyData.currencySymbol, ), mainButton = WalletMainButton.SendButton(false), - topUpState = TopUpState( - allowed = action.allowTopUp ?: wallet.topUpState.allowed + tradeCryptoState = TradeCryptoState( + sellingAllowed = action.allowToSell ?: wallet.tradeCryptoState.sellingAllowed && + state.walletState.tradeCryptoAllowed.availableToSell.contains(wallet.currencyData.currencySymbol), + buyingAllowed = action.allowToBuy + ?: wallet.tradeCryptoState.buyingAllowed ) ) } @@ -172,7 +181,6 @@ private fun internalReduce(action: Action, state: AppState): WalletState { status = BalanceStatus.Unreachable, errorMessage = message ), - topUpState = TopUpState(false) ) val tokenWallets = action.wallet.getTokens() .mapNotNull { newState.getWalletData(it) } @@ -242,6 +250,9 @@ private fun internalReduce(action: Action, state: AppState): WalletState { is WalletAction.ShowDialog.SignedHashesMultiWalletDialog -> { newState = newState.copy(walletDialog = WalletDialog.SignedHashesMultiWalletDialog) } + is WalletAction.ShowDialog.ChooseTradeActionDialog -> { + newState = newState.copy(walletDialog = WalletDialog.ChooseTradeActionDialog) + } is WalletAction.HideDialog -> { newState = newState.copy(walletDialog = null) } @@ -251,7 +262,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState { ) } is WalletAction.Send.Cancel -> newState = newState.copy(walletDialog = null) - is WalletAction.TopUpAction -> return newState + is WalletAction.TradeCryptoAction -> return newState is WalletAction.ChangeSelectedAddress -> { val selectedWalletData = newState.getWalletData(newState.selectedWallet) 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 63a7a78812..c2ff298244 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 @@ -86,11 +86,9 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS btn_share.setOnClickListener { store.dispatch(WalletAction.ShowDialog.QrCode) } - btn_top_up.setOnClickListener { - store.dispatch( - WalletAction.TopUpAction.TopUp(requireContext(), R.color.backgroundLightGray) - ) - } + btn_top_up.setOnClickListener { store.dispatch(WalletAction.TradeCryptoAction.Buy) } + + btn_sell.setOnClickListener { store.dispatch(WalletAction.TradeCryptoAction.Sell) } } override fun newState(state: WalletState) { @@ -131,8 +129,9 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS srl_wallet_details.setOnRefreshListener { if (selectedWallet.currencyData.status != BalanceStatus.Loading) { store.dispatch(WalletAction.LoadWallet( - selectedWallet.topUpState.allowed, - selectedWallet.currency?.blockchain + allowToBuy = selectedWallet.tradeCryptoState.buyingAllowed, + allowToSell = selectedWallet.tradeCryptoState.sellingAllowed, + blockchain = selectedWallet.currency?.blockchain )) } } @@ -140,6 +139,9 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS if (selectedWallet.currencyData.status != BalanceStatus.Loading) { srl_wallet_details.isRefreshing = false } + + btn_top_up.isEnabled = selectedWallet.tradeCryptoState.buyingAllowed + btn_sell.show(selectedWallet.tradeCryptoState.sellingAllowed) } private fun handleCurrencyIcon(wallet: WalletData) { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ChooseTradeActionDialog.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ChooseTradeActionDialog.kt new file mode 100644 index 0000000000..95ff1f65ea --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ChooseTradeActionDialog.kt @@ -0,0 +1,32 @@ +package com.tangem.tap.features.wallet.ui.dialogs + +import android.content.Context +import com.google.android.material.bottomsheet.BottomSheetDialog +import com.tangem.tap.features.wallet.redux.WalletAction +import com.tangem.tap.store +import com.tangem.wallet.R +import kotlinx.android.synthetic.main.dialog_wallet_trade.* + +class ChooseTradeActionDialog(context: Context) : BottomSheetDialog(context) { + + init { + this.setContentView(R.layout.dialog_wallet_trade) + } + + override fun show() { + super.show() + + this.setOnDismissListener { + store.dispatch(WalletAction.HideDialog) + } + + dialog_btn_buy.setOnClickListener { + store.dispatch(WalletAction.TradeCryptoAction.Buy) + dismiss() + } + dialog_btn_sell.setOnClickListener { + store.dispatch(WalletAction.TradeCryptoAction.Sell) + dismiss() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt index ce0623ac86..14883f5233 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt @@ -16,10 +16,7 @@ import com.tangem.tap.features.wallet.ui.BalanceWidget import com.tangem.tap.features.wallet.ui.MultipleAddressUiHelper import com.tangem.tap.features.wallet.ui.WalletFragment import com.tangem.tap.features.wallet.ui.adapters.PendingTransactionsAdapter -import com.tangem.tap.features.wallet.ui.dialogs.AmountToSendDialog -import com.tangem.tap.features.wallet.ui.dialogs.QrDialog -import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog -import com.tangem.tap.features.wallet.ui.dialogs.SignedHashesWarningDialog +import com.tangem.tap.features.wallet.ui.dialogs.* import com.tangem.tap.store import com.tangem.wallet.R import kotlinx.android.synthetic.main.card_balance.* @@ -124,12 +121,17 @@ class SingleWalletView : WalletView { setupButtonsType(state, fragment) - val btnConfirm = if (state.topUpState.allowed) { + val btnConfirm = if (state.tradeCryptoState.sellingAllowed || + state.tradeCryptoState.buyingAllowed + ) { btn_confirm_short } else { btn_confirm_long } - val btnScan = if (state.topUpState.allowed) { + + val btnScan = if (state.tradeCryptoState.sellingAllowed || + state.tradeCryptoState.buyingAllowed + ) { btn_scan_short } else { btn_scan_long @@ -149,14 +151,26 @@ class SingleWalletView : WalletView { btn_show_qr.setOnClickListener { store.dispatch(WalletAction.ShowDialog.QrCode) } btn_top_up.setOnClickListener { - store.dispatch( - WalletAction.TopUpAction.TopUp(fragment.requireContext(), R.color.backgroundLightGray) - ) + tradeCryptoAction(state.tradeCryptoState) } } + private fun tradeCryptoAction(tradeCryptoState: TradeCryptoState) { + val allowedToBuy = tradeCryptoState.buyingAllowed + val allowedToSell = tradeCryptoState.sellingAllowed + val action = when { + allowedToBuy && !allowedToSell -> WalletAction.TradeCryptoAction.Buy + !allowedToBuy && allowedToSell -> WalletAction.TradeCryptoAction.Sell + allowedToBuy && allowedToSell -> WalletAction.ShowDialog.ChooseTradeActionDialog + else -> null + } + if (action != null) store.dispatch(action) + } + private fun setupButtonsType(state: WalletData, fragment: WalletFragment) = with(fragment) { - if (state.topUpState.allowed) { + if (state.tradeCryptoState.sellingAllowed || + state.tradeCryptoState.buyingAllowed + ) { l_buttons_long.hide() l_buttons_short.show() } else { @@ -244,6 +258,9 @@ class SingleWalletView : WalletView { dialog = SignedHashesWarningDialog.create(context).apply { show() } } } + is WalletDialog.ChooseTradeActionDialog -> { + if (dialog == null) dialog = ChooseTradeActionDialog(context).apply { show() } + } null -> { dialog?.dismiss() dialog = null diff --git a/app/src/main/java/com/tangem/tap/network/moonpay/MoonpayApi.kt b/app/src/main/java/com/tangem/tap/network/moonpay/MoonpayApi.kt new file mode 100644 index 0000000000..f3e062abf3 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/network/moonpay/MoonpayApi.kt @@ -0,0 +1,16 @@ +package com.tangem.tap.network.moonpay + +import retrofit2.http.GET +import retrofit2.http.Query + +interface MoonpayApi { + @GET( MOOONPAY_IP_ADDRESS_REQUEST_URL) + suspend fun getUserStatus( + @Query("apiKey") moonpayApiKey: String + ): MoonPayUserStatus + + companion object { + const val MOOONPAY_BASE_URL = "https://api.moonpay.com/v4/" + const val MOOONPAY_IP_ADDRESS_REQUEST_URL = "ip_address/" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/network/moonpay/MoonpayService.kt b/app/src/main/java/com/tangem/tap/network/moonpay/MoonpayService.kt new file mode 100644 index 0000000000..c20d22863e --- /dev/null +++ b/app/src/main/java/com/tangem/tap/network/moonpay/MoonpayService.kt @@ -0,0 +1,24 @@ +package com.tangem.tap.network.moonpay + +import com.squareup.moshi.JsonClass +import com.tangem.commands.common.network.Result +import com.tangem.commands.common.network.performRequest +import com.tangem.tap.network.createRetrofitInstance + +class MoonpayService { + + private val moonpayApi: MoonpayApi by lazy { + createRetrofitInstance(MoonpayApi.MOOONPAY_BASE_URL) + .create(MoonpayApi::class.java) + } + + suspend fun getUserStatus(moonpayApiKey: String): Result { + return performRequest { moonpayApi.getUserStatus(moonpayApiKey) } + } +} + +@JsonClass(generateAdapter = true) +data class MoonPayUserStatus( + val isBuyAllowed: Boolean, + val isSellAllowed: Boolean +) \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_arrows_up_down_short_btn.xml b/app/src/main/res/drawable/ic_arrows_up_down_short_btn.xml new file mode 100644 index 0000000000..51b285c947 --- /dev/null +++ b/app/src/main/res/drawable/ic_arrows_up_down_short_btn.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_wallet_trade.xml b/app/src/main/res/layout/dialog_wallet_trade.xml new file mode 100644 index 0000000000..062d0286d1 --- /dev/null +++ b/app/src/main/res/layout/dialog_wallet_trade.xml @@ -0,0 +1,47 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_topup.xml b/app/src/main/res/layout/fragment_topup.xml index 8069c307f0..01182c0c4c 100644 --- a/app/src/main/res/layout/fragment_topup.xml +++ b/app/src/main/res/layout/fragment_topup.xml @@ -11,7 +11,7 @@ android:layout_height="?attr/actionBarSize" app:layout_constraintTop_toTopOf="parent" app:navigationIcon="@drawable/ic_close" - app:title="@string/wallet_button_topup" /> + app:title="@string/wallet_button_buy" /> diff --git a/app/src/main/res/layout/fragment_wallet_details.xml b/app/src/main/res/layout/fragment_wallet_details.xml index 8270cd53ac..7b61f56d8d 100644 --- a/app/src/main/res/layout/fragment_wallet_details.xml +++ b/app/src/main/res/layout/fragment_wallet_details.xml @@ -83,12 +83,12 @@ android:id="@+id/rv_pending_transaction" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginTop="16dp" android:layout_marginBottom="8dp" android:nestedScrollingEnabled="false" android:overScrollMode="never" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - android:layout_marginTop="16dp" app:layout_constraintTop_toBottomOf="@id/barrier_currency_title" /> + android:textColor="@android:color/white" + android:textSize="14sp" /> - + + + + + app:layout_constraintHorizontal_chainStyle="packed" + app:layout_constraintStart_toEndOf="@id/btn_top_up" + app:layout_constraintEnd_toStartOf="@id/btn_confirm" + app:layout_constraintTop_toBottomOf="@id/barrier" + app:layout_constraintVertical_bias="1" /> diff --git a/app/src/main/res/layout/layout_send_address_payid.xml b/app/src/main/res/layout/layout_send_address_payid.xml index 00a5617a9d..2b0f046335 100644 --- a/app/src/main/res/layout/layout_send_address_payid.xml +++ b/app/src/main/res/layout/layout_send_address_payid.xml @@ -32,6 +32,7 @@ android:id="@+id/etAddressOrPayId" android:layout_width="match_parent" android:layout_height="wrap_content" + android:background="@color/backgroundLightGray" android:ellipsize="middle" android:inputType="textNoSuggestions" android:paddingStart="0dp" @@ -150,6 +151,7 @@ android:id="@+id/etMemo" android:layout_width="match_parent" android:layout_height="wrap_content" + android:background="@color/backgroundLightGray" android:ellipsize="middle" android:paddingStart="0dp" android:paddingEnd="0dp" @@ -180,6 +182,7 @@ android:id="@+id/etDestinationTag" android:layout_width="match_parent" android:layout_height="wrap_content" + android:background="@color/backgroundLightGray" android:ellipsize="end" android:inputType="number" android:paddingStart="0dp" diff --git a/app/src/main/res/layout/layout_send_amount.xml b/app/src/main/res/layout/layout_send_amount.xml index e2b5441fa2..b2e8700482 100644 --- a/app/src/main/res/layout/layout_send_amount.xml +++ b/app/src/main/res/layout/layout_send_amount.xml @@ -51,6 +51,7 @@ android:id="@+id/etAmountToSend" android:layout_width="match_parent" android:layout_height="82dp" + android:background="@color/backgroundLightGray" android:fontFamily="sans-serif-light" android:inputType="numberDecimal" android:paddingStart="0dp" diff --git a/app/src/main/res/layout/layout_wallet_short_buttons.xml b/app/src/main/res/layout/layout_wallet_short_buttons.xml index 072306c7b7..be494ee95b 100644 --- a/app/src/main/res/layout/layout_wallet_short_buttons.xml +++ b/app/src/main/res/layout/layout_wallet_short_buttons.xml @@ -28,10 +28,10 @@ android:layout_marginStart="8dp" android:layout_marginTop="30dp" android:layout_marginBottom="33dp" - android:drawableTop="@drawable/ic_arrow_up_short_btn" + android:drawableTop="@drawable/ic_arrows_up_down_short_btn" android:gravity="bottom|center_horizontal" android:paddingTop="1dp" - android:text="@string/wallet_button_topup" + android:text="@string/wallet_button_trade" app:drawableTint="@android:color/white" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@+id/btn_confirm_short" diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index de5579c26b..509d2b72be 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -5,6 +5,8 @@ #0022D4 #1ACE80 + @color/backgroundLightGray + @color/accent #661ACE80 @color/darkGray6 diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml index a4a2aecc25..08784d1bf0 100644 --- a/app/src/main/res/values/strings_untranslated.xml +++ b/app/src/main/res/values/strings_untranslated.xml @@ -1,5 +1,7 @@ - Top Up + Buy + Sell + Trade Default Compatibility Legacy @@ -160,4 +162,7 @@ this wallet. Failed to establish WalletConnect session: timeout error. Please, try again later. This is a Testnet card. Don\'t accept it as a payment. This card must only be used for testing and development purposes. + + + Do you want to buy or sell crypto? From 3d623afb27a4756ae51106d0185ef3aa41d413fa Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 23 Jul 2021 16:06:27 +0300 Subject: [PATCH 016/189] Updated on 2026-08-14 --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 55910447e1..5bad46aa84 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,8 +2,8 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt' -apply plugin: 'com.google.firebase.crashlytics' apply plugin: 'com.google.gms.google-services' +apply plugin: 'com.google.firebase.crashlytics' android { compileSdkVersion 30 @@ -37,7 +37,7 @@ android { buildConfigField 'String', 'CONFIG_ENVIRONMENT', '\"dev\"' } debug_beta { - initWith debug + initWith release versionNameSuffix "-beta" applicationIdSuffix ".debug" buildConfigField 'String', 'CONFIG_ENVIRONMENT', '\"prod\"' From 3dca7fd4e1a231f03911e81c86ca946bc8d0e703 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 26 Jul 2021 10:48:25 +0300 Subject: [PATCH 017/189] Updated on 2026-08-14 --- .../wallet/ui/wallet/MultiWalletView.kt | 1 - .../wallet/ui/wallet/SingleWalletView.kt | 2 - app/src/main/res/layout/fragment_wallet.xml | 35 +++++++--------- .../main/res/layout/item_currency_wallet.xml | 41 ++++++++++++------- .../main/res/layout/item_popular_token.xml | 19 +++++++-- app/src/main/res/values/styles.xml | 3 +- 6 files changed, 58 insertions(+), 43 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt index 5deff50687..d40eaa4496 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt @@ -42,7 +42,6 @@ class MultiWalletView : WalletView { private fun showMultiWalletView(fragment: WalletFragment) = with(fragment) { tv_twin_card_number.hide() - iv_twin_card.hide() rv_pending_transaction.hide() l_card_balance.hide() l_address.hide() diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt index 14883f5233..b80b989b70 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt @@ -96,7 +96,6 @@ class SingleWalletView : WalletView { ) = with(fragment) { twinCardsState?.cardNumber?.let { cardNumber -> tv_twin_card_number.show() - iv_twin_card.show() val number = when (cardNumber) { TwinCardNumber.First -> "1" TwinCardNumber.Second -> "2" @@ -106,7 +105,6 @@ class SingleWalletView : WalletView { } if (twinCardsState?.cardNumber == null) { tv_twin_card_number.hide() - iv_twin_card.hide() } if (twinCardsState?.showTwinOnboarding == true) { store.dispatch(NavigationAction.NavigateTo(AppScreen.TwinsOnboarding)) diff --git a/app/src/main/res/layout/fragment_wallet.xml b/app/src/main/res/layout/fragment_wallet.xml index c126a81921..3733c23089 100644 --- a/app/src/main/res/layout/fragment_wallet.xml +++ b/app/src/main/res/layout/fragment_wallet.xml @@ -61,39 +61,33 @@ android:src="@drawable/card_placeholder" app:layout_constraintTop_toTopOf="parent" /> - - - + app:constraint_referenced_ids="iv_card,tv_twin_card_number" /> + android:layout_height="wrap_content" + android:minHeight="76dp"> + + @@ -31,6 +32,13 @@ app:layout_constraintTop_toTopOf="@id/iv_currency" tools:text="J" /> + + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index ae260a7854..3642ef243c 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -36,7 +36,8 @@ @dimen/btn_corner_radius @font/saira_semi_condensed_regular 93dp - 52dp + wrap_content + 52dp 3dp From 05113dc124daa9de09c0e3f77443e771f78a20d9 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 3 Aug 2021 09:56:11 +0300 Subject: [PATCH 018/189] Updated on 2026-08-14 --- app/build.gradle | 2 + app/src/main/assets/features_dev.json | 2 +- .../main/java/com/tangem/tap/MainActivity.kt | 11 +- .../tap/common/analytics/AnalyticsHandler.kt | 7 +- .../analytics/FirebaseAnalyticsHandler.kt | 20 ++- .../tap/common/analytics/TangemSdkError.kt | 26 ++-- .../tap/common/redux/global/GlobalAction.kt | 3 + .../common/redux/global/GlobalMidlleware.kt | 4 +- .../tap/common/redux/global/GlobalReducer.kt | 27 +--- .../tap/common/redux/global/GlobalState.kt | 3 +- .../com/tangem/tap/domain/PayIdManager.kt | 2 +- .../com/tangem/tap/domain/TangemSdkManager.kt | 119 ++++++++-------- .../com/tangem/tap/domain/TangemSigner.kt | 40 ++++-- .../java/com/tangem/tap/domain/TapErrors.kt | 2 +- .../com/tangem/tap/domain/TapWalletManager.kt | 34 ++--- .../com/tangem/tap/domain/TapWorkarounds.kt | 67 ++++++--- .../configurable/config/ConfigManager.kt | 2 +- .../warningMessage/WarningMessagesManager.kt | 2 +- .../com/tangem/tap/domain/extensions/Card.kt | 40 +----- .../domain/extensions/TangemSdkConstants.kt | 8 -- .../domain/extensions/WalletManagerFactory.kt | 10 +- .../domain/tasks/CreateWalletAndRescanTask.kt | 33 +++-- .../tap/domain/tasks/CreateWalletsTask.kt | 44 +++--- .../tangem/tap/domain/tasks/ScanNoteTask.kt | 133 ++++++++++-------- .../tangem/tap/domain/tasks/SignHashTask.kt | 36 +++++ .../tangem/tap/domain/tasks/SignHashesTask.kt | 35 +++++ .../tangem/tap/domain/termsOfUse/CardTou.kt | 6 +- .../tap/domain/tokens/CurrenciesRepository.kt | 4 +- .../tangem/tap/domain/topup/TopUpManager.kt | 6 +- .../domain/twins/CreateFirstTwinWalletTask.kt | 28 ++-- .../twins/CreateSecondTwinWalletTask.kt | 26 ++-- .../tap/domain/twins/FinalizeTwinTask.kt | 13 +- .../tap/domain/twins/TwinCardsManager.kt | 8 +- .../tangem/tap/domain/twins/TwinsHelper.kt | 18 +-- .../twins/WriteProtectedIssuerDataTask.kt | 74 ++++++---- .../walletconnect/WalletConnectSdkHelper.kt | 18 +-- .../features/details/redux/DetailsAction.kt | 5 +- .../details/redux/DetailsMiddleware.kt | 25 +++- .../features/details/redux/DetailsReducer.kt | 58 ++++---- .../features/details/redux/DetailsState.kt | 2 +- .../walletconnect/WalletConnectMiddleware.kt | 2 +- .../features/details/ui/DetailsFragment.kt | 2 +- .../tap/features/feedback/FeedbackManager.kt | 10 +- .../middlewares/AddressPayIdMiddleware.kt | 2 +- .../send/redux/middlewares/SendMiddleware.kt | 12 +- .../tap/features/wallet/redux/WalletAction.kt | 4 +- .../redux/middlewares/WalletMiddleware.kt | 31 +++- .../redux/middlewares/WarningsMiddleware.kt | 16 +-- .../wallet/redux/reducers/WalletReducer.kt | 36 +++-- .../coinmarketcap/CoinMarketCapService.kt | 4 +- .../tap/network/moonpay/MoonpayService.kt | 4 +- .../tangem/tap/network/payid/PayIdService.kt | 4 +- .../tap/network/payid/PayIdVerifyService.kt | 4 +- .../main/res/values/strings_untranslated.xml | 2 + dependencies.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 56 files changed, 624 insertions(+), 516 deletions(-) delete mode 100644 app/src/main/java/com/tangem/tap/domain/extensions/TangemSdkConstants.kt create mode 100644 app/src/main/java/com/tangem/tap/domain/tasks/SignHashTask.kt create mode 100644 app/src/main/java/com/tangem/tap/domain/tasks/SignHashesTask.kt diff --git a/app/build.gradle b/app/build.gradle index 5bad46aa84..14a1f59a8a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -83,6 +83,8 @@ dependencies { // WebView implementation "androidx.browser:browser:1.3.0" + implementation 'com.jakewharton.timber:timber:4.7.1' + //lifecycle implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.1" implementation "androidx.lifecycle:lifecycle-common-java8:2.3.1" diff --git a/app/src/main/assets/features_dev.json b/app/src/main/assets/features_dev.json index 71561c4c69..82e46b21bf 100644 --- a/app/src/main/assets/features_dev.json +++ b/app/src/main/assets/features_dev.json @@ -2,5 +2,5 @@ "isWalletPayIdEnabled": true, "isSendingToPayIdEnabled": true, "isTopUpEnabled": true, - "isCreatingTwinCardsAllowed": false + "isCreatingTwinCardsAllowed": true } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index 544e1d7cda..89ede4d80d 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -7,10 +7,7 @@ import android.util.Log import android.view.View import androidx.appcompat.app.AppCompatActivity import com.google.android.material.snackbar.Snackbar -import com.tangem.CardFilter -import com.tangem.Config import com.tangem.TangemSdk -import com.tangem.commands.common.card.CardType import com.tangem.tangem_sdk_new.extensions.init import com.tangem.tap.common.DialogManager import com.tangem.tap.common.IntentHandler @@ -30,7 +27,6 @@ import kotlinx.coroutines.Job import java.io.PrintWriter import java.io.StringWriter import java.lang.ref.WeakReference -import java.util.* import kotlin.coroutines.CoroutineContext lateinit var tangemSdk: TangemSdk @@ -68,10 +64,9 @@ class MainActivity : AppCompatActivity(), SnackbarHandler { requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; store.dispatch(NavigationAction.ActivityCreated(WeakReference(this))) - tangemSdk = TangemSdk.init( - this, Config(cardFilter = CardFilter(EnumSet.allOf(CardType::class.java))) - ) - tangemSdkManager = TangemSdkManager(this) + tangemSdk = TangemSdk.init(this, TangemSdkManager.config) + tangemSdkManager = TangemSdkManager(tangemSdk, this) + store.dispatch(WalletConnectAction.RestoreSessions) } diff --git a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsHandler.kt index 30bb8d545d..1022f85c5a 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsHandler.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsHandler.kt @@ -1,12 +1,11 @@ package com.tangem.tap.common.analytics -import com.tangem.TangemSdkError -import com.tangem.blockchain.common.Blockchain -import com.tangem.commands.common.card.Card +import com.tangem.common.card.Card +import com.tangem.common.core.TangemSdkError interface AnalyticsHandler { - fun triggerEvent(event: AnalyticsEvent, card: Card? = null, blockchain: Blockchain? = null) + fun triggerEvent(event: AnalyticsEvent, card: Card? = null, blockchain: String? = null) fun logCardSdkError( error: TangemSdkError, actionToLog: FirebaseAnalyticsHandler.ActionToLog, 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 f9aea4c495..33383e1089 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 @@ -5,13 +5,12 @@ import androidx.core.os.bundleOf import com.google.firebase.analytics.ktx.analytics import com.google.firebase.crashlytics.FirebaseCrashlytics import com.google.firebase.ktx.Firebase -import com.tangem.TangemSdkError -import com.tangem.blockchain.common.Blockchain -import com.tangem.commands.common.card.Card +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: Blockchain?) { + override fun triggerEvent(event: AnalyticsEvent, card: Card?, blockchain: String?) { Firebase.analytics.logEvent(event.event, setData(card, blockchain)) } @@ -47,18 +46,17 @@ object FirebaseAnalyticsHandler : AnalyticsHandler { private fun getParamsFromCard(card: Card): Map { return mapOf( - AnalyticsParam.FIRMWARE to card.firmwareVersion.version, - AnalyticsParam.BATCH_ID to card.cardData?.batchId + AnalyticsParam.FIRMWARE to card.firmwareVersion.stringValue, + AnalyticsParam.BATCH_ID to card.batchId ).filterNotNull() } - private fun setData(card: Card?, blockchain: Blockchain?): Bundle { + private fun setData(card: Card?, blockchain: String?): Bundle { if (card == null) return bundleOf() return bundleOf( - AnalyticsParam.BLOCKCHAIN.param to (blockchain?.currency - ?: card.cardData?.blockchainName), - AnalyticsParam.BATCH_ID.param to card.cardData?.batchId, - AnalyticsParam.FIRMWARE.param to card.firmwareVersion.version + AnalyticsParam.BLOCKCHAIN.param to (blockchain), + AnalyticsParam.BATCH_ID.param to card.batchId, + AnalyticsParam.FIRMWARE.param to card.firmwareVersion.stringValue ) } 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 1c574708e1..0d6b58db28 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 @@ -1,6 +1,6 @@ package com.tangem.tap.common.analytics -import com.tangem.TangemSdkError +import com.tangem.common.core.TangemSdkError object TangemSdk { @@ -31,17 +31,15 @@ object TangemSdk { is TangemSdkError.WalletNotFound -> TangemSdkError.WalletNotFound() is TangemSdkError.AlreadyPersonalized -> TangemSdkError.AlreadyPersonalized() is TangemSdkError.CannotBeDepersonalized -> TangemSdkError.CannotBeDepersonalized() - is TangemSdkError.Pin1Required -> TangemSdkError.Pin1Required() + is TangemSdkError.AccessCodeRequired -> TangemSdkError.AccessCodeRequired() is TangemSdkError.CardReadWrongWallet -> TangemSdkError.CardReadWrongWallet() is TangemSdkError.CardWithMaxZeroWallets -> TangemSdkError.CardWithMaxZeroWallets() is TangemSdkError.AlreadyCreated -> TangemSdkError.AlreadyCreated() - is TangemSdkError.WalletIndexExceedsMaxValue -> TangemSdkError.WalletIndexExceedsMaxValue() is TangemSdkError.MaxNumberOfWalletsCreated -> TangemSdkError.MaxNumberOfWalletsCreated() - is TangemSdkError.WalletIndexNotCorrect -> TangemSdkError.WalletIndexNotCorrect() is TangemSdkError.PurgeWalletProhibited -> TangemSdkError.PurgeWalletProhibited() - is TangemSdkError.Pin1CannotBeChanged -> TangemSdkError.Pin1CannotBeChanged() - is TangemSdkError.Pin2CannotBeChanged -> TangemSdkError.Pin2CannotBeChanged() - is TangemSdkError.Pin1CannotBeDefault -> TangemSdkError.Pin1CannotBeDefault() + is TangemSdkError.AccessCodeCannotBeChanged -> TangemSdkError.AccessCodeCannotBeChanged() + is TangemSdkError.PasscodeCannotBeChanged -> TangemSdkError.PasscodeCannotBeChanged() + is TangemSdkError.AccessCodeCannotBeDefault -> TangemSdkError.AccessCodeCannotBeDefault() is TangemSdkError.NoRemainingSignatures -> TangemSdkError.NoRemainingSignatures() is TangemSdkError.EmptyHashes -> TangemSdkError.EmptyHashes() is TangemSdkError.HashSizeMustBeEqual -> TangemSdkError.HashSizeMustBeEqual() @@ -52,7 +50,7 @@ object TangemSdk { is TangemSdkError.NotPersonalized -> TangemSdkError.NotPersonalized() is TangemSdkError.NotActivated -> TangemSdkError.NotActivated() is TangemSdkError.WalletIsPurged -> TangemSdkError.WalletIsPurged() - is TangemSdkError.Pin2OrCvcRequired -> TangemSdkError.Pin2OrCvcRequired() + is TangemSdkError.PasscodeRequired -> TangemSdkError.PasscodeRequired() is TangemSdkError.VerificationFailed -> TangemSdkError.VerificationFailed() is TangemSdkError.DataSizeTooLarge -> TangemSdkError.DataSizeTooLarge() is TangemSdkError.MissingCounter -> TangemSdkError.MissingCounter() @@ -60,8 +58,8 @@ object TangemSdk { is TangemSdkError.DataCannotBeWritten -> TangemSdkError.DataCannotBeWritten() is TangemSdkError.MissingIssuerPubicKey -> TangemSdkError.MissingIssuerPubicKey() is TangemSdkError.CardVerificationFailed -> TangemSdkError.CardVerificationFailed() - is TangemSdkError.WrongPin1 -> TangemSdkError.WrongPin1() - is TangemSdkError.WrongPin2 -> TangemSdkError.WrongPin2() + is TangemSdkError.WrongAccessCode -> TangemSdkError.WrongAccessCode() + is TangemSdkError.WrongPasscode -> TangemSdkError.WrongPasscode() is TangemSdkError.UnknownError -> TangemSdkError.UnknownError() is TangemSdkError.UserCancelled -> TangemSdkError.UserCancelled() is TangemSdkError.Busy -> TangemSdkError.Busy() @@ -69,8 +67,14 @@ object TangemSdk { is TangemSdkError.WrongCardNumber -> TangemSdkError.WrongCardNumber() is TangemSdkError.WrongCardType -> TangemSdkError.WrongCardType() is TangemSdkError.CardError -> TangemSdkError.CardError() - is TangemSdkError.FirmwareNotSupported -> TangemSdkError.FirmwareNotSupported() + is TangemSdkError.NotSupportedFirmwareVersion -> TangemSdkError.NotSupportedFirmwareVersion() is TangemSdkError.WalletError -> TangemSdkError.WalletError() + is TangemSdkError.WalletCannotBeCreated -> TangemSdkError.WalletCannotBeCreated() + is TangemSdkError.UnsupportedCurve -> TangemSdkError.UnsupportedCurve() + is TangemSdkError.UnsupportedWalletConfig -> TangemSdkError.UnsupportedWalletConfig() + is TangemSdkError.CryptoUtilsError -> TangemSdkError.CryptoUtilsError() + is TangemSdkError.NetworkError -> TangemSdkError.NetworkError(error.customMessage) + is TangemSdkError.ExceptionError -> TangemSdkError.ExceptionError(error.throwable) } } 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 bb32bfa7b4..c1c9137aec 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 @@ -21,6 +21,9 @@ sealed class GlobalAction : Action { } data class SaveScanNoteResponse(val scanNoteResponse: ScanNoteResponse) : GlobalAction() + + data class SetIfCardVerifiedOnline(val verified: Boolean) : GlobalAction() + data class ChangeAppCurrency(val appCurrency: FiatCurrencyName) : GlobalAction() object RestoreAppCurrency : GlobalAction() { data class Success(val appCurrency: FiatCurrencyName) : 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 e499b320c2..d90e263263 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 @@ -1,8 +1,8 @@ package com.tangem.tap.common.redux.global -import com.tangem.TangemSdkError -import com.tangem.commands.common.network.Result import com.tangem.common.CompletionResult +import com.tangem.common.core.TangemSdkError +import com.tangem.common.services.Result import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.redux.AppState import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager 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 7dbbe6f40e..e1f1fa2217 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 @@ -1,8 +1,6 @@ package com.tangem.tap.common.redux.global -import com.tangem.commands.wallet.WalletIndex import com.tangem.tap.common.redux.AppState -import com.tangem.tap.features.details.redux.SecurityOption import org.rekotlin.Action fun globalReducer(action: Action, state: AppState): GlobalState { @@ -30,29 +28,11 @@ fun globalReducer(action: Action, state: AppState): GlobalState { globalState.copy(configManager = action.configManager) } is GlobalAction.SetWarningManager -> globalState.copy(warningManager = action.warningManager) - is GlobalAction.UpdateSecurityOptions -> { - val card = when (action.securityOption) { - SecurityOption.LongTap -> globalState.scanNoteResponse?.card?.copy( - isPin1Default = true, isPin2Default = true - ) - SecurityOption.PassCode -> globalState.scanNoteResponse?.card?.copy( - isPin1Default = true, isPin2Default = false - ) - SecurityOption.AccessCode -> globalState.scanNoteResponse?.card?.copy( - isPin1Default = false, isPin2Default = true - ) - } - if (card != null) { - globalState.copy(scanNoteResponse = globalState.scanNoteResponse?.copy(card = card)) - } else { - globalState - } - } is GlobalAction.UpdateWalletSignedHashes -> { val wallet = globalState.scanNoteResponse?.card - ?.wallet(WalletIndex.PublicKey(action.walletPublicKey)) + ?.wallet(action.walletPublicKey) ?.copy( - signedHashes = action.walletSignedHashes, + totalSignedHashes = action.walletSignedHashes, remainingSignatures = action.remainingSignatures ) val card = globalState.scanNoteResponse?.card @@ -76,6 +56,9 @@ fun globalReducer(action: Action, state: AppState): GlobalState { is GlobalAction.GetMoonPayUserStatus.Success -> { globalState.copy(moonPayUserStatus = action.moonPayUserStatus) } + is GlobalAction.SetIfCardVerifiedOnline -> + globalState.copy(cardVerifiedOnline = action.verified) + else -> globalState } } \ No newline at end of file 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 77b02d5e8c..f370fca90c 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,6 +1,5 @@ package com.tangem.tap.common.redux.global -import com.tangem.commands.common.network.TangemService import com.tangem.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY import com.tangem.tap.domain.PayIdManager import com.tangem.tap.domain.TapWalletManager @@ -14,10 +13,10 @@ import org.rekotlin.StateType data class GlobalState( val scanNoteResponse: ScanNoteResponse? = null, + val cardVerifiedOnline: Boolean = false, val tapWalletManager: TapWalletManager = TapWalletManager(), val payIdManager: PayIdManager = PayIdManager(), val coinMarketCapService: CoinMarketCapService = CoinMarketCapService(), - val tangemService: TangemService = TangemService(), val configManager: ConfigManager? = null, val warningManager: WarningMessagesManager? = null, val feedbackManager: FeedbackManager? = null, diff --git a/app/src/main/java/com/tangem/tap/domain/PayIdManager.kt b/app/src/main/java/com/tangem/tap/domain/PayIdManager.kt index a6e329ca04..cfd5c2b5c2 100644 --- a/app/src/main/java/com/tangem/tap/domain/PayIdManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/PayIdManager.kt @@ -1,7 +1,7 @@ package com.tangem.tap.domain import com.tangem.blockchain.common.Blockchain -import com.tangem.commands.common.network.Result +import com.tangem.common.services.Result import com.tangem.tap.network.payid.PayIdVerifyService import com.tangem.tap.network.payid.VerifyPayIdResponse import kotlinx.coroutines.Dispatchers 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 2a7810f725..56fe47fb45 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -1,23 +1,24 @@ package com.tangem.tap.domain -import androidx.activity.ComponentActivity -import com.tangem.* -import com.tangem.commands.CommandResponse -import com.tangem.commands.PinType -import com.tangem.commands.SetPinCommand -import com.tangem.commands.SetPinResponse -import com.tangem.commands.common.card.Card -import com.tangem.commands.common.card.CardType -import com.tangem.commands.wallet.PurgeWalletCommand -import com.tangem.commands.wallet.PurgeWalletResponse +import android.content.Context +import com.tangem.Message +import com.tangem.TangemSdk +import com.tangem.common.CardFilter import com.tangem.common.CompletionResult -import com.tangem.common.TangemSdkConstants -import com.tangem.common.extensions.calculateSha256 -import com.tangem.tangem_sdk_new.extensions.init +import com.tangem.common.SuccessResponse +import com.tangem.common.card.Card +import com.tangem.common.card.FirmwareVersion +import com.tangem.common.core.CardSessionRunnable +import com.tangem.common.core.Config +import com.tangem.common.core.TangemSdkError +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.operations.wallet.PurgeWalletCommand 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.domain.extensions.getDefaultWalletIndex import com.tangem.tap.domain.tasks.CreateWalletAndRescanTask import com.tangem.tap.domain.tasks.ScanNoteResponse import com.tangem.tap.domain.tasks.ScanNoteTask @@ -25,23 +26,19 @@ import com.tangem.tap.domain.twins.isTwinCard import com.tangem.wallet.R import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import java.util.* import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine -class TangemSdkManager(val activity: ComponentActivity) { - private val tangemSdk = TangemSdk.init( - activity, Config(cardFilter = CardFilter(EnumSet.allOf(CardType::class.java))) - ) +class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Context) { suspend fun scanNote( - analyticsHandler: AnalyticsHandler, messageRes: Int? = null + analyticsHandler: AnalyticsHandler, messageRes: Int? = null, ): CompletionResult { analyticsHandler.triggerEvent(AnalyticsEvent.READY_TO_SCAN, null) - val result = runTaskAsyncReturnOnMain(ScanNoteTask(), - initialMessage = Message( - activity.getString(messageRes ?: R.string.initial_message_scan_header) - )) + val result = runTaskAsyncReturnOnMain(ScanNoteTask(), + initialMessage = Message( + context.getString(messageRes ?: R.string.initial_message_scan_header) + )) if (result is CompletionResult.Failure) { (result.error as? TangemSdkError)?.let { error -> analyticsHandler.logCardSdkError(error, FirebaseAnalyticsHandler.ActionToLog.Scan) @@ -52,53 +49,57 @@ class TangemSdkManager(val activity: ComponentActivity) { suspend fun createWallet(cardId: String?): CompletionResult { return runTaskAsyncReturnOnMain(CreateWalletAndRescanTask(), cardId, - initialMessage = Message(activity.getString(R.string.initial_message_create_wallet_body))) + initialMessage = Message(context.getString(R.string.initial_message_create_wallet_body))) } - suspend fun eraseWallet(cardId: String?): CompletionResult { - return runTaskAsyncReturnOnMain(PurgeWalletCommand( - TangemSdkConstants.getDefaultWalletIndex()), - cardId, - initialMessage = Message(activity.getString(R.string.initial_message_purge_wallet_body))) + suspend fun eraseWallet(card: Card): CompletionResult { + return runTaskAsyncReturnOnMain( + PurgeWalletCommand(card.wallets.first().publicKey), + card.cardId, + initialMessage = Message(context.getString(R.string.initial_message_purge_wallet_body))) } - suspend fun setPasscode(cardId: String?): CompletionResult { - return runTaskAsyncReturnOnMain(SetPinCommand( - pinType = PinType.Pin2, - newPin1 = tangemSdk.config.defaultPin1.calculateSha256(), - newPin2 = null - ), cardId, initialMessage = Message(activity.getString(R.string.initial_message_change_passcode_body))) + suspend fun setPasscode(cardId: String?): CompletionResult { + return runTaskAsyncReturnOnMain( + SetUserCodeCommand.changePasscode(null), + cardId, + initialMessage = Message(context.getString(R.string.initial_message_change_passcode_body))) } - suspend fun setAccessCode(cardId: String?): CompletionResult { - return runTaskAsyncReturnOnMain(SetPinCommand( - pinType = PinType.Pin1, - newPin1 = null, - newPin2 = tangemSdk.config.defaultPin2.calculateSha256() - ), cardId, initialMessage = Message(activity.getString(R.string.initial_message_change_access_code_body))) + suspend fun setAccessCode(cardId: String?): CompletionResult { + return runTaskAsyncReturnOnMain( + SetUserCodeCommand.changeAccessCode(null), + cardId, + initialMessage = Message(context.getString(R.string.initial_message_change_access_code_body))) } - suspend fun setLongTap(cardId: String?): CompletionResult { - return runTaskAsyncReturnOnMain(SetPinCommand( - pinType = PinType.Pin1, - newPin1 = tangemSdk.config.defaultPin1.calculateSha256(), - newPin2 = tangemSdk.config.defaultPin2.calculateSha256() - ), cardId, initialMessage = Message(activity.getString(R.string.initial_message_tap_header))) + suspend fun setLongTap(cardId: String?): CompletionResult { + return runTaskAsyncReturnOnMain( + SetUserCodeCommand.resetUserCodes(), + cardId, + initialMessage = Message(context.getString(R.string.initial_message_tap_header))) + } + + suspend fun checkUserCodes(cardId: String?): CompletionResult { + return runTaskAsyncReturnOnMain( + CheckUserCodesCommand(), + cardId, + initialMessage = Message(context.getString(R.string.initial_message_tap_header))) } suspend fun runTaskAsync( - runnable: CardSessionRunnable, cardId: String? = null, initialMessage: Message? = null, + runnable: CardSessionRunnable, cardId: String? = null, initialMessage: Message? = null, ): CompletionResult = - withContext(Dispatchers.IO) { - suspendCoroutine { continuation -> - tangemSdk.startSessionWithRunnable(runnable, cardId, initialMessage) { result -> - continuation.resume(result) - } + withContext(Dispatchers.Main) { + suspendCoroutine { continuation -> + tangemSdk.startSessionWithRunnable(runnable, cardId, initialMessage) { result -> + continuation.resume(result) } } + } private suspend fun runTaskAsyncReturnOnMain( - runnable: CardSessionRunnable, cardId: String? = null, initialMessage: Message? = null, + runnable: CardSessionRunnable, cardId: String? = null, initialMessage: Message? = null, ): CompletionResult { val result = runTaskAsync(runnable, cardId, initialMessage) return withContext(Dispatchers.Main) { result } @@ -107,4 +108,12 @@ class TangemSdkManager(val activity: ComponentActivity) { fun changeDisplayedCardIdNumbersCount(card: Card) { tangemSdk.config.cardIdDisplayedNumbersCount = if (card.isTwinCard()) 4 else null } + + companion object { + val config = Config( + filter = CardFilter( + allowedCardTypes = FirmwareVersion.FirmwareType.values().toList() + ) + ) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt b/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt index 7a3556b33a..978ca5288f 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt @@ -3,26 +3,25 @@ package com.tangem.tap.domain import com.tangem.Message import com.tangem.TangemSdk import com.tangem.blockchain.common.TransactionSigner -import com.tangem.commands.SignCommand -import com.tangem.commands.SignResponse -import com.tangem.commands.wallet.WalletIndex import com.tangem.common.CompletionResult +import com.tangem.tap.domain.tasks.SignHashTask +import com.tangem.tap.domain.tasks.SignHashesTask import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine class TangemSigner( private val tangemSdk: TangemSdk, private val initialMessage: Message, - private val signerCallback: (SignResponse) -> Unit + private val signerCallback: (TangemSignerResponse) -> Unit, ) : TransactionSigner { override suspend fun sign( hash: ByteArray, cardId: String, - walletPublicKey: ByteArray + walletPublicKey: ByteArray, ): CompletionResult = suspendCoroutine { continuation -> - val command = SignCommand(arrayOf(hash), WalletIndex.PublicKey(walletPublicKey)) + val command = SignHashTask(hash, walletPublicKey) tangemSdk.startSessionWithRunnable( runnable = command, cardId = cardId, @@ -30,8 +29,13 @@ class TangemSigner( ) { result -> when (result) { is CompletionResult.Success -> { - signerCallback(result.data) - continuation.resume(CompletionResult.Success(result.data.signatures.first())) + signerCallback( + TangemSignerResponse( + result.data.totalSignedHashes, + result.data.remainingSignatures + ) + ) + continuation.resume(CompletionResult.Success(result.data.signature)) } is CompletionResult.Failure -> continuation.resume(CompletionResult.Failure(result.error)) @@ -43,18 +47,23 @@ class TangemSigner( override suspend fun sign( hashes: List, cardId: String, - walletPublicKey: ByteArray + walletPublicKey: ByteArray, ): CompletionResult> = suspendCoroutine { continuation -> - val command = SignCommand(hashes.toTypedArray(), WalletIndex.PublicKey(walletPublicKey)) + val task = SignHashesTask(hashes, walletPublicKey) tangemSdk.startSessionWithRunnable( - runnable = command, + runnable = task, cardId = cardId, initialMessage = initialMessage, ) { result -> when (result) { is CompletionResult.Success -> { - signerCallback(result.data) + signerCallback( + TangemSignerResponse( + result.data.totalSignedHashes, + result.data.remainingSignatures + ) + ) continuation.resume(CompletionResult.Success(result.data.signatures)) } is CompletionResult.Failure -> @@ -62,4 +71,9 @@ class TangemSigner( } } } -} \ No newline at end of file +} + +data class TangemSignerResponse( + val totalSignedHashes: Int?, + val remainingSignatures: Int?, +) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt index 521ed462b4..ca57897238 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt @@ -1,7 +1,7 @@ package com.tangem.tap.domain import androidx.annotation.StringRes -import com.tangem.TangemError +import com.tangem.common.core.TangemError import com.tangem.wallet.R interface TapErrors 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 992ddbc606..466e463f19 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -1,9 +1,8 @@ package com.tangem.tap.domain import com.tangem.blockchain.common.* -import com.tangem.commands.common.card.Card -import com.tangem.commands.common.card.CardStatus -import com.tangem.commands.common.network.Result +import com.tangem.common.card.Card +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.redux.global.FiatCurrencyName @@ -89,7 +88,7 @@ class TapWalletManager { suspend fun onCardScanned(data: ScanNoteResponse, addAnalyticsEvent: Boolean = false) { if (addAnalyticsEvent) { - FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.CARD_IS_SCANNED, data.card) + FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.CARD_IS_SCANNED, data.card, data.walletData?.blockchain) } store.state.globalState.feedbackManager?.infoHolder?.setCardInfo(data.card) updateConfigManager(data) @@ -109,7 +108,7 @@ class TapWalletManager { } } - val blockchain = data.card.getBlockchain() + val blockchain = data.getBlockchain() if (blockchain == Blockchain.Ethereum || blockchain == Blockchain.EthereumTestnet) { store.dispatch(TokensAction.LoadCardTokens) @@ -120,12 +119,12 @@ class TapWalletManager { private fun updateConfigManager(data: ScanNoteResponse) { val configManager = store.state.globalState.configManager - val blockchain = data.card.getBlockchain() + val blockchain = data.getBlockchain() if (data.card.isStart2Coin) { configManager?.turnOff(ConfigManager.isSendingToPayIdEnabled) configManager?.turnOff(ConfigManager.isTopUpEnabled) } else if (blockchain == Blockchain.Bitcoin - || data.card.cardData?.blockchainName == Blockchain.Bitcoin.id) { + || data.walletData?.blockchain == Blockchain.Bitcoin.id) { configManager?.resetToDefault(ConfigManager.isSendingToPayIdEnabled) configManager?.resetToDefault(ConfigManager.isTopUpEnabled) } else { @@ -136,25 +135,25 @@ class TapWalletManager { suspend fun loadData(data: ScanNoteResponse) { withContext(Dispatchers.Main) { - val artworkId = data.verifyResponse?.artworkInfo?.id + + store.dispatch(WalletAction.LoadCardInfo(data.card)) + when { - data.card.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> { + data.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> { store.dispatch(WalletAction.LoadData.Failure(TapError.UnknownBlockchain)) - store.dispatch(WalletAction.LoadArtwork(data.card, artworkId)) } - data.card.getStatus() == CardStatus.Empty || + data.card.wallets.isEmpty() || (data.card.isTwinCard() && data.secondTwinPublicKey == null) -> { store.dispatch(WalletAction.EmptyWallet) - store.dispatch(WalletAction.LoadArtwork(data.card, artworkId)) } else -> { val config = store.state.globalState.configManager?.config ?: return@withContext - val blockchain = data.card.getBlockchain() + val blockchain = data.getBlockchain() val primaryWalletManager = walletManagerFactory.makePrimaryWalletManager(data) if (blockchain != null && primaryWalletManager != null) { - val primaryToken = data.card.getToken() + val primaryToken = data.getPrimaryToken() store.dispatch(WalletAction.MultiWallet.SetPrimaryBlockchain(blockchain)) if (primaryToken != null) { @@ -173,17 +172,14 @@ class TapWalletManager { loadMultiWalletData(data.card, blockchain, null) } } - store.dispatch(WalletAction.SetArtworkId(data.verifyResponse?.artworkInfo?.id)) val moonPayUserStatus = store.state.globalState.moonPayUserStatus store.dispatch(WalletAction.LoadWallet( allowToBuy = config.isTopUpEnabled && moonPayUserStatus?.isBuyAllowed == true, allowToSell = config.isTopUpEnabled && moonPayUserStatus?.isSellAllowed == true, )) - store.dispatch(WalletAction.LoadArtwork(data.card, artworkId)) store.dispatch(WalletAction.LoadFiatRate()) } } - store.dispatch(WalletAction.Warnings.CheckIfNeeded) } } @@ -236,10 +232,10 @@ class TapWalletManager { suspend fun reloadData(data: ScanNoteResponse) { withContext(Dispatchers.Main) { when { - data.card.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> { + data.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> { store.dispatch(WalletAction.LoadData.Failure(TapError.UnknownBlockchain)) } - data.card.getStatus() == CardStatus.Empty || + data.card.wallets.isEmpty() || (data.card.isTwinCard() && data.secondTwinPublicKey == null) -> { store.dispatch(WalletAction.EmptyWallet) } diff --git a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt index 6a6723cd83..8af0e99415 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt @@ -1,10 +1,13 @@ package com.tangem.tap.domain -import com.tangem.commands.common.card.Card -import com.tangem.commands.common.card.EllipticCurve -import com.tangem.commands.common.card.masks.Product +import com.tangem.blockchain.common.Blockchain +import com.tangem.common.card.Card +import com.tangem.common.card.EllipticCurve +import com.tangem.common.card.FirmwareVersion +import com.tangem.tap.domain.TapWorkarounds.isNote import com.tangem.tap.domain.TapWorkarounds.isStart2Coin import com.tangem.tap.domain.extensions.getSingleWallet +import com.tangem.tap.domain.twins.isTwinCard import java.util.* object TapWorkarounds { @@ -14,21 +17,37 @@ object TapWorkarounds { } val Card.isStart2Coin: Boolean - get() = isStart2CoinIssuer(cardData?.issuerName) - - fun Card.isExcluded(): Boolean { - val cardData = this.cardData ?: return false - val productMask = cardData.productMask - val excludedBatch = excludedBatches.contains(cardData.batchId) - val excludedIssuerName = excludedIssuers.contains(cardData.issuerName?.uppercase(Locale.ROOT)) - val excludedProductMask = (productMask != null && // product mask is on cards v2.30 and later - !productMask.contains(Product.Note) && !productMask.contains(Product.TwinCard)) - return excludedBatch || excludedIssuerName || excludedProductMask - - } + get() = isStart2CoinIssuer(issuer.name) val Card.isTestCard: Boolean - get() = cardData?.batchId == TEST_CARD_BATCH && cardId.startsWith(TEST_CARD_ID_STARTS_WITH) + get() = batchId == TEST_CARD_BATCH && cardId.startsWith(TEST_CARD_ID_STARTS_WITH) + + fun Card.isExcluded(): Boolean { + val excludedBatch = excludedBatches.contains(batchId) + val excludedIssuerName = excludedIssuers.contains(issuer.name.uppercase(Locale.ROOT)) + return excludedBatch || excludedIssuerName + } + + fun Card.isNote(): Boolean { + return notesBatches.contains(batchId) + } + + fun Card.isMultiCurrencyWallet(): Boolean { + return multiCurrencyWalletsBatches.contains(batchId) + } + + val Card.noteCurrency: Blockchain? + get() { + return when (batchId) { + "AB01" -> Blockchain.Bitcoin + "AB02" -> Blockchain.Ethereum + "AB03" -> Blockchain.CardanoShelley + "AB04" -> Blockchain.Dogecoin + "AB05" -> Blockchain.Binance + "AB06" -> Blockchain.XRP + else -> null + } + } private const val START_2_COIN_ISSUER = "start2coin" private const val TEST_CARD_BATCH = "99FF" @@ -44,12 +63,22 @@ object TapWorkarounds { private val excludedIssuers = listOf( "TTM BANK" ) + + private val notesBatches = listOf( + "AB01", + "AB02", + "AB03", + "AB04", + "AB05", + "AB06", + ) + + private val multiCurrencyWalletsBatches = listOf("AC01") } val Card.isMultiwalletAllowed: Boolean get() { - return cardData?.productMask?.contains(Product.TwinCard) != true - && !isStart2Coin - && (firmwareVersion.major >= 4 || + return !isTwinCard() && !isStart2Coin && !isNote() + && (firmwareVersion >= FirmwareVersion.MultiWalletAvailable || getSingleWallet()?.curve == EllipticCurve.Secp256k1) } \ No newline at end of file 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 5822a252bc..e08ba924b5 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 @@ -1,7 +1,7 @@ package com.tangem.tap.domain.configurable.config import com.tangem.blockchain.common.BlockchainSdkConfig -import com.tangem.tangem_sdk_new.ui.animation.VoidCallback +import com.tangem.common.extensions.VoidCallback import com.tangem.tap.domain.configurable.Loader /** diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt b/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt index 86b1cf4eff..3cdf14dfea 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt @@ -1,7 +1,7 @@ package com.tangem.tap.domain.configurable.warningMessage import com.tangem.blockchain.common.Blockchain -import com.tangem.tangem_sdk_new.ui.animation.VoidCallback +import com.tangem.common.extensions.VoidCallback import com.tangem.tap.common.extensions.containsAny import com.tangem.tap.common.extensions.removeBy import com.tangem.wallet.R diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt b/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt index 0c8ed31dae..be6eb20dcb 100644 --- a/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt +++ b/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt @@ -1,48 +1,18 @@ package com.tangem.tap.domain.extensions -import com.tangem.FirmwareConstraints -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.Token -import com.tangem.commands.common.card.Card -import com.tangem.commands.common.card.CardStatus -import com.tangem.commands.wallet.CardWallet -import com.tangem.commands.wallet.WalletStatus -import com.tangem.common.TangemSdkConstants - -fun Card.getToken(): Token? { - val symbol = cardData?.tokenSymbol ?: return null - val contractAddress = cardData?.tokenContractAddress ?: return null - val decimals = cardData?.tokenDecimal ?: return null - val blockchain = cardData?.blockchainName?.let { Blockchain.fromId(it) } ?: return null - if (symbol.isBlank() || contractAddress.isBlank()) return null - return Token(symbol, contractAddress, decimals, blockchain) -} - -fun Card.getBlockchain(): Blockchain? { - val blockchainName: String = cardData?.blockchainName ?: return null - return Blockchain.fromId(blockchainName) -} +import com.tangem.common.card.Card +import com.tangem.common.card.CardWallet fun Card.getSingleWallet(): CardWallet? { - return wallet(TangemSdkConstants.getDefaultWalletIndex()) -} - -fun Card.getStatus(): CardStatus { - if (firmwareVersion < FirmwareConstraints.AvailabilityVersions.walletData) return status!! - - return if (wallets.any { it.status == WalletStatus.Loaded }) { - CardStatus.Loaded - } else { - CardStatus.Empty - } + return wallets.firstOrNull() } fun Card.hasSignedHashes(): Boolean { - return wallets.any { it.status == WalletStatus.Loaded && it.signedHashes ?: 0 > 0 } + return wallets.any { it.totalSignedHashes ?: 0 > 0 } } fun Card.signedHashesCount(): Int { - return wallets.map { it.signedHashes ?: 0 }.sum() + return wallets.map { it.totalSignedHashes ?: 0 }.sum() } val Card.remainingSignatures: Int? diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/TangemSdkConstants.kt b/app/src/main/java/com/tangem/tap/domain/extensions/TangemSdkConstants.kt deleted file mode 100644 index c8710e3aeb..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/extensions/TangemSdkConstants.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.tangem.tap.domain.extensions - -import com.tangem.commands.wallet.WalletIndex -import com.tangem.common.TangemSdkConstants - -fun TangemSdkConstants.Companion.getDefaultWalletIndex(): WalletIndex { - return WalletIndex.Index(oldCardDefaultWalletIndex) -} \ No newline at end of file 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 88583ddf84..5ec3f1afa9 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 @@ -3,9 +3,9 @@ package com.tangem.tap.domain.extensions import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.WalletManager import com.tangem.blockchain.common.WalletManagerFactory -import com.tangem.commands.common.card.Card -import com.tangem.commands.common.card.EllipticCurve -import com.tangem.commands.wallet.CardWallet +import com.tangem.common.card.Card +import com.tangem.common.card.CardWallet +import com.tangem.common.card.EllipticCurve import com.tangem.common.extensions.hexToBytes import com.tangem.tap.domain.TapWorkarounds.isTestCard import com.tangem.tap.domain.tasks.ScanNoteResponse @@ -54,9 +54,9 @@ fun WalletManagerFactory.makePrimaryWalletManager( ): WalletManager? { val card = data.card val blockchain = if (card.isTestCard) { - card.getBlockchain()?.getTestnetVersion() + data.getBlockchain()?.getTestnetVersion() } else { - card.getBlockchain() + data.getBlockchain() } val supportedCurves = blockchain?.getSupportedCurves() ?: return null val wallets = card.wallets.filter { wallet -> supportedCurves.contains(wallet.curve) } diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt index 19b87612a0..ee2cda0d50 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt @@ -1,28 +1,27 @@ package com.tangem.tap.domain.tasks -import com.tangem.CardSession -import com.tangem.CardSessionRunnable -import com.tangem.FirmwareConstraints -import com.tangem.TangemSdkError -import com.tangem.commands.common.card.Card import com.tangem.common.CompletionResult -import com.tangem.tasks.CreateWalletTask -import com.tangem.tasks.PreflightReadCapable -import com.tangem.tasks.PreflightReadSettings -import com.tangem.tasks.PreflightReadTask +import com.tangem.common.card.Card +import com.tangem.common.card.FirmwareVersion +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.operations.PreflightReadMode +import com.tangem.operations.PreflightReadTask +import com.tangem.operations.wallet.CreateWalletCommand -class CreateWalletAndRescanTask : CardSessionRunnable, PreflightReadCapable { - override val requiresPin2 = false - override fun preflightReadSettings() = PreflightReadSettings.FullCardRead +class CreateWalletAndRescanTask : CardSessionRunnable { override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { - val firmwareVerion = session.environment.card?.firmwareVersion - if (firmwareVerion == null) { + val card = session.environment.card.guard { callback(CompletionResult.Failure(TangemSdkError.CardError())) return } - val task = if (firmwareVerion < FirmwareConstraints.AvailabilityVersions.walletData) { - CreateWalletTask() + val firmwareVersion = card.firmwareVersion + + val task = if (firmwareVersion < FirmwareVersion.MultiWalletAvailable) { + CreateWalletCommand(card.supportedCurves.first(), false) } else { CreateWalletsTask() } @@ -30,7 +29,7 @@ class CreateWalletAndRescanTask : CardSessionRunnable, PreflightReadCapabl task.run(session) { result -> when (result) { is CompletionResult.Success -> - PreflightReadTask(PreflightReadSettings.FullCardRead).run(session, callback) + PreflightReadTask(PreflightReadMode.FullCardRead).run(session, callback) is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) } } diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt index b72cf26ae3..1443371141 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt @@ -1,51 +1,47 @@ package com.tangem.tap.domain.tasks -import com.tangem.CardSession -import com.tangem.CardSessionRunnable -import com.tangem.commands.common.card.Card -import com.tangem.commands.common.card.EllipticCurve -import com.tangem.commands.wallet.CreateWalletCommand -import com.tangem.commands.wallet.WalletConfig import com.tangem.common.CompletionResult -import com.tangem.tasks.PreflightReadSettings -import com.tangem.tasks.PreflightReadTask +import com.tangem.common.card.Card +import com.tangem.common.card.EllipticCurve +import com.tangem.common.core.CardSession +import com.tangem.common.core.CardSessionRunnable +import com.tangem.operations.PreflightReadMode +import com.tangem.operations.PreflightReadTask +import com.tangem.operations.wallet.CreateWalletCommand -class CreateWalletsTask(wallets: List = emptyList()) : CardSessionRunnable { - override val requiresPin2 = false +class CreateWalletsTask(curves: List = emptyList()) : CardSessionRunnable { - val wallets = if (wallets.isEmpty()) { + private val curves = if (curves.isEmpty()) { listOf( - WalletConfig(null, null, EllipticCurve.Secp256k1, null), - WalletConfig(null, null, EllipticCurve.Ed25519, null), - WalletConfig(null, null, EllipticCurve.Secp256r1, null), + EllipticCurve.Secp256k1, + EllipticCurve.Ed25519, + EllipticCurve.Secp256r1, ) } else { - wallets + curves } var index = 0 override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { - val walletConfig = wallets[index] - createWallet(walletConfig, session, callback) + val curve = curves[index] + createWallet(curve, session, callback) } private fun createWallet( - walletConfig: WalletConfig, session: CardSession, + curve: EllipticCurve, session: CardSession, callback: (result: CompletionResult) -> Unit ) { - CreateWalletCommand( - walletConfig, walletIndexValue = walletConfig.curveId!!.toWalletIndex() - ).run(session) { result -> + CreateWalletCommand(curve, false).run(session) { result -> when (result) { is CompletionResult.Success -> { - if (index == wallets.lastIndex) { - PreflightReadTask(PreflightReadSettings.FullCardRead).run(session, callback) + if (index == curves.lastIndex) { + PreflightReadTask(PreflightReadMode.FullCardRead).run(session, callback) return@run } index += 1 - createWallet(wallets[index], session, callback) + createWallet(curves[index], session, callback) } is CompletionResult.Failure -> { callback(CompletionResult.Failure(result.error)) diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt index 0415316f32..ca90ead2c5 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt @@ -1,50 +1,67 @@ package com.tangem.tap.domain.tasks import com.tangem.* +import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.BlockchainSdkConfig +import com.tangem.blockchain.common.Token import com.tangem.blockchain.common.WalletManagerFactory -import com.tangem.commands.CommandResponse -import com.tangem.commands.ReadIssuerDataCommand -import com.tangem.commands.common.card.Card -import com.tangem.commands.common.card.CardStatus -import com.tangem.commands.common.card.EllipticCurve -import com.tangem.commands.verification.VerifyCardCommand -import com.tangem.commands.verification.VerifyCardResponse -import com.tangem.commands.wallet.WalletConfig import com.tangem.common.CompletionResult +import com.tangem.common.card.Card +import com.tangem.common.card.EllipticCurve +import com.tangem.common.card.WalletData +import com.tangem.common.core.CardSession +import com.tangem.common.core.CardSessionRunnable +import com.tangem.common.core.TangemError import com.tangem.common.extensions.toHexString +import com.tangem.operations.CommandResponse +import com.tangem.operations.ScanTask +import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand import com.tangem.tap.domain.TapSdkError import com.tangem.tap.domain.TapWorkarounds.isExcluded +import com.tangem.tap.domain.TapWorkarounds.isMultiCurrencyWallet +import com.tangem.tap.domain.TapWorkarounds.noteCurrency import com.tangem.tap.domain.extensions.getSingleWallet -import com.tangem.tap.domain.extensions.getStatus import com.tangem.tap.domain.twins.TwinCardsManager import com.tangem.tap.domain.twins.isTwinCard import com.tangem.tap.store -import com.tangem.tasks.PreflightReadCapable -import com.tangem.tasks.PreflightReadSettings -import com.tangem.tasks.ScanTask +import com.tangem.wallet.R data class ScanNoteResponse( - val card: Card, - val verifyResponse: VerifyCardResponse? = null, - val secondTwinPublicKey: String? = null, -) : CommandResponse + val card: Card, + val walletData: WalletData?, + val secondTwinPublicKey: String? = null, +) : CommandResponse { -class ScanNoteTask(val card: Card? = null) : CardSessionRunnable, PreflightReadCapable { - override val requiresPin2 = false + fun getBlockchain(): Blockchain? { + if (card.noteCurrency != null) return card.noteCurrency + val blockchainName: String = walletData?.blockchain ?: return null + return Blockchain.fromId(blockchainName) + } - override fun preflightReadSettings() = PreflightReadSettings.FullCardRead + fun getPrimaryToken(): Token? { + val cardToken = walletData?.token ?: return null + return Token( + cardToken.name, + cardToken.symbol, + cardToken.contractAddress, + cardToken.decimals, + Blockchain.fromId(walletData.blockchain) + ) + } +} - override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { +class ScanNoteTask(val card: Card? = null) : CardSessionRunnable { + + override fun run( + session: CardSession, + callback: (result: CompletionResult) -> Unit, + ) { ScanTask().run(session) { result -> when (result) { is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) is CompletionResult.Success -> { - val card = this.card?.copy( - isPin1Default = result.data.isPin1Default, - isPin2Default = result.data.isPin2Default, - ) ?: result.data + val card = this.card ?: result.data val error = getErrorIfExcludedCard(card) if (error != null) { @@ -52,39 +69,28 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable) -> Unit - ) { - VerifyCardCommand(true).run(session) { verifyResult -> - when (verifyResult) { - is CompletionResult.Success -> { if (card.isTwinCard()) { - dealWithTwinCard(card, session, verifyResult.data, callback) + dealWithTwinCard(card, session, callback) } else if (card.firmwareVersion.major >= 4) { - createMissingWalletsIfNeeded(card, session, verifyResult.data, callback) + createMissingWalletsIfNeeded(card, session, callback) } else { - callback(CompletionResult.Success(ScanNoteResponse(card, verifyResult.data))) + callback(CompletionResult.Success( + ScanNoteResponse(card, session.environment.walletData))) } } - is CompletionResult.Failure -> { - callback(CompletionResult.Failure(TangemSdkError.CardVerificationFailed())) - } } } } private fun createMissingWalletsIfNeeded( - card: Card, session: CardSession, verifyResponse: VerifyCardResponse, - callback: (result: CompletionResult) -> Unit + card: Card, session: CardSession, + callback: (result: CompletionResult) -> Unit, ) { - if (card.getStatus() == CardStatus.Empty) { - callback(CompletionResult.Success(ScanNoteResponse(card, verifyResponse))) + val walletData = session.environment.walletData + if (card.wallets.isEmpty()) { + callback(CompletionResult.Success( + ScanNoteResponse(card, walletData) + )) return } @@ -92,20 +98,14 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable - WalletConfig( - isReusable = null, prohibitPurgeWallet = null, curveId = curve, - signingMethods = null - ) - } - CreateWalletsTask(configs).run(session) { result -> + CreateWalletsTask(curvesToCreate.toList()).run(session) { result -> when (result) { is CompletionResult.Success -> - callback(CompletionResult.Success(ScanNoteResponse(result.data, verifyResponse))) + callback(CompletionResult.Success(ScanNoteResponse(result.data, walletData))) is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) } } @@ -113,8 +113,8 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable) -> Unit + card: Card, session: CardSession, + callback: (result: CompletionResult) -> Unit, ) { ReadIssuerDataCommand().run(session) { readDataResult -> when (readDataResult) { @@ -125,12 +125,15 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable { + override fun run(session: CardSession, callback: CompletionCallback) { + SignHashCommand(hash, walletPublicKey).run(session) { response -> + + when (response) { + is CompletionResult.Success -> { + callback(CompletionResult.Success(TangemSignHashResponse( + response.data.signature, + response.data.totalSignedHashes, + session.environment.card?.wallet(walletPublicKey)?.remainingSignatures + ))) + } + is CompletionResult.Failure -> + callback(CompletionResult.Failure(response.error)) + } + } + } +} diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/SignHashesTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/SignHashesTask.kt new file mode 100644 index 0000000000..2755d9aa82 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/tasks/SignHashesTask.kt @@ -0,0 +1,35 @@ +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.operations.CommandResponse +import com.tangem.operations.sign.SignHashesCommand + +class TangemSignHashesResponse( + val signatures: List, + val totalSignedHashes: Int?, + val remainingSignatures: Int?, +) : CommandResponse + +class SignHashesTask( + private val hashes: Collection, + private val walletPublicKey: ByteArray, +) : CardSessionRunnable { + override fun run(session: CardSession, callback: CompletionCallback) { + SignHashesCommand(hashes.toTypedArray(), walletPublicKey).run(session) { response -> + when (response) { + is CompletionResult.Success -> { + callback(CompletionResult.Success(TangemSignHashesResponse( + response.data.signatures, + response.data.totalSignedHashes, + session.environment.card?.wallet(walletPublicKey)?.remainingSignatures + ))) + } + is CompletionResult.Failure -> + callback(CompletionResult.Failure(response.error)) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/termsOfUse/CardTou.kt b/app/src/main/java/com/tangem/tap/domain/termsOfUse/CardTou.kt index c5d9d91396..032c0b97ca 100644 --- a/app/src/main/java/com/tangem/tap/domain/termsOfUse/CardTou.kt +++ b/app/src/main/java/com/tangem/tap/domain/termsOfUse/CardTou.kt @@ -3,7 +3,7 @@ package com.tangem.tap.domain.termsOfUse import android.content.res.Resources import android.net.Uri import androidx.core.os.ConfigurationCompat -import com.tangem.commands.common.card.Card +import com.tangem.common.card.Card import java.util.* /** @@ -13,8 +13,8 @@ class CardTou { private val locale: Locale = ConfigurationCompat.getLocales(Resources.getSystem().configuration).get(0) fun getUrl(card: Card): Uri? { - val issuerName = card.cardData?.issuerName ?: return null - if (issuerName.toLowerCase() != "start2coin") return null + val issuerName = card.issuer.name ?: return null + if (issuerName.lowercase(Locale.getDefault()) != "start2coin") return null val baseUrl = "https://app.tangem.com/tou/" val regionCode = regionCode(card.cardId) ?: "fr" 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 054698b51b..26aa55369c 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 @@ -8,7 +8,7 @@ import com.squareup.moshi.JsonClass import com.squareup.moshi.Types import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token -import com.tangem.commands.common.card.FirmwareVersion +import com.tangem.common.card.FirmwareVersion import com.tangem.tap.common.extensions.readJsonFileToString import com.tangem.tap.network.createMoshi @@ -127,7 +127,7 @@ class CurrenciesRepository(val context: Application) { cardFirmware: FirmwareVersion?, isTestNet: Boolean = false ): List { - return if (cardFirmware == null || cardFirmware.major < 4) { + return if (cardFirmware == null || cardFirmware < FirmwareVersion.MultiWalletAvailable) { Blockchain.secp256k1Blockchains(isTestNet) } else { Blockchain.secp256k1Blockchains(isTestNet) + Blockchain.ed25519OnlyBlockchains(isTestNet) diff --git a/app/src/main/java/com/tangem/tap/domain/topup/TopUpManager.kt b/app/src/main/java/com/tangem/tap/domain/topup/TopUpManager.kt index 568c65f7d0..775d3748a7 100644 --- a/app/src/main/java/com/tangem/tap/domain/topup/TopUpManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/topup/TopUpManager.kt @@ -34,9 +34,9 @@ class TopUpManager { ) { signResponse -> store.dispatch( GlobalAction.UpdateWalletSignedHashes( - walletSignedHashes = signResponse.walletSignedHashes, - remainingSignatures = signResponse.walletRemainingSignatures, - walletPublicKey = walletManager.wallet.publicKey + walletSignedHashes = signResponse.totalSignedHashes, + walletPublicKey = walletManager.wallet.publicKey, + remainingSignatures = signResponse.remainingSignatures ) ) } diff --git a/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt index 0251173136..0ca3f113c3 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt @@ -1,34 +1,34 @@ package com.tangem.tap.domain.twins -import com.tangem.CardSession -import com.tangem.CardSessionRunnable -import com.tangem.commands.wallet.CreateWalletResponse -import com.tangem.commands.wallet.PurgeWalletCommand import com.tangem.common.CompletionResult -import com.tangem.common.TangemSdkConstants -import com.tangem.tap.domain.extensions.getDefaultWalletIndex +import com.tangem.common.card.EllipticCurve +import com.tangem.common.core.CardSession +import com.tangem.common.core.CardSessionRunnable +import com.tangem.operations.wallet.CreateWalletCommand +import com.tangem.operations.wallet.CreateWalletResponse +import com.tangem.operations.wallet.PurgeWalletCommand import com.tangem.tap.domain.extensions.getSingleWallet -import com.tangem.tasks.CreateWalletTask class CreateFirstTwinWalletTask : CardSessionRunnable { - override val requiresPin2 = false - override fun run( session: CardSession, callback: (result: CompletionResult) -> Unit, ) { - if (session.environment.card?.getSingleWallet()?.publicKey != null) { - PurgeWalletCommand(TangemSdkConstants.getDefaultWalletIndex()).run(session) { response -> + val publicKey = session.environment.card?.getSingleWallet()?.publicKey + if (publicKey != null) { + PurgeWalletCommand(publicKey).run(session) { response -> when (response) { is CompletionResult.Success -> { - session.environment.card = session.environment.card?.changeStatusToEmpty() - CreateWalletTask().run(session) { callback(it) } + session.environment.card = session.environment.card?.setWallets(emptyList()) + CreateWalletCommand(EllipticCurve.Secp256k1, false) + .run(session) { callback(it) } } is CompletionResult.Failure -> callback(CompletionResult.Failure(response.error)) } } } else { - CreateWalletTask().run(session) { callback(it) } + CreateWalletCommand(EllipticCurve.Secp256k1, false) + .run(session) { callback(it) } } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt index c8e936bcce..1c0af37da4 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt @@ -1,31 +1,29 @@ package com.tangem.tap.domain.twins -import com.tangem.CardSession -import com.tangem.CardSessionRunnable import com.tangem.Message -import com.tangem.commands.wallet.CreateWalletResponse -import com.tangem.commands.wallet.PurgeWalletCommand import com.tangem.common.CompletionResult -import com.tangem.common.TangemSdkConstants +import com.tangem.common.card.EllipticCurve +import com.tangem.common.core.CardSession +import com.tangem.common.core.CardSessionRunnable import com.tangem.common.extensions.hexToBytes -import com.tangem.tap.domain.extensions.getDefaultWalletIndex +import com.tangem.operations.wallet.CreateWalletCommand +import com.tangem.operations.wallet.CreateWalletResponse +import com.tangem.operations.wallet.PurgeWalletCommand import com.tangem.tap.domain.extensions.getSingleWallet -import com.tangem.tasks.CreateWalletTask class CreateSecondTwinWalletTask( private val firstPublicKey: String, private val preparingMessage: Message, private val creatingWalletMessage: Message ) : CardSessionRunnable { - override val requiresPin2 = true - override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { - if (session.environment.card?.getSingleWallet()?.publicKey != null) { + val publicKey = session.environment.card?.getSingleWallet()?.publicKey + if (publicKey != null) { session.setInitialMessage(preparingMessage) - PurgeWalletCommand(TangemSdkConstants.getDefaultWalletIndex()).run(session) { response -> + PurgeWalletCommand(publicKey).run(session) { response -> when (response) { is CompletionResult.Success -> { - session.environment.card = session.environment.card?.changeStatusToEmpty() + session.environment.card = session.environment.card?.setWallets(emptyList()) finishTask(session, callback) } is CompletionResult.Failure -> callback(CompletionResult.Failure(response.error)) @@ -38,10 +36,10 @@ class CreateSecondTwinWalletTask( private fun finishTask(session: CardSession, callback: (result: CompletionResult) -> Unit) { session.setInitialMessage(creatingWalletMessage) - CreateWalletTask().run(session) { result -> + CreateWalletCommand(EllipticCurve.Secp256k1, false).run(session) { result -> when (result) { is CompletionResult.Success -> { - session.environment.card = session.environment.card?.changeStatusToLoaded() + session.environment.card = session.environment.card?.updateWallet(result.data.wallet) WriteProtectedIssuerDataTask( firstPublicKey.hexToBytes(), TwinCardsManager.issuerKeys diff --git a/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt index 8eb0315241..aeef1b9481 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt @@ -1,18 +1,17 @@ package com.tangem.tap.domain.twins -import com.tangem.CardSession -import com.tangem.CardSessionRunnable -import com.tangem.KeyPair import com.tangem.common.CompletionResult +import com.tangem.common.KeyPair +import com.tangem.common.core.CardSession +import com.tangem.common.core.CardSessionRunnable +import com.tangem.operations.PreflightReadMode +import com.tangem.operations.PreflightReadTask import com.tangem.tap.domain.tasks.ScanNoteResponse import com.tangem.tap.domain.tasks.ScanNoteTask -import com.tangem.tasks.PreflightReadSettings -import com.tangem.tasks.PreflightReadTask class FinalizeTwinTask( private val twinPublicKey: ByteArray, private val issuerKeys: KeyPair, ) : CardSessionRunnable { - override val requiresPin2 = true override fun run( session: CardSession, @@ -21,7 +20,7 @@ class FinalizeTwinTask( WriteProtectedIssuerDataTask(twinPublicKey, issuerKeys).run(session) { result -> when (result) { is CompletionResult.Success -> - PreflightReadTask(PreflightReadSettings.FullCardRead).run(session) { readResult -> + PreflightReadTask(PreflightReadMode.FullCardRead).run(session) { readResult -> when (readResult) { is CompletionResult.Success -> ScanNoteTask(readResult.data).run(session, callback) 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 af088dc421..0282a18d65 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 @@ -1,11 +1,11 @@ package com.tangem.tap.domain.twins -import com.tangem.KeyPair import com.tangem.Message -import com.tangem.TangemSdkError import com.tangem.blockchain.extensions.Result import com.tangem.blockchain.extensions.SimpleResult import com.tangem.common.CompletionResult +import com.tangem.common.KeyPair +import com.tangem.common.core.TangemSdkError import com.tangem.common.extensions.hexToBytes import com.tangem.common.extensions.toHexString import com.tangem.crypto.CryptoUtils @@ -27,7 +27,7 @@ class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse) { ) when (response) { is CompletionResult.Success -> { - currentCardPublicKey = response.data.walletPublicKey.toHexString() + currentCardPublicKey = response.data.wallet.publicKey.toHexString() return SimpleResult.Success } is CompletionResult.Failure -> { @@ -56,7 +56,7 @@ class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse) { ) when (response) { is CompletionResult.Success -> { - secondCardPublicKey = response.data.walletPublicKey.toHexString() + secondCardPublicKey = response.data.wallet.publicKey.toHexString() return SimpleResult.Success } is CompletionResult.Failure -> { diff --git a/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt b/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt index c534afbec6..9a6a892264 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt @@ -1,10 +1,8 @@ package com.tangem.tap.domain.twins -import com.tangem.commands.common.card.Card -import com.tangem.commands.common.card.CardStatus -import com.tangem.commands.common.card.masks.Product -import com.tangem.commands.wallet.WalletStatus +import com.tangem.common.card.Card import com.tangem.tap.common.extensions.isEven +import com.tangem.tap.domain.twins.TwinsHelper.Companion.getTwinCardNumber class TwinsHelper { companion object { @@ -78,19 +76,9 @@ enum class TwinCardNumber(val number: Int) { } fun Card.isTwinCard(): Boolean { - return this.cardData?.productMask?.contains(Product.TwinCard) == true + return getTwinCardNumber(cardId) != null } fun Card.getTwinCardIdForUser(): String { return TwinsHelper.getTwinCardIdForUser(this.cardId) -} - -fun Card.changeStatusToLoaded(): Card { - val wallets = wallets.map { it.copy(status = WalletStatus.Loaded) } - return copy(status = CardStatus.Loaded, wallets = wallets) -} - -fun Card.changeStatusToEmpty(): Card { - val wallets = wallets.map { it.copy(status = WalletStatus.Empty) } - return copy(status = CardStatus.Empty, wallets = wallets) } \ No newline at end of file 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 bf6c735be5..94ce765f12 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 @@ -1,49 +1,61 @@ package com.tangem.tap.domain.twins -import com.tangem.CardSession -import com.tangem.CardSessionRunnable -import com.tangem.KeyPair -import com.tangem.TangemSdkError -import com.tangem.commands.* import com.tangem.common.CompletionResult -import com.tangem.common.TangemSdkConstants +import com.tangem.common.KeyPair +import com.tangem.common.SuccessResponse +import com.tangem.common.core.CardSession +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.tap.domain.extensions.getDefaultWalletIndex +import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand +import com.tangem.operations.issuerAndUserData.ReadIssuerDataResponse +import com.tangem.operations.issuerAndUserData.WriteIssuerDataCommand +import com.tangem.operations.sign.SignHashCommand +import com.tangem.tap.domain.extensions.getSingleWallet class WriteProtectedIssuerDataTask( - private val twinPublicKey: ByteArray, private val issuerKeys: KeyPair -) : CardSessionRunnable { - override val requiresPin2 = true + private val twinPublicKey: ByteArray, private val issuerKeys: KeyPair, +) : CardSessionRunnable { - override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { - val hashes = arrayOf(twinPublicKey.calculateSha256()) - SignCommand(hashes, TangemSdkConstants.getDefaultWalletIndex()).run(session) { signResult -> - when (signResult) { - is CompletionResult.Success -> { - ReadIssuerDataCommand().run(session) { readResult -> - when (readResult) { - is CompletionResult.Success -> { - writeIssuerData( - twinPublicKey, issuerKeys, signResult.data.signatures[0], + override fun run( + session: CardSession, + callback: (result: CompletionResult) -> Unit, + ) { + SignHashCommand( + twinPublicKey.calculateSha256(), + session.environment.card!!.getSingleWallet()!!.publicKey + ) + .run(session) { signResult -> + when (signResult) { + is CompletionResult.Success -> { + ReadIssuerDataCommand().run(session) { readResult -> + when (readResult) { + is CompletionResult.Success -> { + writeIssuerData( + twinPublicKey, issuerKeys, signResult.data.signature, readResult.data, session, callback - ) + ) + } + is CompletionResult.Failure -> callback(CompletionResult.Failure( + readResult.error)) } - is CompletionResult.Failure -> callback(CompletionResult.Failure(readResult.error)) } } + is CompletionResult.Failure -> callback(CompletionResult.Failure(signResult.error)) } - is CompletionResult.Failure -> callback(CompletionResult.Failure(signResult.error)) } - } } private fun writeIssuerData( - twinPublicKey: ByteArray, issuerKeys: KeyPair, cardSignature: ByteArray, - readResponse: ReadIssuerDataResponse, - session: CardSession, callback: (result: CompletionResult - ) -> Unit) { + twinPublicKey: ByteArray, issuerKeys: KeyPair, cardSignature: ByteArray, + readResponse: ReadIssuerDataResponse, + session: CardSession, + callback: ( + result: CompletionResult, + ) -> Unit, + ) { val cardId = session.environment.card?.cardId.guard { callback(CompletionResult.Failure(TangemSdkError.CardError())) return @@ -51,11 +63,11 @@ class WriteProtectedIssuerDataTask( val counter = (readResponse.issuerDataCounter ?: 0) + 1 val data = twinPublicKey + cardSignature val signedByIssuer = FileHashHelper.prepareHashes( - cardId, data, counter, issuerKeys.privateKey + cardId, data, counter, issuerKeys.privateKey ) WriteIssuerDataCommand( - data, signedByIssuer.finalizingSignature!!, - counter, issuerKeys.publicKey + data, signedByIssuer.finalizingSignature!!, + counter, issuerKeys.publicKey ).run(session, callback) } } \ 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 7cff86a035..5ebfb1cb14 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 @@ -1,7 +1,6 @@ package com.tangem.tap.domain.walletconnect import com.tangem.Message -import com.tangem.TangemSdkError import com.tangem.blockchain.blockchains.ethereum.EthereumGasLoader import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionExtras import com.tangem.blockchain.blockchains.ethereum.EthereumUtils @@ -11,10 +10,10 @@ import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.common.TransactionSender import com.tangem.blockchain.extensions.* -import com.tangem.commands.SignCommand -import com.tangem.commands.wallet.WalletIndex import com.tangem.common.CompletionResult +import com.tangem.common.core.TangemSdkError import com.tangem.common.extensions.hexToBytes +import com.tangem.operations.sign.SignHashCommand import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler import com.tangem.tap.common.extensions.toFormattedString import com.tangem.tap.features.details.redux.walletconnect.* @@ -150,9 +149,9 @@ class WalletConnectSdkHelper { gasLimit = null ) ?: return null - val command = SignCommand( - hashes = arrayOf(dataToSign.hash), - walletIndex = WalletIndex.PublicKey(data.walletManager.wallet.publicKey) + val command = SignHashCommand( + hash = dataToSign.hash, + walletPublicKey = data.walletManager.wallet.publicKey ) val result = tangemSdkManager.runTaskAsync(command, initialMessage = Message()) return when (result) { @@ -210,13 +209,10 @@ class WalletConnectSdkHelper { suspend fun signPersonalMessage(hashToSign: ByteArray, wallet: WalletForSession): String? { val publicKey = wallet.walletPublicKey.hexToBytes() - val command = SignCommand( - arrayOf(hashToSign), - WalletIndex.PublicKey(publicKey)) + val command = SignHashCommand(hashToSign, publicKey) return when (val result = tangemSdkManager.runTaskAsync(command, wallet.cardId)) { is CompletionResult.Success -> { - val hash = result.data.signatures.first() - + val hash = result.data.signature return EthereumUtils.prepareSignedMessageData( hash, hashToSign, publicKey ) diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt index f37ef1c696..79067f026e 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt @@ -2,7 +2,8 @@ package com.tangem.tap.features.details.redux import com.tangem.Message import com.tangem.blockchain.common.Wallet -import com.tangem.commands.common.card.Card +import com.tangem.common.card.Card +import com.tangem.operations.pins.CheckUserCodesResponse import com.tangem.tap.common.redux.NotificationAction import com.tangem.tap.common.redux.global.FiatCurrencyName import com.tangem.tap.domain.tasks.ScanNoteResponse @@ -90,6 +91,8 @@ sealed class DetailsAction : Action { } sealed class ManageSecurity : DetailsAction() { + data class CheckCurrentSecurityOption(val cardId: String?) : ManageSecurity() + data class SetCurrentOption(val userCodes: CheckUserCodesResponse) : ManageSecurity() object OpenSecurity : ManageSecurity() data class SelectOption(val option: SecurityOption) : ManageSecurity() object SaveChanges : ManageSecurity() { 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 90f75c3efe..15eca00858 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 @@ -1,9 +1,10 @@ package com.tangem.tap.features.details.redux -import com.tangem.TangemSdkError -import com.tangem.commands.common.network.Result import com.tangem.common.CompletionResult +import com.tangem.common.core.TangemSdkError +import com.tangem.common.services.Result import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler +import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.AppScreen @@ -83,10 +84,9 @@ class DetailsMiddleware { store.dispatch(NavigationAction.PopBackTo()) } is DetailsAction.EraseWallet.Confirm -> { + val card = store.state.detailsState.card ?: return scope.launch { - val result = tangemSdkManager.eraseWallet( - store.state.detailsState.card?.cardId - ) + val result = tangemSdkManager.eraseWallet(card) withContext(Dispatchers.Main) { when (result) { is CompletionResult.Success -> { @@ -124,6 +124,21 @@ class DetailsMiddleware { class ManageSecurityMiddleware { fun handle(action: DetailsAction.ManageSecurity) { when (action) { + is DetailsAction.ManageSecurity.CheckCurrentSecurityOption -> { + scope.launch { + when (val response = tangemSdkManager.checkUserCodes(action.cardId)) { + is CompletionResult.Success -> { + store.dispatchOnMain( + DetailsAction.ManageSecurity.SetCurrentOption(response.data) + ) + store.dispatchOnMain(DetailsAction.ManageSecurity.OpenSecurity) + } + is CompletionResult.Failure -> { + + } + } + } + } is DetailsAction.ManageSecurity.OpenSecurity -> { store.dispatch(NavigationAction.NavigateTo(AppScreen.DetailsSecurity)) } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt index 132dbddb18..f9a55c5271 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt @@ -1,8 +1,7 @@ package com.tangem.tap.features.details.redux -import com.tangem.commands.common.card.Card -import com.tangem.commands.common.card.masks.Settings +import com.tangem.common.card.Card import com.tangem.tap.common.redux.AppState import com.tangem.tap.domain.TapWorkarounds.isStart2Coin import com.tangem.tap.domain.extensions.isWalletDataSupported @@ -47,17 +46,6 @@ private fun internalReduce(action: Action, state: AppState): DetailsState { } private fun handlePrepareScreen(action: DetailsAction.PrepareScreen, state: DetailsState): DetailsState { - val securityOption = when { - action.card.isPin1Default == false -> { - SecurityOption.AccessCode - } - action.card.isPin2Default == false -> { - SecurityOption.PassCode - } - else -> { - SecurityOption.LongTap - } - } val twinsState = if (action.card.isTwinCard()) { CreateTwinWalletState( scanResponse = action.scanNoteResponse, @@ -76,7 +64,6 @@ private fun handlePrepareScreen(action: DetailsAction.PrepareScreen, state: Deta appCurrencyState = AppCurrencyState( action.fiatCurrencyName ), - securityScreenState = SecurityScreenState(currentOption = securityOption), createTwinWalletState = twinsState, cardTermsOfUseUrl = action.cardTou.getUrl(action.card) ) @@ -86,8 +73,7 @@ private fun handleEraseWallet(action: DetailsAction.EraseWallet, state: DetailsS return when (action) { DetailsAction.EraseWallet.Check -> { val notAllowedByCard = - state.card?.settingsMask?.contains(Settings.ProhibitPurgeWallet) == true - || state.card?.settingsMask?.contains(Settings.IsReusable) == false + state.card?.settings?.isPermanentWallet == true || state.card?.isWalletDataSupported == true val notEmpty = state.wallets.any { !it.recentTransactions.toPendingTransactions(it.address).isNullOrEmpty() @@ -142,6 +128,20 @@ private fun handleSecurityAction( action: DetailsAction.ManageSecurity, state: DetailsState ): DetailsState { return when (action) { + is DetailsAction.ManageSecurity.SetCurrentOption -> { + val securityOption = when { + action.userCodes.isAccessCodeSet -> { + SecurityOption.AccessCode + } + action.userCodes.isPasscodeSet -> { + SecurityOption.PassCode + } + else -> { + SecurityOption.LongTap + } + } + state.copy(securityScreenState = SecurityScreenState(currentOption = securityOption)) + } is DetailsAction.ManageSecurity.OpenSecurity -> { if (state.card?.isStart2Coin == true) { return state.copy(securityScreenState = state.securityScreenState?.copy( @@ -149,13 +149,10 @@ private fun handleSecurityAction( selectedOption = state.securityScreenState.currentOption )) } - if (state.card?.isPin2Default == null) { - return state.copy(securityScreenState = state.securityScreenState?.copy( - allowedOptions = EnumSet.noneOf(SecurityOption::class.java) - )) - } - val allowedSecurityOptions = prepareAllowedSecurityOptions(state.card) + val allowedSecurityOptions = prepareAllowedSecurityOptions( + state.card, state.securityScreenState?.currentOption + ) state.copy(securityScreenState = state.securityScreenState?.copy( allowedOptions = allowedSecurityOptions, selectedOption = state.securityScreenState.currentOption @@ -177,7 +174,6 @@ private fun handleSecurityAction( is DetailsAction.ManageSecurity.SaveChanges.Success -> { // Setting options to show only LongTap from now on state.copy( - card = state.card?.copy(isPin1Default = true, isPin2Default = true), securityScreenState = state.securityScreenState?.copy( currentOption = state.securityScreenState.selectedOption, allowedOptions = EnumSet.of(SecurityOption.LongTap) @@ -188,19 +184,19 @@ private fun handleSecurityAction( } } -private fun prepareAllowedSecurityOptions(card: Card): EnumSet { - val prohibitDefaultPin = card.settingsMask?.contains(Settings.ProhibitDefaultPIN1) == true - val isDefaultPin1 = card.isPin1Default != false - val isDefaultPin2 = card.isPin2Default != false +private fun prepareAllowedSecurityOptions( + card: Card?, currentSecurityOption: SecurityOption? +): EnumSet { + val prohibitDefaultPin = card?.settings?.isRemovingAccessCodeAllowed != true val allowedSecurityOptions = EnumSet.noneOf(SecurityOption::class.java) - if ((isDefaultPin1 && isDefaultPin2) || !prohibitDefaultPin) { + if ((currentSecurityOption == SecurityOption.LongTap) || !prohibitDefaultPin) { allowedSecurityOptions.add(SecurityOption.LongTap) } - if (!isDefaultPin1) { + if (currentSecurityOption == SecurityOption.AccessCode) { allowedSecurityOptions.add(SecurityOption.AccessCode) } - if (!isDefaultPin2) { + if (currentSecurityOption == SecurityOption.PassCode) { allowedSecurityOptions.add(SecurityOption.PassCode) } return allowedSecurityOptions @@ -209,6 +205,6 @@ private fun prepareAllowedSecurityOptions(card: Card): EnumSet { private fun Card.toCardInfo(): CardInfo? { val cardId = this.cardId.chunked(4).joinToString(separator = " ") - val issuer = this.cardData?.issuerName ?: return null + val issuer = this.issuer.name val signedHashes = this.signedHashesCount() return CardInfo(cardId, issuer, signedHashes)} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt index 5ef1033e2f..bf16dd3674 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt @@ -2,7 +2,7 @@ package com.tangem.tap.features.details.redux import android.net.Uri import com.tangem.blockchain.common.Wallet -import com.tangem.commands.common.card.Card +import com.tangem.common.card.Card import com.tangem.tap.common.entities.Button import com.tangem.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY import com.tangem.tap.common.redux.global.FiatCurrencyName 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 6b9dfc4b07..813dba5494 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,8 +1,8 @@ package com.tangem.tap.features.details.redux.walletconnect import com.tangem.blockchain.common.* -import com.tangem.commands.common.card.Card import com.tangem.common.CompletionResult +import com.tangem.common.card.Card import com.tangem.common.extensions.toHexString import com.tangem.tap.* import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler 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 3b0e9c83a1..55f9711482 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 @@ -126,7 +126,7 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber) { diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt index 3da9747dd3..33a7313b5d 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt @@ -2,7 +2,7 @@ package com.tangem.tap.features.send.redux.middlewares import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Wallet -import com.tangem.commands.common.network.Result +import com.tangem.common.services.Result import com.tangem.tap.common.redux.AppState import com.tangem.tap.domain.PayIdManager import com.tangem.tap.domain.isPayIdSupported 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 d24bfa3949..2c5b8ebd58 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 @@ -1,12 +1,12 @@ package com.tangem.tap.features.send.redux.middlewares import com.google.firebase.crashlytics.FirebaseCrashlytics -import com.tangem.TangemSdkError import com.tangem.blockchain.blockchains.stellar.StellarTransactionExtras import com.tangem.blockchain.blockchains.xrp.XrpTransactionBuilder import com.tangem.blockchain.common.* import com.tangem.blockchain.extensions.SimpleResult -import com.tangem.commands.common.card.Card +import com.tangem.common.card.Card +import com.tangem.common.core.TangemSdkError import com.tangem.tap.common.analytics.AnalyticsEvent import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler import com.tangem.tap.common.extensions.dispatchOnMain @@ -136,9 +136,9 @@ private fun sendTransaction( ) { signResponse -> store.dispatch( GlobalAction.UpdateWalletSignedHashes( - walletSignedHashes = signResponse.walletSignedHashes, - remainingSignatures = signResponse.walletRemainingSignatures, - walletPublicKey = walletManager.wallet.publicKey + walletSignedHashes = signResponse.totalSignedHashes, + walletPublicKey = walletManager.wallet.publicKey, + remainingSignatures = signResponse.remainingSignatures ) ) } @@ -150,7 +150,7 @@ private fun sendTransaction( FirebaseAnalyticsHandler.triggerEvent( event = AnalyticsEvent.TRANSACTION_IS_SENT, card = card, - blockchain = walletManager.wallet.blockchain + blockchain = walletManager.wallet.blockchain.currency ) dispatch(SendAction.SendSuccess) 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 cff630a1a8..488adca48d 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 @@ -3,7 +3,7 @@ package com.tangem.tap.features.wallet.redux import android.content.Context import com.tangem.blockchain.common.* import com.tangem.blockchain.common.address.AddressType -import com.tangem.commands.common.card.Card +import com.tangem.common.card.Card import com.tangem.tap.common.redux.ErrorAction import com.tangem.tap.common.redux.NotificationAction import com.tangem.tap.domain.TapError @@ -93,6 +93,8 @@ sealed class WalletAction : Action { object Failure : WalletAction() } + class LoadCardInfo(val card: Card) : WalletAction() + data class LoadArtwork(val card: Card, val artworkId: String?) : WalletAction() { data class Success(val artwork: Artwork) : WalletAction() object Failure : WalletAction() 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 8dc207048b..8c0b638587 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 @@ -3,13 +3,16 @@ package com.tangem.tap.features.wallet.redux.middlewares import android.content.Intent import android.net.Uri import androidx.core.content.ContextCompat -import com.tangem.TangemSdkError import com.tangem.blockchain.common.* import com.tangem.common.CompletionResult +import com.tangem.common.core.TangemSdkError 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.extensions.copyToClipboard +import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.shareText import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction @@ -155,6 +158,32 @@ class WalletMiddleware { } } } + is WalletAction.LoadCardInfo -> { + scope.launch { + val response = OnlineCardVerifier().getCardInfo( + action.card.cardId, action.card.cardPublicKey + ) + when (response) { + is Result.Success -> { + store.dispatchOnMain( + WalletAction.SetArtworkId(response.data.artwork?.id) + ) + store.dispatchOnMain( + WalletAction.LoadArtwork(action.card, response.data.artwork?.id) + ) + store.dispatchOnMain( + GlobalAction.SetIfCardVerifiedOnline(response.data.passed) + ) + } + is Result.Failure -> { + store.dispatchOnMain( + GlobalAction.SetIfCardVerifiedOnline(false) + ) + } + } + store.dispatchOnMain(WalletAction.Warnings.CheckIfNeeded) + } + } is WalletAction.LoadData -> { scope.launch { val scanNoteResponse = globalState?.scanNoteResponse 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 32694c8282..d3a6c2d046 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 @@ -4,10 +4,8 @@ import com.tangem.blockchain.common.BlockchainSdkError import com.tangem.blockchain.common.SignatureCountValidator import com.tangem.blockchain.common.Wallet import com.tangem.blockchain.extensions.SimpleResult -import com.tangem.commands.common.card.Card -import com.tangem.commands.common.card.CardType -import com.tangem.commands.verification.VerifyCardState -import com.tangem.common.extensions.getType +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 @@ -91,15 +89,13 @@ class WarningsMiddleware { } showWarningLowRemainingSignaturesIfNeeded(card) - if (card.getType() != CardType.Release) { + if (card.firmwareVersion.type != FirmwareVersion.FirmwareType.Release) { addWarningMessage(WarningMessagesManager.devCardWarning()) } else if (!preferencesStorage.wasCardScannedBefore(card.cardId)) { checkIfWarningNeeded(card)?.let { warning -> addWarningMessage(warning) } } - if (card.getType() == CardType.Release) { - if (globalState.scanNoteResponse.verifyResponse?.verificationState == - VerifyCardState.VerifiedOffline - ) { + if (card.firmwareVersion.type == FirmwareVersion.FirmwareType.Release) { + if (!globalState.cardVerifiedOnline) { addWarningMessage(WarningMessagesManager.onlineVerificationFailed()) } } @@ -161,7 +157,7 @@ class WarningsMiddleware { val validator = store.state.walletState.walletManagers.firstOrNull() as? SignatureCountValidator scope.launch { - val signedHashes = card.getSingleWallet()?.signedHashes ?: 0 + val signedHashes = card.getSingleWallet()?.totalSignedHashes ?: 0 val result = validator?.validateSignatureCount(signedHashes) withContext(Dispatchers.Main) { when (result) { 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 3298b8a9cc..9bdef4b340 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 @@ -2,8 +2,8 @@ package com.tangem.tap.features.wallet.redux.reducers import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.Wallet -import com.tangem.commands.common.network.TangemService import com.tangem.common.extensions.toHexString +import com.tangem.operations.attestation.OnlineCardVerifier import com.tangem.tap.common.extensions.* import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.FiatCurrencyName @@ -216,21 +216,27 @@ private fun internalReduce(action: Action, state: AppState): WalletState { newState = setNewFiatRate(action.fiatRate, state.globalState.appCurrency, newState) is WalletAction.LoadArtwork -> { val cardId = action.card.cardId - val cardPublicKey = action.card.cardPublicKey?.toHexString() - val artworkUrl = if (cardPublicKey != null && action.artworkId != null) { - TangemService.getUrlForArtwork(cardId, cardPublicKey, action.artworkId) - } else if (action.card.cardId.startsWith(Artwork.SERGIO_CARD_ID)) { - Artwork.SERGIO_CARD_URL - } else if (action.card.cardId.startsWith(Artwork.MARTA_CARD_ID)) { - Artwork.MARTA_CARD_URL - } else if (newState.twinCardsState?.cardNumber != null) { - when (newState.twinCardsState?.cardNumber) { - TwinCardNumber.First -> Artwork.TWIN_CARD_1 - TwinCardNumber.Second -> Artwork.TWIN_CARD_2 - null -> Artwork.DEFAULT_IMG_URL + val cardPublicKey = action.card.cardPublicKey.toHexString() + val artworkUrl = when { + action.artworkId != null -> { + OnlineCardVerifier.getUrlForArtwork(cardId, cardPublicKey, action.artworkId) + } + action.card.cardId.startsWith(Artwork.SERGIO_CARD_ID) -> { + Artwork.SERGIO_CARD_URL + } + action.card.cardId.startsWith(Artwork.MARTA_CARD_ID) -> { + Artwork.MARTA_CARD_URL + } + newState.twinCardsState?.cardNumber != null -> { + when (newState.twinCardsState?.cardNumber) { + TwinCardNumber.First -> Artwork.TWIN_CARD_1 + TwinCardNumber.Second -> Artwork.TWIN_CARD_2 + null -> Artwork.DEFAULT_IMG_URL + } + } + else -> { + Artwork.DEFAULT_IMG_URL } - } else { - Artwork.DEFAULT_IMG_URL } newState = newState.copy(cardImage = Artwork(artworkId = artworkUrl)) } diff --git a/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapService.kt b/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapService.kt index fffc439820..cf497373cd 100644 --- a/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapService.kt +++ b/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapService.kt @@ -1,7 +1,7 @@ package com.tangem.tap.network.coinmarketcap -import com.tangem.commands.common.network.Result -import com.tangem.commands.common.network.performRequest +import com.tangem.common.services.Result +import com.tangem.common.services.performRequest import com.tangem.tap.common.redux.global.FiatCurrencyName import com.tangem.tap.store import kotlinx.coroutines.Dispatchers diff --git a/app/src/main/java/com/tangem/tap/network/moonpay/MoonpayService.kt b/app/src/main/java/com/tangem/tap/network/moonpay/MoonpayService.kt index c20d22863e..9ef4b8b678 100644 --- a/app/src/main/java/com/tangem/tap/network/moonpay/MoonpayService.kt +++ b/app/src/main/java/com/tangem/tap/network/moonpay/MoonpayService.kt @@ -1,8 +1,8 @@ package com.tangem.tap.network.moonpay import com.squareup.moshi.JsonClass -import com.tangem.commands.common.network.Result -import com.tangem.commands.common.network.performRequest +import com.tangem.common.services.Result +import com.tangem.common.services.performRequest import com.tangem.tap.network.createRetrofitInstance class MoonpayService { diff --git a/app/src/main/java/com/tangem/tap/network/payid/PayIdService.kt b/app/src/main/java/com/tangem/tap/network/payid/PayIdService.kt index 2e691228ea..6eab298c6f 100644 --- a/app/src/main/java/com/tangem/tap/network/payid/PayIdService.kt +++ b/app/src/main/java/com/tangem/tap/network/payid/PayIdService.kt @@ -1,8 +1,8 @@ package com.tangem.tap.network.payid import com.squareup.moshi.JsonClass -import com.tangem.commands.common.network.Result -import com.tangem.commands.common.network.performRequest +import com.tangem.common.services.Result +import com.tangem.common.services.performRequest import com.tangem.tap.network.createRetrofitInstance import retrofit2.Retrofit diff --git a/app/src/main/java/com/tangem/tap/network/payid/PayIdVerifyService.kt b/app/src/main/java/com/tangem/tap/network/payid/PayIdVerifyService.kt index 109dc5e97f..13d79d4591 100644 --- a/app/src/main/java/com/tangem/tap/network/payid/PayIdVerifyService.kt +++ b/app/src/main/java/com/tangem/tap/network/payid/PayIdVerifyService.kt @@ -1,7 +1,7 @@ package com.tangem.tap.network.payid -import com.tangem.commands.common.network.Result -import com.tangem.commands.common.network.performRequest +import com.tangem.common.services.Result +import com.tangem.common.services.performRequest import com.tangem.tap.network.createRetrofitInstance /** diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml index 01bdb3d5d6..9404210575 100644 --- a/app/src/main/res/values/strings_untranslated.xml +++ b/app/src/main/res/values/strings_untranslated.xml @@ -166,4 +166,6 @@ this wallet. Do you want to buy or sell crypto? + + Oops, the current version of the application is not ready to work with this card, please check for updates. diff --git a/dependencies.gradle b/dependencies.gradle index e4beaa5aaa..5567b6aa65 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -1,4 +1,4 @@ ext.versions = [ kotlin : '1.5.20', - build_gradle: '4.2.2', + build_gradle: '7.0.0', ] diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index c6dab66ff6..8e5b6ad562 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip From 237afe50ae6afacc204b3b64993111ffdf6142a9 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 3 Aug 2021 14:38:29 +0300 Subject: [PATCH 019/189] Updated on 2026-08-14 --- app/src/main/res/layout/item_currency_wallet.xml | 4 ++-- app/src/main/res/layout/layout_warning.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/layout/item_currency_wallet.xml b/app/src/main/res/layout/item_currency_wallet.xml index 9721744db7..ce49ed06df 100644 --- a/app/src/main/res/layout/item_currency_wallet.xml +++ b/app/src/main/res/layout/item_currency_wallet.xml @@ -152,12 +152,12 @@ android:id="@+id/tv_amount_fiat" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginBottom="16dp" android:paddingStart="16dp" android:paddingEnd="16dp" android:textColor="@color/darkGray2" android:textSize="14sp" - app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintBottom_toBottomOf="@id/tv_exchange_rate" + app:layout_constraintTop_toTopOf="@id/tv_exchange_rate" app:layout_constraintEnd_toEndOf="parent" tools:text="0.43 USD" /> diff --git a/app/src/main/res/layout/layout_warning.xml b/app/src/main/res/layout/layout_warning.xml index a9a76d4637..e2f6f41d6f 100644 --- a/app/src/main/res/layout/layout_warning.xml +++ b/app/src/main/res/layout/layout_warning.xml @@ -44,7 +44,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" - android:layout_marginTop="12dp" + android:layout_marginTop="4dp" android:layout_marginEnd="16dp" android:layout_marginBottom="16dp" android:text="Test text messaging, or texting, is the act of composing and sending electronic messages, typically consisting of alphabetic and numeric characters, between" From a6331b229166feb0bffe5ff2d77f0415e8bcd6f0 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 9 Aug 2021 09:14:41 +0300 Subject: [PATCH 020/189] Updated on 2026-08-14 --- app/build.gradle | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 14a1f59a8a..e97b9251f0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -67,18 +67,17 @@ dependencies { implementation fileTree(include: ['*.aar'], dir: 'libs') implementation 'androidx.core:core-ktx:1.6.0' - implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'androidx.fragment:fragment-ktx:1.3.5' - implementation 'androidx.constraintlayout:constraintlayout:2.0.4' + implementation 'androidx.appcompat:appcompat:1.3.1' + implementation 'androidx.constraintlayout:constraintlayout:2.1.0' implementation 'com.google.android.material:material:1.4.0' implementation "androidx.core:core-ktx:1.6.0" implementation 'com.google.android.play:core:1.10.0' 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-34' - implementation 'com.tangem:core:fixes_for_app_release-44' - implementation 'com.tangem:sdk:fixes_for_app_release-44' + implementation 'com.tangem:blockchain:develop-36' + implementation 'com.tangem.tangem-sdk-kotlin:core:develop-47' + implementation 'com.tangem.tangem-sdk-kotlin:android:develop-47' // WebView implementation "androidx.browser:browser:1.3.0" From 4f494e66522f91784e754d80450268a6ed6f5519 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 11 Aug 2021 23:13:21 +0300 Subject: [PATCH 021/189] Updated on 2026-08-14 --- app/src/main/assets/binance_tokens.json | 1428 +++++++++++++++++ .../main/assets/binance_tokens_testnet.json | 15 +- 2 files changed, 1442 insertions(+), 1 deletion(-) diff --git a/app/src/main/assets/binance_tokens.json b/app/src/main/assets/binance_tokens.json index 1d01ab9280..08ed8356fa 100644 --- a/app/src/main/assets/binance_tokens.json +++ b/app/src/main/assets/binance_tokens.json @@ -1654,5 +1654,1433 @@ }, "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ZEC-93E/logo.png", "name" : "ZECBEP2" + }, + { + "contractAddress": "NMX-7AFM", + "symbol": "NMX", + "name": "NOMINEX", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "GTMRS-D96M", + "symbol": "GTMRS", + "name": "Getmoders", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "DRC-FF4M", + "symbol": "DRC", + "name": "DARIK COIN", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "IMC-D5AM", + "symbol": "IMC", + "name": "IMC", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "JPIN-89AM", + "symbol": "JPIN", + "name": "JADEPIN", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "TBCC", + "symbol": "TBCC", + "name": "TBC", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "SZAR-952M", + "symbol": "SZAR", + "name": "SMART ZAR", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "SOUL-D11M", + "symbol": "SOUL", + "name": "CryptoSoul", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "VTBC-C26M", + "symbol": "VTBC", + "name": "TBCCVPN", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "LGVM-0F6M", + "symbol": "LGVM", + "name": "Longevium", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "JUV-FC7M", + "symbol": "JUV", + "name": "Juventus", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "KNB-9D6M", + "symbol": "KNB", + "name": "Kronobit", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "POLO-E0DM", + "symbol": "POLO", + "name": "MarcoPoloCoin", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "FIO-0CDM", + "symbol": "FIO", + "name": "FIO", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "BIR", + "symbol": "BIR", + "name": "BinanceIran", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "SBC-AF0M", + "symbol": "SBC", + "name": "SBCOIN", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "DPSC-CCBM", + "symbol": "DPSC", + "name": "DPASSCOIN", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "EPIC-066M", + "symbol": "EPIC", + "name": "EPIC", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "UBETS-619M", + "symbol": "UBETS", + "name": "UBETS Coin", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "JDXU-932M", + "symbol": "JDXU", + "name": "JDXUCoin", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "GMNG-084M", + "symbol": "GMNG", + "name": "GlobalGaming", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "BTC-992M", + "symbol": "BTC", + "name": "Bitcoin", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "ETH-EDDM", + "symbol": "ETH", + "name": "Ethereum", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "ANK-5D8M", + "symbol": "ANK", + "name": "AppleNetwork", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "FLGHT-96EM", + "symbol": "FLGHT", + "name": "TheFlight", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "YTC-6BFM", + "symbol": "YTC", + "name": "YouTubeCoin", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "MSV-114M", + "symbol": "MSV", + "name": "MoskvinInvest", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "AZBIT-A07M", + "symbol": "AZBIT", + "name": "AZbitCryptoExchange", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "BFT-321M", + "symbol": "BFT", + "name": "BitForexToken", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "LIBRA-D54M", + "symbol": "LIBRA", + "name": "FacebookCoin", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "ZANO-F71M", + "symbol": "ZANO", + "name": "Zano", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "BTS-FA7M", + "symbol": "BTS", + "name": "BTSentertainment", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "VNB-684M", + "symbol": "VNB", + "name": "VNNetwork", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "MKR-119M", + "symbol": "MKR", + "name": "Maker", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "XAV-3C5M", + "symbol": "XAV", + "name": "XAVIERCOIN", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "TSL-751M", + "symbol": "TSL", + "name": "TESLA", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "GRAM-6BBM", + "symbol": "GRAM", + "name": "TelegramOpenNetwork", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "ZENK-8BCM", + "symbol": "ZENK", + "name": "ZenKoin", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "STK70-882M", + "symbol": "STK70", + "name": "STK70", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "UBER-5D0M", + "symbol": "UBER", + "name": "UBER", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "CR7-A67M", + "symbol": "CR7", + "name": "CristianoRonaldoFanToken", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "ITTT-C65M", + "symbol": "ITTT", + "name": "IntuitiveToken", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "FNXS-983M", + "symbol": "FNXS", + "name": "FinanceX", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "ELT-61FM", + "symbol": "ELT", + "name": "ELITE", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "KODK-9ABM", + "symbol": "KODK", + "name": "EastmanKodakCompany", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "COMP-3C5M", + "symbol": "COMP", + "name": "Compound", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "CCT-AD7M", + "symbol": "CCT", + "name": "CryptoCompareToken", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "BNS-5ADM", + "symbol": "BNS", + "name": "BNSAFEER", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "10XDF-9FBM", + "symbol": "10XDF", + "name": "10xDeFiXy", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "RUBY-FFCM", + "symbol": "RUBY", + "name": "VNOvernight", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "BHC-3E8M", + "symbol": "BHC", + "name": "BillionHappiness", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "SAND-25CM", + "symbol": "SAND", + "name": "Sandbox", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "VNBIT-5D5M", + "symbol": "VNBIT", + "name": "VNBit", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "CVD-940M", + "symbol": "CVD", + "name": "Covid19", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "100X-0A2M", + "symbol": "100X", + "name": "100x", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "PLV-328M", + "symbol": "PLV", + "name": "PLAVNI", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "VOTE-692M", + "symbol": "VOTE", + "name": "Vote", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "NBTC-09BM", + "symbol": "NBTC", + "name": "NBTC", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "WEBIT-4D0M", + "symbol": "WEBIT", + "name": "WEBIT", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "BUSY-3AFM", + "symbol": "BUSY", + "name": "BusyProtocol", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "DEFIX-46FM", + "symbol": "DEFIX", + "name": "DefiX", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "200DX-985M", + "symbol": "200DX", + "name": "200DX", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "GOGH-510M", + "symbol": "GOGH", + "name": "Vincent", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "HOOTZ-CE2M", + "symbol": "HOOTZ", + "name": "Hootz", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "YFFI-7E4M", + "symbol": "YFFI", + "name": "YFFIfinance", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "YFII-7FBM", + "symbol": "YFII", + "name": "yearnfinanceII", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "TNH-2D6M", + "symbol": "TNH", + "name": "Tanhu", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "GOLD-42AM", + "symbol": "GOLD", + "name": "GOLD", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "CMW-CF8M", + "symbol": "CMW", + "name": "CMWLTD14", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "AMZN-2A5M", + "symbol": "AMZN", + "name": "Amazon", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "YFL-4CFM", + "symbol": "YFL", + "name": "YFLink", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "CUCAP-090M", + "symbol": "CUCAP", + "name": "CopperCommons", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "YETH-687M", + "symbol": "YETH", + "name": "YETH", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "XLS-A82M", + "symbol": "XLS", + "name": "Xwolfers", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "POKER-C92M", + "symbol": "POKER", + "name": "POKERSTARS", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "XCT", + "symbol": "XCT", + "name": "Citron", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "SIP", + "symbol": "SIP", + "name": "Arbitrx", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "KDC-4E4M", + "symbol": "KDC", + "name": "KingdomCasino", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "INNBC", + "symbol": "INNBC", + "name": "InnovativeBioresearchCoin", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "ODAEE-AD8M", + "symbol": "ODAEE", + "name": "ODAEE", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "CALTO-C51M", + "symbol": "CALTO", + "name": "caltechcasino", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "ZONIC-C26M", + "symbol": "ZONIC", + "name": "ZONIC", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "TAINO-CC7M", + "symbol": "TAINO", + "name": "TAINOCOIN", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "BZAR-188M", + "symbol": "BZAR", + "name": "Bitcoinrand", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "JSB-614M", + "symbol": "JSB", + "name": "JSB", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "BSATT-9F8M", + "symbol": "BSATT", + "name": "BinanceSATT", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "GOAL-A7EM", + "symbol": "GOAL", + "name": "GoalCoin", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "ALF-346M", + "symbol": "ALF", + "name": "AlfaBlock", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "GLXY-783M", + "symbol": "GLXY", + "name": "GalaxianCoin", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "FXP-BE7M", + "symbol": "FXP", + "name": "FXPay", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "ATM-95EM", + "symbol": "ATM", + "name": "Atletico Madrid", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "VRTN2-F89M", + "symbol": "VRTN2", + "name": "VINYL RECORDS TOKEN 2", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "STN-DB6M", + "symbol": "STN", + "name": "Saturno", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "TON-12EM", + "symbol": "TON", + "name": "Telegram Open Network", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "FLATS-9A4M", + "symbol": "FLATS", + "name": "FLAT EARTH", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "LTT-D42M", + "symbol": "LTT", + "name": "LiteTrader", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "FUTSWAP-8D1M", + "symbol": "FUTSWAP", + "name": "Futswap", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "IBAI-6FDM", + "symbol": "IBAI", + "name": "IBAI FAN TOKEN", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "BNGO-9D3M", + "symbol": "BNGO", + "name": "BINGO", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "STH-07AM", + "symbol": "STH", + "name": "SmartHoldem", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "WSB-63CM", + "symbol": "WSB", + "name": "WallStreetBets", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "XAQ-ABCM", + "symbol": "XAQ", + "name": "AQAQ", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "XCL-DAFM", + "symbol": "XCL", + "name": "CLICO", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "AGU-14FM", + "symbol": "AGU", + "name": "Aguacate Mini Token", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "GRPX-D25M", + "symbol": "GRPX", + "name": "Grapbox", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "SHIT-580M", + "symbol": "SHIT", + "name": "SHIT DIAMOND", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "SWAN-288M", + "symbol": "SWAN", + "name": "Swan Finance", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "TRS-182M", + "symbol": "TRS", + "name": "Traders", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "SZR-4F6M", + "symbol": "SZR", + "name": "Smart ZAR", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "TOD-442M", + "symbol": "TOD", + "name": "TOD COIN", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "WWWC-C3EM", + "symbol": "WWWC", + "name": "World Wide Wallet Coin", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "CFN-D88M", + "symbol": "CFN", + "name": "Cafeina Token", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "BZB-034M", + "symbol": "BZB", + "name": "One million BZE", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "CCSCEX-E2CM", + "symbol": "CCSCEX", + "name": "EXCHANGE", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "XOTL-247M", + "symbol": "XOTL", + "name": "Xolotl Token", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "UWIM-06AM", + "symbol": "UWIM", + "name": "UWIM", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "LEARN", + "symbol": "LEARN", + "name": "BinanceAcademyToken", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "IKA-105M", + "symbol": "IKA", + "name": "Mining", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } + }, + { + "contractAddress": "SCAM-914M", + "symbol": "SCAM", + "name": "SCAM", + "decimalCount": 8, + "type": "bep8", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + } } ] diff --git a/app/src/main/assets/binance_tokens_testnet.json b/app/src/main/assets/binance_tokens_testnet.json index fe51488c70..7d5590d293 100644 --- a/app/src/main/assets/binance_tokens_testnet.json +++ b/app/src/main/assets/binance_tokens_testnet.json @@ -1 +1,14 @@ -[] +[ + { + "symbol" : "HEM", + "decimalCount" : 8, + "contractAddress" : "HEM-452", + "blockchain" : { + "key" : "binance", + "curve" : "secp256k1", + "testnet" : false + }, + "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/MATIC-84A/logo.png", + "name" : "Hemster - 452" + } +] From 54ee7d1d9701786f792fb2ca9c7555c26e388184 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 16 Aug 2021 09:04:52 +0300 Subject: [PATCH 022/189] Updated on 2026-08-14 --- .../java/com/tangem/tap/features/wallet/redux/WalletState.kt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) 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 d08f8caecf..0d81713483 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 @@ -216,10 +216,7 @@ data class WalletData( ) { fun shouldShowMultipleAddress(): Boolean { val listOfAddresses = walletAddresses?.list ?: return false - return (currency?.blockchain == Blockchain.Bitcoin || - currency?.blockchain == Blockchain.BitcoinTestnet || - currency?.blockchain == Blockchain.CardanoShelley) && - listOfAddresses.size > 1 + return listOfAddresses.size > 1 } } From bb1f7b4c97bb5d131f8cf2863c60992d00d76d39 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 16 Aug 2021 19:31:01 +0300 Subject: [PATCH 023/189] Updated on 2026-08-14 --- app/build.gradle | 16 ++++++++-------- dependencies.gradle | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 5bad46aa84..d9946006d0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -67,9 +67,9 @@ dependencies { implementation fileTree(include: ['*.aar'], dir: 'libs') implementation 'androidx.core:core-ktx:1.6.0' - implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'androidx.fragment:fragment-ktx:1.3.5' - implementation 'androidx.constraintlayout:constraintlayout:2.0.4' + implementation 'androidx.appcompat:appcompat:1.3.1' + implementation 'androidx.fragment:fragment-ktx:1.3.6' + implementation 'androidx.constraintlayout:constraintlayout:2.1.0' implementation 'com.google.android.material:material:1.4.0' implementation "androidx.core:core-ktx:1.6.0" implementation 'com.google.android.play:core:1.10.0' @@ -113,8 +113,8 @@ dependencies { // Network and Json implementation 'com.squareup.retrofit2:retrofit:2.8.1' implementation 'com.squareup.retrofit2:converter-moshi:2.6.0' - implementation 'com.squareup.moshi:moshi:1.9.2' - implementation "com.squareup.moshi:moshi-kotlin:1.9.2" + implementation 'com.squareup.moshi:moshi:1.12.0' + implementation "com.squareup.moshi:moshi-kotlin:1.12.0" implementation 'com.squareup.okhttp3:logging-interceptor:4.2.2' // // //Crypto @@ -122,8 +122,8 @@ dependencies { testImplementation 'junit:junit:4.13.2' - testImplementation "com.google.truth:truth:1.0.1" - androidTestImplementation 'androidx.test.ext:junit:1.1.2' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' + testImplementation "com.google.truth:truth:1.1.3" + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' } \ No newline at end of file diff --git a/dependencies.gradle b/dependencies.gradle index e4beaa5aaa..cb6bb51434 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -1,4 +1,4 @@ ext.versions = [ - kotlin : '1.5.20', + kotlin : '1.5.21', build_gradle: '4.2.2', ] From a5598d3909760fab04dbf2783863c96019449fb9 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 18 Aug 2021 13:39:08 +0300 Subject: [PATCH 024/189] Updated on 2026-08-14 --- .../disclaimer/ui/DisclaimerFragment.kt | 5 ++++- .../drawable/bg_half_transparent_overlay.xml | 9 +++++++++ .../main/res/layout/fragment_disclaimer.xml | 19 +++++++++++++------ 3 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 app/src/main/res/drawable/bg_half_transparent_overlay.xml diff --git a/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt b/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt index fb7ebe023d..ec7a45dd3d 100644 --- a/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt @@ -58,6 +58,9 @@ class DisclaimerFragment : Fragment(R.layout.fragment_disclaimer), override fun newState(state: DisclaimerState) { if (activity == null) return - if (state.accepted) btn_accept.hide() + if (state.accepted) { + half_transparent_overlay.hide() + btn_accept.hide() + } } } \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_half_transparent_overlay.xml b/app/src/main/res/drawable/bg_half_transparent_overlay.xml new file mode 100644 index 0000000000..4b5937d793 --- /dev/null +++ b/app/src/main/res/drawable/bg_half_transparent_overlay.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_disclaimer.xml b/app/src/main/res/layout/fragment_disclaimer.xml index 0c771b9995..bec0ceace0 100644 --- a/app/src/main/res/layout/fragment_disclaimer.xml +++ b/app/src/main/res/layout/fragment_disclaimer.xml @@ -28,17 +28,15 @@ android:id="@+id/cl_details_confirm" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="33dp" app:layout_behavior="@string/appbar_scrolling_view_behavior"> + app:layout_constraintBottom_toBottomOf="parent"> @@ -62,18 +60,27 @@ app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintGuide_percent="0.37" /> + + + app:layout_constraintStart_toEndOf="@+id/guideline" + android:layout_marginBottom="24dp" /> From cf2e7407344cf972ab488bfe933be05b9167d3be Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 18 Aug 2021 13:39:26 +0300 Subject: [PATCH 025/189] Updated on 2026-08-14 --- app/src/main/assets/tangem-app-config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index 415eb6e9cc..0cf3af21e6 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit 415eb6e9ccfcdc3573391b869adeba433f790087 +Subproject commit 0cf3af21e6f441e6323a1ba127ee7518168797df From ec15770753999ceeee3200c2bb741669553aace4 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 24 Aug 2021 15:30:19 +0300 Subject: [PATCH 026/189] 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 e97b9251f0..94d37ff858 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -75,7 +75,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-36' + implementation 'com.tangem:blockchain:develop-38' implementation 'com.tangem.tangem-sdk-kotlin:core:develop-47' implementation 'com.tangem.tangem-sdk-kotlin:android:develop-47' From b3ae1d31acc23d7713798584eeed988293e652a7 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 26 Aug 2021 12:27:47 +0300 Subject: [PATCH 027/189] Updated on 2026-08-14 --- .../com/tangem/tap/common/redux/AppState.kt | 2 +- .../tap/features/send/BaseStoreFragment.kt | 16 +------ .../send/redux/middlewares/SendMiddleware.kt | 48 ++++++++++--------- .../tap/features/send/ui/SendFragment.kt | 20 ++++++++ 4 files changed, 47 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt index 02deb7d980..8762583a80 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt @@ -39,7 +39,7 @@ data class AppState( logMiddleware, navigationMiddleware, notificationsMiddleware, globalMiddleware, HomeMiddleware().homeMiddleware, WalletMiddleware().walletMiddleware, - sendMiddleware, + SendMiddleware().sendMiddleware, DetailsMiddleware().detailsMiddleware, DisclaimerMiddleware().disclaimerMiddleware, TokensMiddleware().tokensMiddleware, diff --git a/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt b/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt index d7b8d2af90..5ad9b1147a 100644 --- a/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt @@ -4,12 +4,9 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.activity.OnBackPressedCallback import androidx.fragment.app.Fragment import androidx.transition.TransitionInflater import com.google.android.material.snackbar.Snackbar -import com.tangem.tap.common.redux.navigation.NavigationAction -import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.store import com.tangem.wallet.R import kotlinx.android.synthetic.main.fragment_wallet.* @@ -27,18 +24,7 @@ abstract class BaseStoreFragment(layoutId: Int) : Fragment(layoutId) { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - val externalTransactionData = store.state.sendState.externalTransactionData - if (externalTransactionData == null) { - store.dispatch(NavigationAction.PopBackTo()) - } else { - store.dispatch( - WalletAction.TradeCryptoAction.FinishSelling(externalTransactionData.transactionId) - ) - } - } - }) + val inflater = TransitionInflater.from(requireContext()) enterTransition = inflater.inflateTransition(R.transition.slide_right) exitTransition = inflater.inflateTransition(R.transition.fade) 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 2c5b8ebd58..3b19952951 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 @@ -41,35 +41,37 @@ import java.util.* /** [REDACTED_AUTHOR] */ -val sendMiddleware: Middleware = { dispatch, appState -> - { nextDispatch -> - { action -> - when (action) { - is AddressPayIdActionUi -> AddressPayIdMiddleware().handle(action, appState(), dispatch) - is AmountActionUi -> AmountMiddleware().handle(action, appState(), dispatch) - is RequestFee -> RequestFeeMiddleware().handle(appState(), dispatch) - is SendActionUi.SendAmountToRecipient -> - verifyAndSendTransaction(action, appState(), dispatch) - is PrepareSendScreen -> setIfSendingToPayIdEnabled(appState(), dispatch) - is SendAction.Warnings.Update -> updateWarnings(dispatch) - is SendActionUi.CheckIfTransactionDataWasProvided -> { - val transactionData = appState()?.sendState?.externalTransactionData - if (transactionData != null) { - store.dispatchOnMain(AddressPayIdVerifyAction.AddressVerification.SetWalletAddress( - transactionData.destinationAddress, false - )) - store.dispatchOnMain(AmountActionUi.SetMainCurrency(MainCurrencyType.CRYPTO)) - store.dispatchOnMain(AmountActionUi.HandleUserInput(transactionData.amount)) - store.dispatchOnMain(AmountAction.SetAmount(transactionData.amount.toBigDecimal(), - false)) +class SendMiddleware { + val sendMiddleware: Middleware = { dispatch, appState -> + { nextDispatch -> + { action -> + when (action) { + is AddressPayIdActionUi -> AddressPayIdMiddleware().handle(action, appState(), dispatch) + is AmountActionUi -> AmountMiddleware().handle(action, appState(), dispatch) + is RequestFee -> RequestFeeMiddleware().handle(appState(), dispatch) + is SendActionUi.SendAmountToRecipient -> + verifyAndSendTransaction(action, appState(), dispatch) + is PrepareSendScreen -> setIfSendingToPayIdEnabled(appState(), dispatch) + is SendAction.Warnings.Update -> updateWarnings(dispatch) + is SendActionUi.CheckIfTransactionDataWasProvided -> { + val transactionData = appState()?.sendState?.externalTransactionData + if (transactionData != null) { + store.dispatchOnMain(AddressPayIdVerifyAction.AddressVerification.SetWalletAddress( + transactionData.destinationAddress, false + )) + store.dispatchOnMain(AmountActionUi.SetMainCurrency(MainCurrencyType.CRYPTO)) + store.dispatchOnMain(AmountActionUi.HandleUserInput(transactionData.amount)) + store.dispatchOnMain(AmountAction.SetAmount(transactionData.amount.toBigDecimal(), + false)) + } } } + nextDispatch(action) } - nextDispatch(action) } } -} +} private fun verifyAndSendTransaction( action: SendActionUi.SendAmountToRecipient, appState: AppState?, dispatch: (Action) -> Unit, ) { diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt index 9736cc16e0..bb675cf3fe 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt @@ -7,6 +7,7 @@ import android.text.method.DigitsKeyListener import android.view.View import android.view.inputmethod.EditorInfo import android.widget.EditText +import androidx.activity.OnBackPressedCallback import androidx.core.view.postDelayed import androidx.core.widget.addTextChangedListener import androidx.recyclerview.widget.LinearLayoutManager @@ -20,6 +21,7 @@ import com.tangem.tap.common.entities.TapCurrency import com.tangem.tap.common.extensions.getFromClipboard import com.tangem.tap.common.extensions.setOnImeActionListener import com.tangem.tap.common.qrCodeScan.ScanQrCodeActivity +import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.common.snackBar.MaxAmountSnackbar import com.tangem.tap.common.text.truncateMiddleWith import com.tangem.tap.common.toggleWidget.* @@ -31,6 +33,7 @@ import com.tangem.tap.features.send.redux.FeeActionUi.* import com.tangem.tap.features.send.redux.states.FeeType import com.tangem.tap.features.send.redux.states.MainCurrencyType import com.tangem.tap.features.send.ui.stateSubscribers.SendStateSubscriber +import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.features.wallet.ui.adapters.SpacesItemDecoration import com.tangem.tap.features.wallet.ui.adapters.WarningMessagesAdapter import com.tangem.tap.mainScope @@ -60,6 +63,23 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { private val sendSubscriber = SendStateSubscriber(this) private lateinit var keyboardObserver: KeyboardObserver + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + val externalTransactionData = store.state.sendState.externalTransactionData + if (externalTransactionData == null) { + store.dispatch(NavigationAction.PopBackTo()) + } else { + store.dispatch( + WalletAction.TradeCryptoAction.FinishSelling(externalTransactionData.transactionId) + ) + } + } + }) + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) From 613b68b9730222b24849906620f7943fb8b8ac2f Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 26 Aug 2021 15:55:13 +0300 Subject: [PATCH 028/189] Updated on 2026-08-14 --- .gitignore | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 13b2127afa..4cc00a7e1f 100644 --- a/.gitignore +++ b/.gitignore @@ -13,10 +13,7 @@ local.properties .gradle # User-specific configurations -.idea/caches/ -.idea/libraries/ -.idea/*.xml -.idea/codeStyles +.idea # LayoutInspector files /captures From a61f1c06669eaefd241c1e2ba879ee03561b0c5a Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 6 Sep 2021 16:58:56 +0300 Subject: [PATCH 029/189] Updated on 2026-08-14 --- app/build.gradle | 4 +- app/src/main/assets/tangem-app-config | 2 +- .../tangem/tap/common/extensions/Context.kt | 16 +++++ .../configurable/config/ConfigLoaders.kt | 9 +-- .../domain/tasks/CreateWalletAndRescanTask.kt | 2 +- .../tap/domain/tasks/CreateWalletsTask.kt | 2 +- .../domain/twins/CreateFirstTwinWalletTask.kt | 4 +- .../twins/CreateSecondTwinWalletTask.kt | 6 +- .../tap/domain/twins/TwinCardsManager.kt | 65 ++++++++++++++----- .../features/details/redux/DetailsAction.kt | 5 +- .../redux/twins/CreateTwinWalletMiddleware.kt | 2 +- .../ui/twins/CreateTwinWalletFragment.kt | 11 ++-- 12 files changed, 89 insertions(+), 39 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/common/extensions/Context.kt diff --git a/app/build.gradle b/app/build.gradle index 94d37ff858..e6cb6cd396 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -76,8 +76,8 @@ dependencies { coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' implementation 'com.tangem:blockchain:develop-38' - implementation 'com.tangem.tangem-sdk-kotlin:core:develop-47' - implementation 'com.tangem.tangem-sdk-kotlin:android:develop-47' + implementation 'com.tangem.tangem-sdk-kotlin:core:develop-70' + implementation 'com.tangem.tangem-sdk-kotlin:android:develop-70' // 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 415eb6e9cc..0cf3af21e6 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit 415eb6e9ccfcdc3573391b869adeba433f790087 +Subproject commit 0cf3af21e6f441e6323a1ba127ee7518168797df diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Context.kt b/app/src/main/java/com/tangem/tap/common/extensions/Context.kt new file mode 100644 index 0000000000..60345e1ab7 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/extensions/Context.kt @@ -0,0 +1,16 @@ +package com.tangem.tap.common.extensions + +import android.content.Context + +fun Context.readFile(fileName: String): String = + this.openFileInput(fileName).bufferedReader().readText() + +fun Context.rewriteFile(content: String, fileName: String) { + this.openFileOutput(fileName, Context.MODE_PRIVATE).use { + it.write(content.toByteArray(), 0, content.length) + } +} + +fun Context.readAssetAsString(fileName: String): String { + return this.assets.open("$fileName.json").bufferedReader().readText() +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigLoaders.kt b/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigLoaders.kt index a47e8fec1c..4c19bab889 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigLoaders.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigLoaders.kt @@ -6,6 +6,7 @@ import com.google.firebase.remoteconfig.ktx.remoteConfig import com.squareup.moshi.JsonAdapter import com.squareup.moshi.Moshi import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler +import com.tangem.tap.common.extensions.readAssetAsString import com.tangem.tap.domain.configurable.Loader import timber.log.Timber @@ -22,8 +23,8 @@ class FeaturesLocalLoader( val featureAdapter: JsonAdapter = moshi.adapter(FeatureModel::class.java) val valuesAdapter: JsonAdapter = moshi.adapter(ConfigValueModel::class.java) - val jsonFeatures = readAssetAsString(Loader.featuresName) - val jsonConfigValues = readAssetAsString(Loader.configValuesName) + val jsonFeatures = context.readAssetAsString(Loader.featuresName) + val jsonConfigValues = context.readAssetAsString(Loader.configValuesName) ConfigModel(featureAdapter.fromJson(jsonFeatures), valuesAdapter.fromJson(jsonConfigValues)) } catch (ex: Exception) { @@ -32,10 +33,6 @@ class FeaturesLocalLoader( } onComplete(config) } - - private fun readAssetAsString(fileName: String): String { - return context.assets.open("$fileName.json").bufferedReader().readText() - } } class FeaturesRemoteLoader( diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt index ee2cda0d50..c75a37a709 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt @@ -21,7 +21,7 @@ class CreateWalletAndRescanTask : CardSessionRunnable { val firmwareVersion = card.firmwareVersion val task = if (firmwareVersion < FirmwareVersion.MultiWalletAvailable) { - CreateWalletCommand(card.supportedCurves.first(), false) + CreateWalletCommand(card.supportedCurves.first()) } else { CreateWalletsTask() } diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt index 1443371141..f1afec8e00 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt @@ -33,7 +33,7 @@ class CreateWalletsTask(curves: List = emptyList()) : CardSession callback: (result: CompletionResult) -> Unit ) { - CreateWalletCommand(curve, false).run(session) { result -> + CreateWalletCommand(curve).run(session) { result -> when (result) { is CompletionResult.Success -> { if (index == curves.lastIndex) { diff --git a/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt index 0ca3f113c3..400ee19ae3 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt @@ -20,14 +20,14 @@ class CreateFirstTwinWalletTask : CardSessionRunnable { when (response) { is CompletionResult.Success -> { session.environment.card = session.environment.card?.setWallets(emptyList()) - CreateWalletCommand(EllipticCurve.Secp256k1, false) + CreateWalletCommand(EllipticCurve.Secp256k1) .run(session) { callback(it) } } is CompletionResult.Failure -> callback(CompletionResult.Failure(response.error)) } } } else { - CreateWalletCommand(EllipticCurve.Secp256k1, false) + CreateWalletCommand(EllipticCurve.Secp256k1) .run(session) { callback(it) } } } diff --git a/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt index 1c0af37da4..826e57090b 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt @@ -2,6 +2,7 @@ package com.tangem.tap.domain.twins import com.tangem.Message import com.tangem.common.CompletionResult +import com.tangem.common.KeyPair import com.tangem.common.card.EllipticCurve import com.tangem.common.core.CardSession import com.tangem.common.core.CardSessionRunnable @@ -13,6 +14,7 @@ import com.tangem.tap.domain.extensions.getSingleWallet class CreateSecondTwinWalletTask( private val firstPublicKey: String, + private val issuerKeys: KeyPair, private val preparingMessage: Message, private val creatingWalletMessage: Message ) : CardSessionRunnable { @@ -36,13 +38,13 @@ class CreateSecondTwinWalletTask( private fun finishTask(session: CardSession, callback: (result: CompletionResult) -> Unit) { session.setInitialMessage(creatingWalletMessage) - CreateWalletCommand(EllipticCurve.Secp256k1, false).run(session) { result -> + CreateWalletCommand(EllipticCurve.Secp256k1).run(session) { result -> when (result) { is CompletionResult.Success -> { session.environment.card = session.environment.card?.updateWallet(result.data.wallet) WriteProtectedIssuerDataTask( - firstPublicKey.hexToBytes(), TwinCardsManager.issuerKeys + firstPublicKey.hexToBytes(), issuerKeys ).run(session) { writeResult -> when (writeResult) { is CompletionResult.Success -> callback(result) 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 0282a18d65..8f742b5852 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 @@ -1,5 +1,8 @@ package com.tangem.tap.domain.twins +import android.content.Context +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.Types import com.tangem.Message import com.tangem.blockchain.extensions.Result import com.tangem.blockchain.extensions.SimpleResult @@ -10,10 +13,12 @@ import com.tangem.common.extensions.hexToBytes import com.tangem.common.extensions.toHexString import com.tangem.crypto.CryptoUtils import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler +import com.tangem.tap.common.extensions.readAssetAsString import com.tangem.tap.domain.tasks.ScanNoteResponse +import com.tangem.tap.network.createMoshi import com.tangem.tap.tangemSdkManager -class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse) { +class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse, context: Context) { private val currentCardId: String = scanNoteResponse.card.cardId private val secondCardId: String? = TwinsHelper.getTwinsCardId(currentCardId) @@ -21,9 +26,13 @@ class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse) { private var currentCardPublicKey: String? = null private var secondCardPublicKey: String? = null + private val issuerKeyPair: KeyPair = getIssuerKeys( + context, scanNoteResponse.card.issuer.publicKey.toHexString() + ) + suspend fun createFirstWallet(message: Message): SimpleResult { val response = tangemSdkManager.runTaskAsync( - CreateFirstTwinWalletTask(), currentCardId, message + CreateFirstTwinWalletTask(), currentCardId, message ) when (response) { is CompletionResult.Success -> { @@ -46,13 +55,17 @@ class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse) { suspend fun createSecondWallet( - initialMessage: Message, - preparingMessage: Message, - creatingWalletMessage: Message + initialMessage: Message, + preparingMessage: Message, + creatingWalletMessage: Message, ): SimpleResult { val response = tangemSdkManager.runTaskAsync( - CreateSecondTwinWalletTask(currentCardPublicKey!!, preparingMessage, creatingWalletMessage), - secondCardId, initialMessage + CreateSecondTwinWalletTask( + firstPublicKey = currentCardPublicKey!!, + issuerKeys = issuerKeyPair, + preparingMessage = preparingMessage, + creatingWalletMessage = creatingWalletMessage), + secondCardId, initialMessage ) when (response) { is CompletionResult.Success -> { @@ -75,8 +88,8 @@ class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse) { suspend fun complete(message: Message): Result { val response = tangemSdkManager.runTaskAsync( - FinalizeTwinTask(secondCardPublicKey!!.hexToBytes(), issuerKeys), - currentCardId, message + FinalizeTwinTask(secondCardPublicKey!!.hexToBytes(), issuerKeyPair), + currentCardId, message ) return when (response) { is CompletionResult.Success -> Result.Success(response.data) @@ -94,13 +107,6 @@ class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse) { } companion object { - val issuerKeys = KeyPair( - privateKey = "F9F4C50636C9E6FC65F92655BD5C21C85A5F6A34DCD0F1E75FCEA1980FE242F5".hexToBytes(), - publicKey = ("048196AA4B410AC44A3B9CCE18E7BE226AEA070ACC83A9CF67540F" + - "AC49AF25129F6A538A28AD6341358E3C4F9963064F" + - "7E365372A651D374E5C23CDD37FD099BF2").hexToBytes() - ) - fun verifyTwinPublicKey(issuerData: ByteArray, cardWalletPublicKey: ByteArray?): Boolean { if (issuerData.size < 65) return false val publicKey = issuerData.sliceArray(0 until 65) @@ -108,5 +114,30 @@ class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse) { return (cardWalletPublicKey != null && CryptoUtils.verify(cardWalletPublicKey, publicKey, signedKey)) } + + private fun getIssuerKeys(context: Context, publicKey: String): KeyPair { + val issuer = getIssuers(context).first { it.publicKey == publicKey } + return KeyPair( + publicKey = issuer.publicKey.hexToBytes(), + privateKey = issuer.privateKey.hexToBytes() + ) + } + + private fun getAdapter(): JsonAdapter> { + return createMoshi().adapter( + Types.newParameterizedType(List::class.java, Issuer::class.java) + ) + } + + private fun getIssuers(context: Context): List { + val file = context.readAssetAsString("tangem-app-config/issuers") + return getAdapter().fromJson(file)!! + } } -} \ No newline at end of file +} + +private class Issuer( + val id: String, + val privateKey: String, + val publicKey: String, +) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt index 79067f026e..4d3882a2a7 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt @@ -1,5 +1,6 @@ package com.tangem.tap.features.details.redux +import android.content.Context import com.tangem.Message import com.tangem.blockchain.common.Wallet import com.tangem.common.card.Card @@ -63,7 +64,9 @@ sealed class DetailsAction : Action { object Confirm : CreateTwinWalletAction() } - data class LaunchFirstStep(val message: Message) : CreateTwinWalletAction() { + data class LaunchFirstStep( + val message: Message, val context: Context + ) : CreateTwinWalletAction() { object Success : CreateTwinWalletAction() object Failure : CreateTwinWalletAction() } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt index e99f7c7ad6..fa81ea9a18 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt @@ -49,7 +49,7 @@ class CreateTwinWalletMiddleware { } is DetailsAction.CreateTwinWalletAction.LaunchFirstStep -> { store.state.globalState.scanNoteResponse?.let { - twinsManager = TwinCardsManager(it) + twinsManager = TwinCardsManager(it, action.context) } scope.launch { val result = twinsManager?.createFirstWallet(action.message) diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/twins/CreateTwinWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/twins/CreateTwinWalletFragment.kt index 50d87de052..f8a09c7131 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/twins/CreateTwinWalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/twins/CreateTwinWalletFragment.kt @@ -97,11 +97,12 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_details_twin_cards), v_step_3.setBackgroundColor(defaultColor) btn_tap.setOnClickListener { store.dispatch(DetailsAction.CreateTwinWalletAction.LaunchFirstStep( - Message(getString( - R.string.details_twins_recreate_title_format, - twinCardNumberString - ) - ))) + Message(getString( + R.string.details_twins_recreate_title_format, + twinCardNumberString + )), + requireContext() + )) } btn_tap.text = getString(R.string.details_twins_recreate_button_format, twinCardNumber.number.toString()) From ff633ff5bc82c2141d7958b92e10c2d6b2e9e06d Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 6 Sep 2021 18:40:35 +0300 Subject: [PATCH 030/189] Updated on 2026-08-14 --- app/src/main/res/layout/item_currency_wallet.xml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/layout/item_currency_wallet.xml b/app/src/main/res/layout/item_currency_wallet.xml index ce49ed06df..3b74abdffd 100644 --- a/app/src/main/res/layout/item_currency_wallet.xml +++ b/app/src/main/res/layout/item_currency_wallet.xml @@ -56,7 +56,7 @@ + + From 935ae544e9a2cd613476706febdf7cec9b837fb4 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 9 Sep 2021 21:25:26 +0300 Subject: [PATCH 031/189] Updated on 2026-08-14 --- .../main/java/com/tangem/tap/common/extensions/Navigation.kt | 1 + .../features/wallet/ui/{dialogs => }/TwinsOnboardingFragment.kt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) rename app/src/main/java/com/tangem/tap/features/wallet/ui/{dialogs => }/TwinsOnboardingFragment.kt (98%) diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt index d56ae0af85..77b292ac22 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt @@ -15,6 +15,7 @@ import com.tangem.tap.features.disclaimer.ui.DisclaimerFragment import com.tangem.tap.features.home.HomeFragment import com.tangem.tap.features.send.ui.SendFragment import com.tangem.tap.features.tokens.ui.AddTokensFragment +import com.tangem.tap.features.wallet.ui.TwinsOnboardingFragment import com.tangem.tap.features.wallet.ui.WalletDetailsFragment import com.tangem.tap.features.wallet.ui.WalletFragment import com.tangem.tap.features.wallet.ui.dialogs.TwinsOnboardingFragment diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/TwinsOnboardingFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/TwinsOnboardingFragment.kt similarity index 98% rename from app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/TwinsOnboardingFragment.kt rename to app/src/main/java/com/tangem/tap/features/wallet/ui/TwinsOnboardingFragment.kt index a20b06aa72..0d3abe02e2 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/TwinsOnboardingFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/TwinsOnboardingFragment.kt @@ -1,4 +1,4 @@ -package com.tangem.tap.features.wallet.ui.dialogs +package com.tangem.tap.features.wallet.ui import android.os.Bundle import android.view.View From 82dba6811abbf9611fbf2cd6c6ccf393bcc1d688 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 9 Sep 2021 21:30:58 +0300 Subject: [PATCH 032/189] Updated on 2026-08-14 --- dependencies.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index e01cbb450b..9d12481f3f 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -1,4 +1,4 @@ ext.versions = [ - kotlin : '1.5.21', - build_gradle: '7.0.0', + kotlin : '1.5.30', + build_gradle: '7.0.1', ] From 7e39873b235ecc124f5fa38507eca2b25d093ed6 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 9 Sep 2021 21:34:14 +0300 Subject: [PATCH 033/189] Updated on 2026-08-14 --- .../java/com/tangem/tap/common/Handler.kt | 26 ++++++++++++++++ .../com/tangem/tap/common/extensions/Toast.kt | 31 +++++++++++++++++++ .../com/tangem/tap/common/extensions/UI.kt | 21 +++++++++++-- 3 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/common/Handler.kt create mode 100644 app/src/main/java/com/tangem/tap/common/extensions/Toast.kt diff --git a/app/src/main/java/com/tangem/tap/common/Handler.kt b/app/src/main/java/com/tangem/tap/common/Handler.kt new file mode 100644 index 0000000000..f36d4d1541 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/Handler.kt @@ -0,0 +1,26 @@ +package com.tangem.tap.common + +import android.os.Handler +import android.os.HandlerThread +import android.os.Looper + +private val uiHandler = Handler(Looper.getMainLooper()) +private val backgroundHandler = Handler(HandlerThread("AppMainHandlerThread").apply { start() }.looper) + +fun postUi(ms: Long = 0, func: Runnable) { + if (ms == 0L) uiHandler.post { func.run() } else uiHandler.postDelayed(func, ms) +} + +fun postBackground(ms: Long = 0, func: Runnable) { + if (ms == 0L) backgroundHandler.post { func.run() } else backgroundHandler.postDelayed(func, ms) +} + +fun post(ms: Long = 0, func: Runnable) { + if (ms == 0L) { + func.run() + } else { + val currentLooper = Looper.myLooper() ?: return + + Handler(currentLooper).postDelayed(func, ms) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Toast.kt b/app/src/main/java/com/tangem/tap/common/extensions/Toast.kt new file mode 100644 index 0000000000..23d969af40 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/extensions/Toast.kt @@ -0,0 +1,31 @@ +package com.tangem.tap.common.extensions + +import android.content.Context +import android.view.View +import android.widget.Toast +import androidx.annotation.StringRes +import androidx.fragment.app.Fragment + +fun Context.toast(message: String, length: Int = Toast.LENGTH_LONG) { + Toast.makeText(this, message, length).show() +} + +fun Context.toast(@StringRes messageRes: Int, length: Int = Toast.LENGTH_LONG) { + Toast.makeText(this, this.getString(messageRes), length).show() +} + +fun View.toast(message: String, length: Int = Toast.LENGTH_LONG) { + context.toast(message, length) +} + +fun View.toast(@StringRes messageRes: Int, length: Int = Toast.LENGTH_LONG) { + context.toast(context.getString(messageRes), length) +} + +fun Fragment.toast(message: String, length: Int = Toast.LENGTH_LONG) { + context?.toast(message, length) +} + +fun Fragment.toast(@StringRes messageRes: Int, length: Int = Toast.LENGTH_LONG) { + context?.let { it.toast(it.getString(messageRes), length) } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/UI.kt b/app/src/main/java/com/tangem/tap/common/extensions/UI.kt index d29b032ef3..e468ec2203 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/UI.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/UI.kt @@ -5,6 +5,7 @@ import android.content.* import android.content.res.Resources import android.graphics.drawable.Drawable import android.os.Build +import android.os.Bundle import android.util.TypedValue import android.view.View import android.view.ViewGroup @@ -16,6 +17,7 @@ import androidx.annotation.StringRes import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import com.google.android.material.card.MaterialCardView +import com.tangem.common.extensions.VoidCallback fun Fragment.getDrawable(@DrawableRes drawableResId: Int): Drawable? { return ContextCompat.getDrawable(requireContext(), drawableResId) @@ -76,8 +78,8 @@ fun Context.dpToPixels(dp: Int): Int = TypedValue.COMPLEX_UNIT_DIP, dp.toFloat(), this.resources.displayMetrics ).toInt() -tailrec fun Context?.getActivity(): Activity? = this as? Activity ?: - (this as? ContextWrapper)?.baseContext?.getActivity() +tailrec fun Context?.getActivity(): Activity? = this as? Activity + ?: (this as? ContextWrapper)?.baseContext?.getActivity() fun MaterialCardView.setMargins( marginLeftDp: Int = 16, @@ -141,4 +143,19 @@ fun Context.shareText(text: String) { fun Fragment.shareText(text: String) { requireContext().shareText(text) +} + +fun Context.safeStartActivity( + intent: Intent, + options: Bundle? = null, + fallback: ((ActivityNotFoundException) -> Unit)? = null, + finally: VoidCallback? = null +) { + try { + this.startActivity(intent, options) + } catch (ex: ActivityNotFoundException) { + fallback?.invoke(ex) + } finally { + finally?.invoke() + } } \ No newline at end of file From 35ac528768c01dd5808b13ed37ec6d98781e7a86 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 9 Sep 2021 21:37:07 +0300 Subject: [PATCH 034/189] Updated on 2026-08-14 --- .../com/tangem/tap/common/entities/Button.kt | 16 +++++++++++++++- .../tap/features/send/redux/SendScreenAction.kt | 4 ++-- .../send/redux/middlewares/SendMiddleware.kt | 6 +++--- .../send/redux/reducers/SendScreenReducer.kt | 5 ++++- .../tap/features/send/redux/states/SendState.kt | 7 ++++--- .../ui/stateSubscribers/SendStateSubscriber.kt | 16 ++-------------- 6 files changed, 30 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/common/entities/Button.kt b/app/src/main/java/com/tangem/tap/common/entities/Button.kt index 986a170564..3255e490ca 100644 --- a/app/src/main/java/com/tangem/tap/common/entities/Button.kt +++ b/app/src/main/java/com/tangem/tap/common/entities/Button.kt @@ -1,3 +1,17 @@ package com.tangem.tap.common.entities -open class Button(val enabled: Boolean) \ No newline at end of file +import com.tangem.tap.common.toggleWidget.ProgressState +import com.tangem.tap.features.send.redux.states.ButtonState + +open class Button(val enabled: Boolean) + +open class IndeterminateProgressButton( + val state: ButtonState +) : Button(state != ButtonState.DISABLED) { + + val progressState: ProgressState + get() = when (state) { + ButtonState.PROGRESS -> ProgressState.Progress() + else -> ProgressState.None + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt index 921e29f667..6f20155539 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt @@ -11,7 +11,7 @@ import com.tangem.tap.domain.TapError import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.features.send.redux.states.FeeType import com.tangem.tap.features.send.redux.states.MainCurrencyType -import com.tangem.tap.features.send.redux.states.SendButtonState +import com.tangem.tap.features.send.redux.states.ButtonState import com.tangem.wallet.R import org.rekotlin.Action import java.math.BigDecimal @@ -137,7 +137,7 @@ sealed class SendActionUi : SendScreenActionUi { sealed class SendAction : SendScreenAction { - data class ChangeSendButtonState(val state: SendButtonState) : SendAction() + data class ChangeSendButtonState(val state: ButtonState) : SendAction() object SendSuccess : SendAction(), ToastNotificationAction { override val messageResource: Int = R.string.send_transaction_success } 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 3b19952951..c6fb811fba 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 @@ -23,7 +23,7 @@ import com.tangem.tap.features.send.redux.* import com.tangem.tap.features.send.redux.FeeAction.RequestFee import com.tangem.tap.features.send.redux.states.ExternalTransactionData import com.tangem.tap.features.send.redux.states.MainCurrencyType -import com.tangem.tap.features.send.redux.states.SendButtonState +import com.tangem.tap.features.send.redux.states.ButtonState import com.tangem.tap.features.send.redux.states.TransactionExtrasState import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.scope @@ -121,7 +121,7 @@ private fun sendTransaction( externalTransactionData: ExternalTransactionData?, dispatch: (Action) -> Unit, ) { - dispatch(SendAction.ChangeSendButtonState(SendButtonState.PROGRESS)) + dispatch(SendAction.ChangeSendButtonState(ButtonState.PROGRESS)) var txData = walletManager.createTransaction(amountToSend, feeAmount, destinationAddress) transactionExtras.xlmMemo?.memo?.let { txData = txData.copy(extras = StellarTransactionExtras(it)) } @@ -236,7 +236,7 @@ private fun sendTransaction( } } } - dispatch(SendAction.ChangeSendButtonState(SendButtonState.ENABLED)) + dispatch(SendAction.ChangeSendButtonState(ButtonState.ENABLED)) } } } diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt index 58b36afe00..673f19cb66 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt @@ -2,6 +2,7 @@ package com.tangem.tap.features.send.redux.reducers import com.tangem.blockchain.common.AmountType import com.tangem.tap.common.CurrencyConverter +import com.tangem.tap.common.entities.IndeterminateProgressButton import com.tangem.tap.features.send.redux.* import com.tangem.tap.features.send.redux.states.ExternalTransactionData import com.tangem.tap.features.send.redux.states.IdStateHolder @@ -41,7 +42,9 @@ class SendScreenReducer { private class SendReducer : SendInternalReducer { override fun handle(action: SendScreenAction, sendState: SendState): SendState { val result = when (action) { - is SendAction.ChangeSendButtonState -> sendState.copy(sendButtonState = action.state) + is SendAction.ChangeSendButtonState -> { + sendState.copy(sendButtonState = IndeterminateProgressButton(action.state)) + } is SendAction.Dialog.TezosWarningDialog -> sendState.copy(dialog = action) is SendAction.Dialog.SendTransactionFails -> sendState.copy(dialog = action) is SendAction.Dialog.Hide -> sendState.copy(dialog = null) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt index eed6fa793c..3f3d32df5e 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt @@ -5,6 +5,7 @@ import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.WalletManager import com.tangem.common.extensions.isZero import com.tangem.tap.common.CurrencyConverter +import com.tangem.tap.common.entities.IndeterminateProgressButton import com.tangem.tap.common.entities.TapCurrency import com.tangem.tap.common.redux.global.StateDialog import com.tangem.tap.common.text.DecimalDigitsInputFilter @@ -39,7 +40,7 @@ data class SendState( val feeState: FeeState = FeeState(), val receiptState: ReceiptState = ReceiptState(), val sendWarningsList: List = listOf(), - val sendButtonState: SendButtonState = SendButtonState.DISABLED, + val sendButtonState: IndeterminateProgressButton = IndeterminateProgressButton(ButtonState.DISABLED), val dialog: StateDialog? = null, val externalTransactionData: ExternalTransactionData? = null ) : SendScreenState { @@ -87,7 +88,7 @@ data class SendState( } } - fun getButtonState(): SendButtonState = if (isReadyToSend()) SendButtonState.ENABLED else SendButtonState.DISABLED + fun getButtonState(): ButtonState = if (isReadyToSend()) ButtonState.ENABLED else ButtonState.DISABLED fun getTotalAmountToSend(value: BigDecimal = amountState.amountToSendCrypto): BigDecimal { val needToExtractFee = amountState.isCoinAmount() && feeState.feeIsIncluded @@ -117,7 +118,7 @@ data class SendState( } } -enum class SendButtonState { +enum class ButtonState { ENABLED, DISABLED, PROGRESS } diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt index 05c7af7d18..39b4eefe8f 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt @@ -116,20 +116,8 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber } } - when (state.sendButtonState) { - SendButtonState.ENABLED -> { - fg.btnSend.isEnabled = true - sendFragment.sendBtn.changeState(ProgressState.None) - } - SendButtonState.DISABLED -> { - fg.btnSend.isEnabled = false - sendFragment.sendBtn.changeState(ProgressState.None) - } - SendButtonState.PROGRESS -> { - fg.btnSend.isEnabled = true - sendFragment.sendBtn.changeState(ProgressState.Progress()) - } - } + sendFragment.sendBtn.changeState(state.sendButtonState.progressState) + sendFragment.sendBtn.mainView.isEnabled = state.sendButtonState.enabled val rv = fg.rv_warning_messages val adapter = rv.adapter as? WarningMessagesAdapter ?: return From c938b9fbe3d5533536ffe71536bb53a0b23dc8a1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 9 Sep 2021 21:41:28 +0300 Subject: [PATCH 035/189] Updated on 2026-08-14 --- .../tap/common/extensions/Navigation.kt | 33 ++++++++++++++++--- .../redux/navigation/NavigationAction.kt | 19 +++++++++-- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt index 77b292ac22..b3657a070c 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt @@ -1,9 +1,12 @@ package com.tangem.tap.common.extensions +import androidx.activity.OnBackPressedCallback import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentManager +import com.tangem.common.extensions.VoidCallback import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.common.redux.navigation.FragmentShareTransition import com.tangem.tap.features.details.ui.DetailsConfirmFragment import com.tangem.tap.features.details.ui.DetailsFragment import com.tangem.tap.features.details.ui.DetailsSecurityFragment @@ -18,13 +21,26 @@ import com.tangem.tap.features.tokens.ui.AddTokensFragment import com.tangem.tap.features.wallet.ui.TwinsOnboardingFragment import com.tangem.tap.features.wallet.ui.WalletDetailsFragment import com.tangem.tap.features.wallet.ui.WalletFragment -import com.tangem.tap.features.wallet.ui.dialogs.TwinsOnboardingFragment import com.tangem.wallet.R -fun FragmentActivity.openFragment(screen: AppScreen, addToBackStack: Boolean = true) { +fun FragmentActivity.openFragment( + screen: AppScreen, + addToBackstack: Boolean, + fgShareTransition: FragmentShareTransition? = null +) { val transaction = this.supportFragmentManager.beginTransaction() - transaction.replace(R.id.fragment_container, fragmentFactory(screen), screen.name) - if (addToBackStack && screen != AppScreen.Home) transaction.addToBackStack(null) + val fragment = fragmentFactory(screen) + fgShareTransition?.apply { + fragment.sharedElementEnterTransition = enterTransitionSet + fragment.sharedElementReturnTransition = exitTransitionSet + shareElements.forEach { shareElement -> + shareElement.wView.get()?.let { view -> + transaction.addSharedElement(view, shareElement.name) + } + } + } + transaction.replace(R.id.fragment_container, fragment, screen.name) + if (addToBackstack && screen != AppScreen.Home) transaction.addToBackStack(null) transaction.commitAllowingStateLoss() } @@ -39,6 +55,15 @@ fun FragmentActivity.getPreviousScreen(): AppScreen? { return tag?.let { AppScreen.valueOf(tag) } } +fun FragmentActivity.addOnBackPressedDispatcher( + isEnabled: Boolean = true, + onBackPressed: VoidCallback +): OnBackPressedCallback = (object : OnBackPressedCallback(isEnabled) { + override fun handleOnBackPressed() { + onBackPressed() + } +}).also { this.onBackPressedDispatcher.addCallback(it) } + private fun fragmentFactory(screen: AppScreen): Fragment { return when (screen) { AppScreen.Home -> HomeFragment() diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt index 6da57dfec8..9a4794b29d 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt @@ -1,12 +1,17 @@ package com.tangem.tap.common.redux.navigation +import android.view.View import androidx.fragment.app.FragmentActivity +import androidx.transition.TransitionSet import org.rekotlin.Action import java.lang.ref.WeakReference sealed class NavigationAction : Action { - data class NavigateTo(val screen: AppScreen, val addToBackstack: Boolean = true) : - NavigationAction() + data class NavigateTo( + val screen: AppScreen, + val addToBackstack: Boolean = true, + val fragmentShareTransition: FragmentShareTransition? = null + ) : NavigationAction() data class PopBackTo(val screen: AppScreen? = null) : NavigationAction() @@ -14,4 +19,12 @@ sealed class NavigationAction : Action { data class ActivityCreated(val activity: WeakReference) : NavigationAction() object ActivityDestroyed : NavigationAction() -} \ No newline at end of file +} + +data class FragmentShareTransition( + val shareElements: List, + val enterTransitionSet: TransitionSet, + val exitTransitionSet: TransitionSet +) + +data class ShareElement(val wView: WeakReference, val name: String) \ No newline at end of file From 3545b4574362a66eb813e706d0c861bff60f4782 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 9 Sep 2021 21:45:12 +0300 Subject: [PATCH 036/189] Updated on 2026-08-14 --- .../tap/common/redux/navigation/NavigationMiddleware.kt | 6 +++++- .../toggleWidget/IndeterminateProgressButtonWidget.kt | 2 ++ .../com/tangem/tap/common/toggleWidget/ViewStateWidget.kt | 3 +++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt index 43c8ed406e..31fe730615 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt @@ -13,7 +13,11 @@ val navigationMiddleware: Middleware = { dispatch, state -> val navState = state()?.navigationState when (action) { is NavigationAction.NavigateTo -> { - navState?.activity?.get()?.openFragment(action.screen, action.addToBackstack) + navState?.activity?.get()?.openFragment( + action.screen, + action.addToBackstack, + action.fragmentShareTransition + ) } is NavigationAction.PopBackTo -> { if (action.screen == AppScreen.Home) { diff --git a/app/src/main/java/com/tangem/tap/common/toggleWidget/IndeterminateProgressButtonWidget.kt b/app/src/main/java/com/tangem/tap/common/toggleWidget/IndeterminateProgressButtonWidget.kt index c6cb5f22bd..153f7c662d 100644 --- a/app/src/main/java/com/tangem/tap/common/toggleWidget/IndeterminateProgressButtonWidget.kt +++ b/app/src/main/java/com/tangem/tap/common/toggleWidget/IndeterminateProgressButtonWidget.kt @@ -23,6 +23,8 @@ open class IndeterminateProgressButtonWidget( changeState(initialState) } + override val mainView: View = button + override fun changeState(state: WidgetState) { val progressState = state as? ProgressState ?: return diff --git a/app/src/main/java/com/tangem/tap/common/toggleWidget/ViewStateWidget.kt b/app/src/main/java/com/tangem/tap/common/toggleWidget/ViewStateWidget.kt index 246268de7a..0c06410ace 100644 --- a/app/src/main/java/com/tangem/tap/common/toggleWidget/ViewStateWidget.kt +++ b/app/src/main/java/com/tangem/tap/common/toggleWidget/ViewStateWidget.kt @@ -1,5 +1,7 @@ package com.tangem.tap.common.toggleWidget +import android.view.View + /** [REDACTED_AUTHOR] @@ -7,6 +9,7 @@ package com.tangem.tap.common.toggleWidget interface WidgetState interface ViewStateWidget { + val mainView: View fun changeState(state: WidgetState) } From 91493bc01bb466d370484121dc946079d3a49bae Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 9 Sep 2021 21:52:52 +0300 Subject: [PATCH 037/189] Updated on 2026-08-14 --- .../com/tangem/tap/domain/extensions/Card.kt | 2 ++ .../tap/domain/tasks/CreateWalletsTask.kt | 18 +++++++----------- .../tangem/tap/domain/tasks/ScanNoteTask.kt | 13 +++++-------- .../tap/domain/twins/TwinCardsManager.kt | 6 +++--- .../tap/features/send/BaseStoreFragment.kt | 2 +- 5 files changed, 18 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt b/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt index be6eb20dcb..aeef948edf 100644 --- a/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt +++ b/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt @@ -7,6 +7,8 @@ fun Card.getSingleWallet(): CardWallet? { return wallets.firstOrNull() } +fun Card.hasWallets(): Boolean = wallets.isNotEmpty() + fun Card.hasSignedHashes(): Boolean { return wallets.any { it.totalSignedHashes ?: 0 > 0 } } diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt index 1443371141..e185b62b4c 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt @@ -9,19 +9,15 @@ import com.tangem.operations.PreflightReadMode import com.tangem.operations.PreflightReadTask import com.tangem.operations.wallet.CreateWalletCommand -class CreateWalletsTask(curves: List = emptyList()) : CardSessionRunnable { +class CreateWalletsTask(curves: List? = null) : CardSessionRunnable { - private val curves = if (curves.isEmpty()) { - listOf( - EllipticCurve.Secp256k1, - EllipticCurve.Ed25519, - EllipticCurve.Secp256r1, - ) - } else { - curves - } + private val curves = curves ?: listOf( + EllipticCurve.Secp256k1, + EllipticCurve.Ed25519, + EllipticCurve.Secp256r1, + ) - var index = 0 + private var index = 0 override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { val curve = curves[index] diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt index ca90ead2c5..9f31169566 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt @@ -7,7 +7,7 @@ import com.tangem.blockchain.common.Token import com.tangem.blockchain.common.WalletManagerFactory import com.tangem.common.CompletionResult import com.tangem.common.card.Card -import com.tangem.common.card.EllipticCurve +import com.tangem.common.card.FirmwareVersion import com.tangem.common.card.WalletData import com.tangem.common.core.CardSession import com.tangem.common.core.CardSessionRunnable @@ -18,13 +18,11 @@ import com.tangem.operations.ScanTask import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand import com.tangem.tap.domain.TapSdkError import com.tangem.tap.domain.TapWorkarounds.isExcluded -import com.tangem.tap.domain.TapWorkarounds.isMultiCurrencyWallet import com.tangem.tap.domain.TapWorkarounds.noteCurrency import com.tangem.tap.domain.extensions.getSingleWallet import com.tangem.tap.domain.twins.TwinCardsManager import com.tangem.tap.domain.twins.isTwinCard import com.tangem.tap.store -import com.tangem.wallet.R data class ScanNoteResponse( val card: Card, @@ -71,7 +69,7 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable= 4) { + } else if (card.firmwareVersion >= FirmwareVersion.MultiWalletAvailable) { createMissingWalletsIfNeeded(card, session, callback) } else { callback(CompletionResult.Success( @@ -88,14 +86,13 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable Date: Thu, 23 Sep 2021 10:49:04 +0300 Subject: [PATCH 038/189] Updated on 2026-08-14 --- dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index 9d12481f3f..43b263845b 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -1,4 +1,4 @@ ext.versions = [ kotlin : '1.5.30', - build_gradle: '7.0.1', + build_gradle: '7.0.2', ] From 5eb8b71a1f3526c1b36836688d590e1a177d46a6 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 23 Sep 2021 11:28:43 +0300 Subject: [PATCH 039/189] Updated on 2026-08-14 --- app/build.gradle | 4 +- .../tap/common/extensions/Collections.kt | 7 ++ .../common/redux/NotificationsMiddleware.kt | 45 +++++--- .../com/tangem/tap/domain/TangemSdkManager.kt | 22 ++-- .../tangem/tap/domain/TapBlockchainConfig.kt | 13 --- .../java/com/tangem/tap/domain/TapErrors.kt | 5 +- .../com/tangem/tap/domain/TapWalletManager.kt | 10 +- .../com/tangem/tap/domain/TapWorkarounds.kt | 46 +++----- .../com/tangem/tap/domain/UrlBitmapLoader.kt | 38 +++++++ .../com/tangem/tap/domain/extensions/Card.kt | 18 +++ .../domain/extensions/WalletManagerFactory.kt | 10 +- .../tangem/tap/domain/tasks/ScanNoteTask.kt | 33 ++---- .../tangem/tap/domain/twins/TwinsHelper.kt | 2 +- .../features/details/redux/DetailsReducer.kt | 14 +-- .../features/details/ui/DetailsFragment.kt | 8 +- .../wallet/models/PendingTransaction.kt | 9 ++ .../redux/middlewares/WarningsMiddleware.kt | 12 +- .../wallet/redux/reducers/WalletReducer.kt | 26 +---- .../tap/persistence/PreferencesStorage.kt | 21 ++-- .../tap/persistence/UsedCardsPrefStorage.kt | 107 ++++++++++++++++++ .../main/res/values/strings_untranslated.xml | 26 +++++ 21 files changed, 316 insertions(+), 160 deletions(-) delete mode 100644 app/src/main/java/com/tangem/tap/domain/TapBlockchainConfig.kt create mode 100644 app/src/main/java/com/tangem/tap/domain/UrlBitmapLoader.kt create mode 100644 app/src/main/java/com/tangem/tap/persistence/UsedCardsPrefStorage.kt diff --git a/app/build.gradle b/app/build.gradle index a1a8cda618..923c2c5326 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,8 +77,8 @@ dependencies { coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' implementation 'com.tangem:blockchain:develop-38' - implementation 'com.tangem.tangem-sdk-kotlin:core:develop-70' - implementation 'com.tangem.tangem-sdk-kotlin:android:develop-70' + implementation 'com.tangem.tangem-sdk-kotlin:core:develop-78' + implementation 'com.tangem.tangem-sdk-kotlin:android:develop-78' // WebView implementation "androidx.browser:browser:1.3.0" diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Collections.kt b/app/src/main/java/com/tangem/tap/common/extensions/Collections.kt index d1c417e842..b264485c44 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Collections.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Collections.kt @@ -14,4 +14,11 @@ fun MutableList.removeBy(predicate: (T) -> Boolean): Boolean { val toRemove = this.filter(predicate) this.removeAll(toRemove) return toRemove.isNotEmpty() +} + +fun MutableList.replaceBy(item: T, predicate: (T) -> Boolean) { + val toRemove = this.filter(predicate) + val indexes = toRemove.map { indexOf(it) } + this.removeAll(toRemove) + indexes.forEach { this.add(it, item) } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt b/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt index 8a55bcfafe..c797764bbd 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt @@ -9,6 +9,7 @@ import com.tangem.tap.domain.MultiMessageError import com.tangem.tap.domain.TapError import com.tangem.tap.domain.assembleErrors import com.tangem.tap.notificationsHandler +import com.tangem.wallet.BuildConfig import org.rekotlin.Action import org.rekotlin.Middleware import java.lang.ref.WeakReference @@ -63,23 +64,29 @@ fun getMessageString(context: Context, message: Int, args: List?): String { val notificationsMiddleware: Middleware = { dispatch, state -> { next -> { action -> - when (action) { - is NotificationAction -> notificationsHandler?.showNotification(action.messageResource) - is ToastNotificationAction -> notificationsHandler?.showToastNotification(action.messageResource) - is ErrorAction -> { - when (action.error) { - is MultiMessageError -> { - val multiError = action.error as MultiMessageError - notificationsHandler?.showNotification(multiError.assembleErrors(), multiError.builder) - } - else -> { - val args = (action.error as? ArgError)?.args ?: listOf() - notificationsHandler?.showNotification(action.error.localizedMessage, args) - } - } + handleNotificationAction(action) + next(action) + } + } +} + +private fun handleNotificationAction(action: Action) { + if (action is Debug && !BuildConfig.DEBUG) return + + when (action) { + is NotificationAction -> notificationsHandler?.showNotification(action.messageResource) + is ToastNotificationAction -> notificationsHandler?.showToastNotification(action.messageResource) + is ErrorAction -> { + when (action.error) { + is MultiMessageError -> { + val multiError = action.error as MultiMessageError + notificationsHandler?.showNotification(multiError.assembleErrors(), multiError.builder) + } + else -> { + val args = (action.error as? ArgError)?.args ?: listOf() + notificationsHandler?.showNotification(action.error.localizedMessage, args) } } - next(action) } } } @@ -94,4 +101,10 @@ interface NotificationAction : Action { interface ErrorAction : Action { val error: TapError -} \ No newline at end of file +} + +// Processed only in the debug builds +interface Debug +interface DebugNotification : Debug, NotificationAction +interface DebugToastNotification : Debug, ToastNotificationAction +interface DebugErrorAction : Debug, ErrorAction \ No newline at end of file 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 56fe47fb45..adc0404f24 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -22,7 +22,7 @@ import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler import com.tangem.tap.domain.tasks.CreateWalletAndRescanTask import com.tangem.tap.domain.tasks.ScanNoteResponse import com.tangem.tap.domain.tasks.ScanNoteTask -import com.tangem.tap.domain.twins.isTwinCard +import com.tangem.tap.domain.twins.isTangemTwin import com.tangem.wallet.R import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -35,16 +35,22 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co analyticsHandler: AnalyticsHandler, messageRes: Int? = null, ): CompletionResult { analyticsHandler.triggerEvent(AnalyticsEvent.READY_TO_SCAN, null) - val result = runTaskAsyncReturnOnMain(ScanNoteTask(), - initialMessage = Message( - context.getString(messageRes ?: R.string.initial_message_scan_header) - )) - if (result is CompletionResult.Failure) { + + return runTaskAsyncReturnOnMain( + ScanNoteTask(), + initialMessage = Message(context.getString(messageRes ?: R.string.initial_message_scan_header)) + ).also { sendScanFailuresToAnalytics(analyticsHandler, it) } + } + + private fun sendScanFailuresToAnalytics( + 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) } } - return result } suspend fun createWallet(cardId: String?): CompletionResult { @@ -106,7 +112,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co } fun changeDisplayedCardIdNumbersCount(card: Card) { - tangemSdk.config.cardIdDisplayedNumbersCount = if (card.isTwinCard()) 4 else null + tangemSdk.config.cardIdDisplayedNumbersCount = if (card.isTangemTwin()) 4 else null } companion object { diff --git a/app/src/main/java/com/tangem/tap/domain/TapBlockchainConfig.kt b/app/src/main/java/com/tangem/tap/domain/TapBlockchainConfig.kt deleted file mode 100644 index b9ec4705a5..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/TapBlockchainConfig.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.tangem.tap.domain - -class TapBlockchainConfig { - companion object { - val blockchairApiKey: String = "A___0Shpsu4KagE7oSabrw20DfXAqWlT" - val blockcypherTokens: Set = setOf( - "aa8184b0e0894b88a5688e01b3dc1e82", - "56c4ca23c6484c8f8864c32fde4def8d", - "66a8a37c5e9d4d2c9bb191acfe7f93aa" - ) - val infuraProjectId: String = "613a0b14833145968b1f656240c7d245" - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt index ca57897238..8ad394c14f 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt @@ -21,6 +21,7 @@ sealed class TapError( ) : Throwable(), TapErrors, ArgError { object UnknownError : TapError(R.string.send_error_unknown) + object ScanCardError : TapError(R.string.scan_card_error) data class CustomError(val customMessage: String) : TapError(R.string.common_custom_string, listOf(customMessage)) object PayIdAlreadyCreated : TapError(R.string.wallet_create_payid_error_already_created) object PayIdCreatingError : TapError(R.string.wallet_create_payid_error_message) @@ -38,8 +39,8 @@ sealed class TapError( object DustChange : TapError(R.string.send_error_dust_change) data class CreateAccountUnderfunded(override val args: List) : TapError(R.string.send_error_no_target_account) - sealed class XmlError { - object AssetAccountNotCreated: TapError(R.string.send_error_no_account_xlm) + sealed class XmlError { + object AssetAccountNotCreated : TapError(R.string.send_error_no_account_xlm) } data class ValidateTransactionErrors( 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 466e463f19..0193a9d2a1 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -15,7 +15,7 @@ import com.tangem.tap.domain.extensions.* import com.tangem.tap.domain.tasks.ScanNoteResponse import com.tangem.tap.domain.tokens.CardCurrencies import com.tangem.tap.domain.twins.TwinsHelper -import com.tangem.tap.domain.twins.isTwinCard +import com.tangem.tap.domain.twins.isTangemTwin import com.tangem.tap.features.tokens.redux.TokensAction import com.tangem.tap.features.wallet.redux.Currency import com.tangem.tap.features.wallet.redux.WalletAction @@ -98,7 +98,7 @@ class TapWalletManager { store.dispatch(GlobalAction.SaveScanNoteResponse(data)) store.dispatch(WalletAction.SetIfTestnetCard(data.card.isTestCard)) store.dispatch(WalletAction.MultiWallet.SetIsMultiwalletAllowed(data.card.isMultiwalletAllowed)) - if (data.card.isTwinCard()) { + if (data.card.isTangemTwin()) { val secondCardId = TwinsHelper.getTwinsCardId(data.card.cardId) val cardNumber = TwinsHelper.getTwinCardNumber(data.card.cardId) if (secondCardId != null && cardNumber != null) { @@ -143,7 +143,7 @@ class TapWalletManager { store.dispatch(WalletAction.LoadData.Failure(TapError.UnknownBlockchain)) } data.card.wallets.isEmpty() || - (data.card.isTwinCard() && data.secondTwinPublicKey == null) -> { + (data.card.isTangemTwin() && data.secondTwinPublicKey == null) -> { store.dispatch(WalletAction.EmptyWallet) } else -> { @@ -152,7 +152,7 @@ class TapWalletManager { val blockchain = data.getBlockchain() val primaryWalletManager = walletManagerFactory.makePrimaryWalletManager(data) - if (blockchain != null && primaryWalletManager != null) { + if (blockchain != Blockchain.Unknown && primaryWalletManager != null) { val primaryToken = data.getPrimaryToken() store.dispatch(WalletAction.MultiWallet.SetPrimaryBlockchain(blockchain)) @@ -236,7 +236,7 @@ class TapWalletManager { store.dispatch(WalletAction.LoadData.Failure(TapError.UnknownBlockchain)) } data.card.wallets.isEmpty() || - (data.card.isTwinCard() && data.secondTwinPublicKey == null) -> { + (data.card.isTangemTwin() && data.secondTwinPublicKey == null) -> { store.dispatch(WalletAction.EmptyWallet) } else -> { diff --git a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt index 8af0e99415..ffc138b170 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt @@ -4,10 +4,10 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.common.card.Card import com.tangem.common.card.EllipticCurve import com.tangem.common.card.FirmwareVersion -import com.tangem.tap.domain.TapWorkarounds.isNote import com.tangem.tap.domain.TapWorkarounds.isStart2Coin +import com.tangem.tap.domain.TapWorkarounds.isTangemNote import com.tangem.tap.domain.extensions.getSingleWallet -import com.tangem.tap.domain.twins.isTwinCard +import com.tangem.tap.domain.twins.isTangemTwin import java.util.* object TapWorkarounds { @@ -28,26 +28,10 @@ object TapWorkarounds { return excludedBatch || excludedIssuerName } - fun Card.isNote(): Boolean { - return notesBatches.contains(batchId) - } + fun Card.isTangemNote(): Boolean = tangemNoteBatches.contains(batchId) + fun Card.isTangemWallet(): Boolean = tangemWalletBatches.contains(batchId) - fun Card.isMultiCurrencyWallet(): Boolean { - return multiCurrencyWalletsBatches.contains(batchId) - } - - val Card.noteCurrency: Blockchain? - get() { - return when (batchId) { - "AB01" -> Blockchain.Bitcoin - "AB02" -> Blockchain.Ethereum - "AB03" -> Blockchain.CardanoShelley - "AB04" -> Blockchain.Dogecoin - "AB05" -> Blockchain.Binance - "AB06" -> Blockchain.XRP - else -> null - } - } + fun Card.getTangemNoteBlockchain(): Blockchain? = tangemNoteBatches[batchId] private const val START_2_COIN_ISSUER = "start2coin" private const val TEST_CARD_BATCH = "99FF" @@ -64,21 +48,21 @@ object TapWorkarounds { "TTM BANK" ) - private val notesBatches = listOf( - "AB01", - "AB02", - "AB03", - "AB04", - "AB05", - "AB06", - ) + private val tangemWalletBatches = listOf("AC01") - private val multiCurrencyWalletsBatches = listOf("AC01") + private val tangemNoteBatches = mapOf( + "AB01" to Blockchain.Bitcoin, + "AB02" to Blockchain.Ethereum, + "AB03" to Blockchain.CardanoShelley, + "AB04" to Blockchain.Dogecoin, + "AB05" to Blockchain.Binance, + "AB06" to Blockchain.XRP, + ) } val Card.isMultiwalletAllowed: Boolean get() { - return !isTwinCard() && !isStart2Coin && !isNote() + return !isTangemTwin() && !isStart2Coin && !isTangemNote() && (firmwareVersion >= FirmwareVersion.MultiWalletAvailable || getSingleWallet()?.curve == EllipticCurve.Secp256k1) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/UrlBitmapLoader.kt b/app/src/main/java/com/tangem/tap/domain/UrlBitmapLoader.kt new file mode 100644 index 0000000000..d09d8ed3bc --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/UrlBitmapLoader.kt @@ -0,0 +1,38 @@ +package com.tangem.tap.domain + +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import com.squareup.picasso.Picasso +import com.squareup.picasso.Target +import com.tangem.common.services.Result + +/** +[REDACTED_AUTHOR] + */ +class UrlBitmapLoader { + + fun loadBitmap(url: String, callback: (Result) -> Unit) { + Picasso.get().load(url).into(DownloadTarget(callback)) + } + + fun loadBitmap(url: String, target: DownloadTarget) { + Picasso.get().load(url).into(target) + } +} + +// It adds the ability to trigger multiple downloads with a unique callback. +open class DownloadTarget( + val callback: (Result) -> Unit, +) : Target { + + override fun onBitmapLoaded(bitmap: Bitmap, from: Picasso.LoadedFrom) { + callback(Result.Success(bitmap)) + } + + override fun onBitmapFailed(e: java.lang.Exception?, errorDrawable: Drawable?) { + callback.invoke(Result.Failure(e)) + } + + override fun onPrepareLoad(placeHolderDrawable: Drawable?) { + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt b/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt index aeef948edf..dc3fc1a618 100644 --- a/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt +++ b/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt @@ -2,6 +2,9 @@ package com.tangem.tap.domain.extensions import com.tangem.common.card.Card import com.tangem.common.card.CardWallet +import com.tangem.common.extensions.toHexString +import com.tangem.operations.attestation.OnlineCardVerifier +import com.tangem.tap.features.wallet.redux.Artwork fun Card.getSingleWallet(): CardWallet? { return wallets.firstOrNull() @@ -9,6 +12,10 @@ fun Card.getSingleWallet(): CardWallet? { fun Card.hasWallets(): Boolean = wallets.isNotEmpty() +fun Card.hasNoWallets(): Boolean = wallets.isEmpty() + +fun Card.hasSingleWallet(): Boolean = wallets.size == 1 + fun Card.hasSignedHashes(): Boolean { return wallets.any { it.totalSignedHashes ?: 0 > 0 } } @@ -17,6 +24,17 @@ fun Card.signedHashesCount(): Int { return wallets.map { it.totalSignedHashes ?: 0 }.sum() } +fun Card.getArtworkUrl(artworkId: String?): String? { + return when { + artworkId != null -> { + OnlineCardVerifier.getUrlForArtwork(cardId, cardPublicKey.toHexString(), artworkId) + } + cardId.startsWith(Artwork.SERGIO_CARD_ID) -> Artwork.SERGIO_CARD_URL + cardId.startsWith(Artwork.MARTA_CARD_ID) -> Artwork.MARTA_CARD_URL + else -> null + } +} + val Card.remainingSignatures: Int? get() = this.getSingleWallet()?.remainingSignatures 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 5ec3f1afa9..0ab0ec0293 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 @@ -9,7 +9,7 @@ import com.tangem.common.card.EllipticCurve import com.tangem.common.extensions.hexToBytes import com.tangem.tap.domain.TapWorkarounds.isTestCard import com.tangem.tap.domain.tasks.ScanNoteResponse -import com.tangem.tap.domain.twins.isTwinCard +import com.tangem.tap.domain.twins.isTangemTwin fun WalletManagerFactory.makeWalletManagerForApp( card: Card, @@ -54,16 +54,18 @@ fun WalletManagerFactory.makePrimaryWalletManager( ): WalletManager? { val card = data.card val blockchain = if (card.isTestCard) { - data.getBlockchain()?.getTestnetVersion() + data.getBlockchain().getTestnetVersion() } else { data.getBlockchain() } val supportedCurves = blockchain?.getSupportedCurves() ?: return null + val wallets = card.wallets.filter { wallet -> supportedCurves.contains(wallet.curve) } val wallet = selectWallet(wallets) val publicKey = wallet?.publicKey ?: return null - val curveToUse = wallet.curve ?: return null - return if (card.isTwinCard() && data.secondTwinPublicKey != null) { + val curveToUse = wallet.curve + + return if (card.isTangemTwin() && data.secondTwinPublicKey != null) { makeMultisigWalletManager( cardId = card.cardId, walletPublicKey = publicKey, pairPublicKey = data.secondTwinPublicKey.hexToBytes(), diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt index 9f31169566..510d2785d8 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt @@ -2,9 +2,7 @@ package com.tangem.tap.domain.tasks import com.tangem.* import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.BlockchainSdkConfig import com.tangem.blockchain.common.Token -import com.tangem.blockchain.common.WalletManagerFactory import com.tangem.common.CompletionResult import com.tangem.common.card.Card import com.tangem.common.card.FirmwareVersion @@ -17,12 +15,12 @@ import com.tangem.operations.CommandResponse import com.tangem.operations.ScanTask import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand import com.tangem.tap.domain.TapSdkError +import com.tangem.tap.domain.TapWorkarounds.getTangemNoteBlockchain import com.tangem.tap.domain.TapWorkarounds.isExcluded -import com.tangem.tap.domain.TapWorkarounds.noteCurrency +import com.tangem.tap.domain.TapWorkarounds.isTangemNote import com.tangem.tap.domain.extensions.getSingleWallet import com.tangem.tap.domain.twins.TwinCardsManager -import com.tangem.tap.domain.twins.isTwinCard -import com.tangem.tap.store +import com.tangem.tap.domain.twins.isTangemTwin data class ScanNoteResponse( val card: Card, @@ -30,9 +28,9 @@ data class ScanNoteResponse( val secondTwinPublicKey: String? = null, ) : CommandResponse { - fun getBlockchain(): Blockchain? { - if (card.noteCurrency != null) return card.noteCurrency - val blockchainName: String = walletData?.blockchain ?: return null + fun getBlockchain(): Blockchain { + if (card.isTangemNote()) return card.getTangemNoteBlockchain() ?: return Blockchain.Unknown + val blockchainName: String = walletData?.blockchain ?: return Blockchain.Unknown return Blockchain.fromId(blockchainName) } @@ -67,7 +65,7 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable= FirmwareVersion.MultiWalletAvailable) { createMissingWalletsIfNeeded(card, session, callback) @@ -146,20 +144,7 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable { - val notAllowedByCard = - state.card?.settings?.isPermanentWallet == true - || state.card?.isWalletDataSupported == true + val notAllowedByAnyWallet = state.card?.wallets?.any { it.settings.isPermanent } ?: false + val notAllowedByCard = notAllowedByAnyWallet || state.card?.isWalletDataSupported == true val notEmpty = state.wallets.any { - !it.recentTransactions.toPendingTransactions(it.address).isNullOrEmpty() - || it.amounts.toSendableAmounts().isNotEmpty() + it.hasPendingTransactions() || it.amounts.toSendableAmounts().isNotEmpty() } val eraseWalletState = when { notAllowedByCard -> EraseWalletState.NotAllowedByCard 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 55f9711482..640cc47cf0 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 @@ -12,7 +12,7 @@ import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.twins.getTwinCardIdForUser -import com.tangem.tap.domain.twins.isTwinCard +import com.tangem.tap.domain.twins.isTangemTwin import com.tangem.tap.features.details.redux.DetailsAction import com.tangem.tap.features.details.redux.DetailsState import com.tangem.tap.features.details.redux.SecurityOption @@ -68,7 +68,7 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber.toPendingTransactionsForToken(token: Token, walletAddress: String): List { return this.mapNotNull { it.toPendingTransactionForToken(token, walletAddress) } +} + +fun Wallet.getPendingTransactions(): List { + return recentTransactions.toPendingTransactions(address) +} + +fun Wallet.hasPendingTransactions(): Boolean { + return recentTransactions.toPendingTransactions(address).isNotEmpty() } \ No newline at end of file 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 d3a6c2d046..d69b213ece 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 @@ -17,7 +17,7 @@ import com.tangem.tap.domain.extensions.getSingleWallet import com.tangem.tap.domain.extensions.hasSignedHashes import com.tangem.tap.domain.extensions.remainingSignatures import com.tangem.tap.domain.isMultiwalletAllowed -import com.tangem.tap.domain.twins.isTwinCard +import com.tangem.tap.domain.twins.isTangemTwin import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.network.NetworkConnectivity import com.tangem.tap.preferencesStorage @@ -41,7 +41,7 @@ class WarningsMiddleware { is WalletAction.Warnings.CheckHashesCount.CheckHashesCountOnline -> checkHashesCountOnline() is WalletAction.Warnings.CheckHashesCount.SaveCardId -> { val cardId = globalState?.scanNoteResponse?.card?.cardId - cardId?.let { preferencesStorage.saveScannedCardId(it) } + cardId?.let { preferencesStorage.usedCardsPrefStorage.scanned(it) } } is WalletAction.Warnings.AppRating.RemindLater -> { preferencesStorage.appRatingLaunchObserver.applyDelayedShowing() @@ -91,7 +91,7 @@ class WarningsMiddleware { showWarningLowRemainingSignaturesIfNeeded(card) if (card.firmwareVersion.type != FirmwareVersion.FirmwareType.Release) { addWarningMessage(WarningMessagesManager.devCardWarning()) - } else if (!preferencesStorage.wasCardScannedBefore(card.cardId)) { + } else if (!preferencesStorage.usedCardsPrefStorage.wasScanned(card.cardId)) { checkIfWarningNeeded(card)?.let { warning -> addWarningMessage(warning) } } if (card.firmwareVersion.type == FirmwareVersion.FirmwareType.Release) { @@ -119,7 +119,7 @@ class WarningsMiddleware { private fun checkIfWarningNeeded( card: Card, ): WarningMessage? { - if (card.isTwinCard()) return null + if (card.isTangemTwin()) return null if (card.isMultiwalletAllowed) { return if (card.hasSignedHashes()) { @@ -150,9 +150,9 @@ class WarningsMiddleware { if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) return val card = store.state.globalState.scanNoteResponse?.card - if (card == null || preferencesStorage.wasCardScannedBefore(card.cardId)) return + if (card == null || preferencesStorage.usedCardsPrefStorage.wasScanned(card.cardId)) return - if (card.isTwinCard() || card.isMultiwalletAllowed) return + if (card.isTangemTwin() || card.isMultiwalletAllowed) return val validator = store.state.walletState.walletManagers.firstOrNull() as? SignatureCountValidator 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 9bdef4b340..8c6db9d9a2 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 @@ -2,12 +2,11 @@ package com.tangem.tap.features.wallet.redux.reducers import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.Wallet -import com.tangem.common.extensions.toHexString -import com.tangem.operations.attestation.OnlineCardVerifier import com.tangem.tap.common.extensions.* import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.FiatCurrencyName import com.tangem.tap.domain.TapError +import com.tangem.tap.domain.extensions.getArtworkUrl import com.tangem.tap.domain.getFirstToken import com.tangem.tap.domain.twins.TwinCardNumber import com.tangem.tap.features.wallet.redux.* @@ -215,29 +214,12 @@ private fun internalReduce(action: Action, state: AppState): WalletState { is WalletAction.LoadFiatRate.Success -> newState = setNewFiatRate(action.fiatRate, state.globalState.appCurrency, newState) is WalletAction.LoadArtwork -> { - val cardId = action.card.cardId - val cardPublicKey = action.card.cardPublicKey.toHexString() - val artworkUrl = when { - action.artworkId != null -> { - OnlineCardVerifier.getUrlForArtwork(cardId, cardPublicKey, action.artworkId) - } - action.card.cardId.startsWith(Artwork.SERGIO_CARD_ID) -> { - Artwork.SERGIO_CARD_URL - } - action.card.cardId.startsWith(Artwork.MARTA_CARD_ID) -> { - Artwork.MARTA_CARD_URL - } - newState.twinCardsState?.cardNumber != null -> { - when (newState.twinCardsState?.cardNumber) { + val artworkUrl = action.card.getArtworkUrl(action.artworkId) + ?: when (newState.twinCardsState?.cardNumber) { TwinCardNumber.First -> Artwork.TWIN_CARD_1 TwinCardNumber.Second -> Artwork.TWIN_CARD_2 - null -> Artwork.DEFAULT_IMG_URL + else -> Artwork.DEFAULT_IMG_URL } - } - else -> { - Artwork.DEFAULT_IMG_URL - } - } newState = newState.copy(cardImage = Artwork(artworkId = artworkUrl)) } is WalletAction.ShowDialog.QrCode -> { diff --git a/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt b/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt index 871e3eb3d5..77c8be322d 100644 --- a/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt +++ b/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt @@ -19,10 +19,13 @@ class PreferencesStorage(applicationContext: Application) { private val preferences: SharedPreferences = applicationContext.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE) val appRatingLaunchObserver: AppRatingLaunchObserver + val usedCardsPrefStorage: UsedCardsPrefStorage init { incrementLaunchCounter() appRatingLaunchObserver = AppRatingLaunchObserver(preferences, getCountOfLaunches()) + usedCardsPrefStorage = UsedCardsPrefStorage(preferences) + usedCardsPrefStorage.migrate() } private val fiatCurrenciesAdapter: JsonAdapter> by lazy { @@ -54,20 +57,11 @@ class PreferencesStorage(applicationContext: Application) { fun getCountOfLaunches(): Int = preferences.getInt(APP_LAUNCH_COUNT_KEY, 1) - fun saveScannedCardId(cardId: String) { - val scannedCardsIds: String = restoreScannedCardIds() - if (!scannedCardsIds.contains(cardId)) { - preferences.edit().putString(SCANNED_CARDS_IDS_KEY, "$scannedCardsIds$cardId, ").apply() - } - } - + @Deprecated("Use UsedCardsPrefStorage instead") fun wasCardScannedBefore(cardId: String): Boolean { - return restoreScannedCardIds().contains(cardId) + return usedCardsPrefStorage.wasScanned(cardId) } - private fun restoreScannedCardIds(): String = - preferences.getString(SCANNED_CARDS_IDS_KEY, "") ?: "" - fun saveDisclaimerAccepted() { preferences.edit().putBoolean(DISCLAIMER_ACCEPTED_KEY, true).apply() } @@ -93,7 +87,6 @@ class PreferencesStorage(applicationContext: Application) { private const val PREFERENCES_NAME = "tapPrefs" private const val APP_CURRENCY_KEY = "appCurrency" private const val FIAT_CURRENCIES_KEY = "fiatCurrencies" - private const val SCANNED_CARDS_IDS_KEY = "scannedCardIds" private const val DISCLAIMER_ACCEPTED_KEY = "disclaimerAccepted" private const val TWINS_ONBOARDING_SHOWN_KEY = "twinsOnboardingShown" private const val APP_LAUNCH_COUNT_KEY = "launchCount" @@ -102,8 +95,8 @@ class PreferencesStorage(applicationContext: Application) { } class AppRatingLaunchObserver( - private val preferences: SharedPreferences, - private val launchCounts: Int, + private val preferences: SharedPreferences, + private val launchCounts: Int, ) { private val K_SHOW_RATING_AT_LAUNCH_COUNT = "showRatingDialogAtLaunchCount" private val K_FUNDS_FOUND_DATE = "fundsFoundDate" diff --git a/app/src/main/java/com/tangem/tap/persistence/UsedCardsPrefStorage.kt b/app/src/main/java/com/tangem/tap/persistence/UsedCardsPrefStorage.kt new file mode 100644 index 0000000000..b6ebfd8a32 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/persistence/UsedCardsPrefStorage.kt @@ -0,0 +1,107 @@ +package com.tangem.tap.persistence + +import android.content.SharedPreferences +import androidx.core.content.edit +import com.tangem.common.json.MoshiJsonConverter +import com.tangem.tap.common.extensions.replaceBy +import timber.log.Timber + +/** +[REDACTED_AUTHOR] + */ +class UsedCardsPrefStorage( + private val preferences: SharedPreferences, +) { + + private val jsonConverter = MoshiJsonConverter.INSTANCE + + internal fun migrate() { + val scannedIds = preferences.getString(SCANNED_CARDS_IDS_KEY, null) ?: return + + val usedCardsInfo = scannedIds.split(",").map { UsedCardInfo(it.trim(), true) } + if (save(usedCardsInfo.toMutableList())) { + preferences.edit { this.remove(SCANNED_CARDS_IDS_KEY) } + } + } + + fun scanned(cardId: String) { + val restoredList = restore() + val foundItem = findCardInfo(cardId, restoredList)?.copy(isScanned = true) + ?: UsedCardInfo(cardId, true) + + save(foundItem, restoredList) + } + + fun wasScanned(cardId: String): Boolean { + return findCardInfo(cardId)?.isScanned ?: false + } + + fun activationStarted(cardId: String) { + val restoredList = restore() + val foundItem = findCardInfo(cardId, restoredList)?.copy(isActivationStarted = true) + ?: UsedCardInfo(cardId, isActivationStarted = true) + + save(foundItem, restoredList) + } + + fun activationIsStarted(cardId: String): Boolean { + return findCardInfo(cardId)?.isActivationStarted ?: false + } + + fun activated(cardId: String) { + val restoredList = restore() + val foundItem = findCardInfo(cardId, restoredList)?.copy(isActivated = true) + ?: UsedCardInfo(cardId, isActivated = true) + + save(foundItem, restoredList) + } + + fun wasActivated(cardId: String): Boolean { + return findCardInfo(cardId)?.isActivated ?: false + } + + private fun findCardInfo(cardId: String, list: MutableList? = null): UsedCardInfo? { + val findInList = list ?: restore() + return findInList.firstOrNull { it.cardId == cardId } + } + + private fun save(usedCardInfo: UsedCardInfo?, usedCardsInfo: MutableList) { + val info = usedCardInfo ?: return + + usedCardsInfo.replaceBy(info) { it.cardId == info.cardId } + save(usedCardsInfo) + } + + private fun save(list: MutableList): Boolean { + return try { + val json = jsonConverter.toJson(list) + preferences.edit { putString(USED_CARDS_INFO, json) } + true + } catch (ex: Exception) { + Timber.e(ex) + false + } + } + + private fun restore(): MutableList { + val json = preferences.getString(USED_CARDS_INFO, null) ?: return mutableListOf() + return try { + jsonConverter.fromJson(json)!! + } catch (ex: Exception) { + preferences.edit(true) { remove(USED_CARDS_INFO) } + mutableListOf() + } + } + + companion object { + private const val USED_CARDS_INFO = "usedCardsInfo" + private const val SCANNED_CARDS_IDS_KEY = "scannedCardIds" + } +} + +private data class UsedCardInfo( + val cardId: String, + val isScanned: Boolean = false, + val isActivationStarted: Boolean = false, + val isActivated: Boolean = false, +) \ 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 9404210575..888918f16b 100644 --- a/app/src/main/res/values/strings_untranslated.xml +++ b/app/src/main/res/values/strings_untranslated.xml @@ -1,4 +1,6 @@ + Scan card failed. Try again + Buy Sell Trade @@ -168,4 +170,28 @@ this wallet. Do you want to buy or sell crypto? Oops, the current version of the application is not ready to work with this card, please check for updates. + + + Welcome to Tangem + The safest way to buy, use and\n store cryptocurrency + Get a new card + + Activating card + What does it mean? + Create a wallet + Let’s generate all the keys on your card and create a secure wallet + Create wallet + + Top up your wallet + To get started, simply top up the card with any amount + Buy crypto + Show the wallet’s address + Scan the address to top up your wallet + + Done! + Your crypto card is activated and ready to be used + Continue + + Internal error: can\'t create wallet manager + From 99f1b91baeb5bc7a6dfde1c8f2ff38d2b99f24a6 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 23 Sep 2021 11:57:44 +0300 Subject: [PATCH 040/189] Updated on 2026-08-14 --- app/src/main/java/com/tangem/tap/common/redux/AppState.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt index 8762583a80..79400725e3 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt @@ -12,7 +12,7 @@ import com.tangem.tap.features.disclaimer.redux.DisclaimerMiddleware import com.tangem.tap.features.disclaimer.redux.DisclaimerState import com.tangem.tap.features.home.redux.HomeMiddleware import com.tangem.tap.features.home.redux.HomeState -import com.tangem.tap.features.send.redux.middlewares.sendMiddleware +import com.tangem.tap.features.send.redux.middlewares.SendMiddleware import com.tangem.tap.features.send.redux.states.SendState import com.tangem.tap.features.tokens.redux.TokensMiddleware import com.tangem.tap.features.tokens.redux.TokensState From a7fc5e10363b2e0788888c6a3a9326db949f13ba Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 23 Sep 2021 12:49:34 +0300 Subject: [PATCH 041/189] Updated on 2026-08-14 --- .../tap/common/extensions/Navigation.kt | 10 +- .../com/tangem/tap/common/redux/AppReducer.kt | 6 + .../com/tangem/tap/common/redux/AppState.kt | 19 +- .../tap/common/redux/global/GlobalAction.kt | 11 ++ .../common/redux/global/GlobalMidlleware.kt | 27 ++- .../redux/navigation/NavigationAction.kt | 15 +- .../redux/navigation/NavigationState.kt | 15 +- .../disclaimer/redux/DisclaimerMiddleware.kt | 17 +- .../tangem/tap/features/home/HomeFragment.kt | 77 +++++--- .../tap/features/home/redux/HomeAction.kt | 16 +- .../tap/features/home/redux/HomeMiddleware.kt | 126 ++++++------- .../tap/features/home/redux/HomeReducer.kt | 21 ++- .../tap/features/home/redux/HomeState.kt | 14 +- .../features/onboarding/OnboardingService.kt | 168 +++++++++++++++++ .../products/note/OnboardingNoteFragment.kt | 133 ++++++++++++++ .../note/redux/OnboardingNoteAction.kt | 16 ++ .../note/redux/OnboardingNoteMiddleware.kt | 72 ++++++++ .../note/redux/OnboardingNoteReducer.kt | 38 ++++ .../note/redux/OnboardingNoteState.kt | 18 ++ .../old/OnboardingOtherCardsFragment.kt | 50 ++++++ .../wallet/OnboardingWalletFragment.kt | 52 ++++++ .../wallet/redux/OnboardingWalletAction.kt | 19 ++ .../redux/OnboardingWalletMiddleware.kt | 18 ++ .../wallet/redux/OnboardingWalletReducer.kt | 20 +++ .../wallet/redux/OnboardingWalletState.kt | 15 ++ .../onboarding/redux/OnboardingAction.kt | 22 +++ .../onboarding/redux/OnboardingMiddleware.kt | 22 +++ .../onboarding/redux/OnboardingReducer.kt | 24 +++ .../onboarding/redux/OnboardingState.kt | 11 ++ .../onboarding/redux/StateSupportClasses.kt | 26 +++ ...Fragment.kt => OnboardingTwinsFragment.kt} | 2 +- .../wallet/ui/wallet/SingleWalletView.kt | 2 +- app/src/main/res/drawable/bg_circle_gray.xml | 9 + .../main/res/drawable/ic_home_card_back.xml | 108 +++++++++++ .../main/res/drawable/ic_home_card_front.xml | 126 +++++++++++++ app/src/main/res/layout/fragment_home.xml | 169 +++++++++++------- .../main/res/layout/fragment_onboarding.xml | 161 +++++++++++++++++ .../res/layout/fragment_onboarding_wallet.xml | 6 + 38 files changed, 1486 insertions(+), 195 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/OnboardingService.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/old/OnboardingOtherCardsFragment.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/OnboardingWalletFragment.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingAction.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingMiddleware.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingReducer.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingState.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/redux/StateSupportClasses.kt rename app/src/main/java/com/tangem/tap/features/wallet/ui/{TwinsOnboardingFragment.kt => OnboardingTwinsFragment.kt} (97%) create mode 100644 app/src/main/res/drawable/bg_circle_gray.xml create mode 100644 app/src/main/res/drawable/ic_home_card_back.xml create mode 100644 app/src/main/res/drawable/ic_home_card_front.xml create mode 100644 app/src/main/res/layout/fragment_onboarding.xml create mode 100644 app/src/main/res/layout/fragment_onboarding_wallet.xml diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt index b3657a070c..c7e04cf44f 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt @@ -16,9 +16,12 @@ import com.tangem.tap.features.details.ui.walletconnect.QrScanFragment import com.tangem.tap.features.details.ui.walletconnect.WalletConnectSessionsFragment import com.tangem.tap.features.disclaimer.ui.DisclaimerFragment import com.tangem.tap.features.home.HomeFragment +import com.tangem.tap.features.onboarding.products.note.OnboardingNoteFragment +import com.tangem.tap.features.onboarding.products.old.OnboardingOtherCardsFragment +import com.tangem.tap.features.onboarding.products.wallet.OnboardingWalletFragment import com.tangem.tap.features.send.ui.SendFragment import com.tangem.tap.features.tokens.ui.AddTokensFragment -import com.tangem.tap.features.wallet.ui.TwinsOnboardingFragment +import com.tangem.tap.features.wallet.ui.OnboardingTwinsFragment import com.tangem.tap.features.wallet.ui.WalletDetailsFragment import com.tangem.tap.features.wallet.ui.WalletFragment import com.tangem.wallet.R @@ -67,6 +70,10 @@ fun FragmentActivity.addOnBackPressedDispatcher( private fun fragmentFactory(screen: AppScreen): Fragment { return when (screen) { AppScreen.Home -> HomeFragment() + AppScreen.OnboardingNote -> OnboardingNoteFragment() + AppScreen.OnboardingWallet -> OnboardingWalletFragment() + AppScreen.OnboardingTwins -> OnboardingTwinsFragment() + AppScreen.OnboardingOther -> OnboardingOtherCardsFragment() AppScreen.Wallet -> WalletFragment() AppScreen.Send -> SendFragment() AppScreen.Details -> DetailsFragment() @@ -75,7 +82,6 @@ private fun fragmentFactory(screen: AppScreen): Fragment { AppScreen.Disclaimer -> DisclaimerFragment() AppScreen.CreateTwinWalletWarning -> TwinWalletWarningFragment() AppScreen.CreateTwinWallet -> CreateTwinWalletFragment() - AppScreen.TwinsOnboarding -> TwinsOnboardingFragment() AppScreen.AddTokens -> AddTokensFragment() AppScreen.WalletDetails -> WalletDetailsFragment() AppScreen.WalletConnectSessions -> WalletConnectSessionsFragment() diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt index 57ed1eaf2c..e60b9cade1 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt @@ -6,6 +6,9 @@ import com.tangem.tap.features.details.redux.DetailsReducer import com.tangem.tap.features.details.redux.walletconnect.WalletConnectReducer import com.tangem.tap.features.disclaimer.redux.DisclaimerReducer import com.tangem.tap.features.home.redux.HomeReducer +import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteReducer +import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletReducer +import com.tangem.tap.features.onboarding.redux.OnboardingReducer import com.tangem.tap.features.send.redux.reducers.SendScreenReducer import com.tangem.tap.features.tokens.redux.TokensReducer import com.tangem.tap.features.wallet.redux.reducers.WalletReducer @@ -19,6 +22,9 @@ fun appReducer(action: Action, state: AppState?): AppState { navigationState = NavigationReducer.reduce(action, state), globalState = globalReducer(action, state), homeState = HomeReducer.reduce(action, state), + onboardingState = OnboardingReducer.reduce(action, state), + onboardingNoteState = OnboardingNoteReducer.reduce(action, state), + onboardingWalletState = OnboardingWalletReducer.reduce(action, state), walletState = WalletReducer.reduce(action, state), sendState = SendScreenReducer.reduce(action, state.sendState), detailsState = DetailsReducer.reduce(action, state), diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt index 79400725e3..985a2a75ba 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt @@ -1,7 +1,7 @@ package com.tangem.tap.common.redux +import com.tangem.tap.common.redux.global.GlobalMiddleware import com.tangem.tap.common.redux.global.GlobalState -import com.tangem.tap.common.redux.global.globalMiddleware import com.tangem.tap.common.redux.navigation.NavigationState import com.tangem.tap.common.redux.navigation.navigationMiddleware import com.tangem.tap.features.details.redux.DetailsMiddleware @@ -12,6 +12,12 @@ import com.tangem.tap.features.disclaimer.redux.DisclaimerMiddleware import com.tangem.tap.features.disclaimer.redux.DisclaimerState import com.tangem.tap.features.home.redux.HomeMiddleware import com.tangem.tap.features.home.redux.HomeState +import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteMiddleware +import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteState +import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletMiddleware +import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletState +import com.tangem.tap.features.onboarding.redux.OnboardingMiddleware +import com.tangem.tap.features.onboarding.redux.OnboardingState import com.tangem.tap.features.send.redux.middlewares.SendMiddleware import com.tangem.tap.features.send.redux.states.SendState import com.tangem.tap.features.tokens.redux.TokensMiddleware @@ -25,6 +31,9 @@ data class AppState( val navigationState: NavigationState = NavigationState(), val globalState: GlobalState = GlobalState(), val homeState: HomeState = HomeState(), + val onboardingState: OnboardingState = OnboardingState(), + val onboardingNoteState: OnboardingNoteState = OnboardingNoteState(), + val onboardingWalletState: OnboardingWalletState = OnboardingWalletState(), val walletState: WalletState = WalletState(), val sendState: SendState = SendState(), val detailsState: DetailsState = DetailsState(), @@ -36,8 +45,12 @@ data class AppState( companion object { fun getMiddleware(): List> { return listOf( - logMiddleware, navigationMiddleware, notificationsMiddleware, globalMiddleware, - HomeMiddleware().homeMiddleware, + logMiddleware, navigationMiddleware, notificationsMiddleware, + GlobalMiddleware.handler, + HomeMiddleware.handler, + OnboardingMiddleware.handler, + OnboardingNoteMiddleware.handler, + OnboardingWalletMiddleware.handler, WalletMiddleware().walletMiddleware, SendMiddleware().sendMiddleware, DetailsMiddleware().detailsMiddleware, 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 c1c9137aec..928cb7b341 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 @@ -2,6 +2,9 @@ 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.redux.DebugErrorAction +import com.tangem.tap.domain.TapError import com.tangem.tap.domain.configurable.config.ConfigManager import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager @@ -14,6 +17,14 @@ import org.rekotlin.Action sealed class GlobalAction : Action { + data class DebugShowError(override val error: TapError) : GlobalAction(), DebugErrorAction + + data class ReadCard( + val onSuccess: (suspend (ScanNoteResponse) -> Unit)? = null, + val onFailure: (suspend (TangemError) -> Unit)? = null, + val messageResId: Int? = null, + ) : GlobalAction() + object ScanFailsCounter { data class ChooseBehavior(val result: CompletionResult) : GlobalAction() object Reset : 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 d90e263263..115329af90 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 @@ -3,6 +3,7 @@ package com.tangem.tap.common.redux.global import com.tangem.common.CompletionResult import com.tangem.common.core.TangemSdkError import com.tangem.common.services.Result +import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.redux.AppState import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager @@ -13,10 +14,19 @@ import com.tangem.tap.network.moonpay.MoonpayService import com.tangem.tap.preferencesStorage import com.tangem.tap.scope import com.tangem.tap.store +import com.tangem.tap.tangemSdkManager +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.rekotlin.Middleware -val globalMiddleware: Middleware = { dispatch, appState -> +class GlobalMiddleware { + companion object { + val handler = globalMiddlewareHandler + } +} + +private val globalMiddlewareHandler: Middleware = { dispatch, appState -> { nextDispatch -> { action -> when (action) { @@ -78,6 +88,21 @@ val globalMiddleware: Middleware = { dispatch, appState -> } } } + is GlobalAction.ReadCard -> { + scope.launch { + val result = tangemSdkManager.scanNote(FirebaseAnalyticsHandler, action.messageResId) + withContext(Dispatchers.Main) { + store.dispatch(GlobalAction.ScanFailsCounter.ChooseBehavior(result)) + when (result) { + is CompletionResult.Success -> { + tangemSdkManager.changeDisplayedCardIdNumbersCount(result.data.card) + action.onSuccess?.invoke(result.data) + } + is CompletionResult.Failure -> action.onFailure?.invoke(result.error) + } + } + } + } } nextDispatch(action) } diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt index 9a4794b29d..375b16e785 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt @@ -2,6 +2,9 @@ package com.tangem.tap.common.redux.navigation import android.view.View import androidx.fragment.app.FragmentActivity +import androidx.transition.ChangeBounds +import androidx.transition.ChangeImageTransform +import androidx.transition.ChangeTransform import androidx.transition.TransitionSet import org.rekotlin.Action import java.lang.ref.WeakReference @@ -27,4 +30,14 @@ data class FragmentShareTransition( val exitTransitionSet: TransitionSet ) -data class ShareElement(val wView: WeakReference, val name: String) \ No newline at end of file +data class ShareElement(val wView: WeakReference, val name: String) + +//TODO: remove transition example +class DetailsTransition : TransitionSet() { + init { + ordering = ORDERING_TOGETHER; + addTransition(ChangeBounds()) + .addTransition(ChangeTransform()) + .addTransition(ChangeImageTransform()) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt index ed59b9b2d5..4fa02150f0 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt @@ -5,12 +5,19 @@ import org.rekotlin.StateType import java.lang.ref.WeakReference data class NavigationState( - val backStack: List = listOf(AppScreen.Home), - val activity: WeakReference? = null + val backStack: List = listOf(AppScreen.Home), + val activity: WeakReference? = null ) : StateType enum class AppScreen { - Home, Wallet, WalletDetails, Send, Details, DetailsConfirm, DetailsSecurity, Disclaimer, - CreateTwinWalletWarning, CreateTwinWallet, TwinsOnboarding, AddTokens, WalletConnectSessions, + Home, + Disclaimer, + OnboardingNote, OnboardingWallet, OnboardingTwins, OnboardingOther, + Wallet, WalletDetails, + Send, + Details, DetailsConfirm, DetailsSecurity, + CreateTwinWalletWarning, CreateTwinWallet, + AddTokens, + WalletConnectSessions, QrScan } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerMiddleware.kt b/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerMiddleware.kt index 066b2b83db..12a1cfaccd 100644 --- a/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerMiddleware.kt @@ -1,8 +1,9 @@ package com.tangem.tap.features.disclaimer.redux +import com.tangem.tap.common.post import com.tangem.tap.common.redux.AppState -import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction +import com.tangem.tap.features.home.redux.HomeAction import com.tangem.tap.preferencesStorage import com.tangem.tap.store import org.rekotlin.Middleware @@ -14,16 +15,10 @@ class DisclaimerMiddleware { when (action) { is DisclaimerAction.AcceptDisclaimer -> { preferencesStorage.saveDisclaimerAccepted() - if (store.state.walletState.twinCardsState != null) { - val showOnboarding = !preferencesStorage.wasTwinsOnboardingShown() - if (showOnboarding) { - store.dispatch(NavigationAction.NavigateTo(AppScreen.TwinsOnboarding)) - } else { - store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) - } - } else { - store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) - } + store.dispatch(NavigationAction.PopBackTo()) + + //delayed sending used for better animation + post(550) { store.dispatch(HomeAction.ReadCard) } } } next(action) diff --git a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt index 29cd04dc73..168941d2c5 100644 --- a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt @@ -1,12 +1,20 @@ package com.tangem.tap.features.home import android.app.Dialog +import android.content.Intent +import android.net.Uri import android.os.Bundle import android.view.View import androidx.fragment.app.Fragment import androidx.transition.TransitionInflater -import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.common.extensions.safeStartActivity +import com.tangem.tap.common.extensions.toast import com.tangem.tap.common.redux.global.StateDialog +import com.tangem.tap.common.redux.navigation.DetailsTransition +import com.tangem.tap.common.redux.navigation.FragmentShareTransition +import com.tangem.tap.common.redux.navigation.ShareElement +import com.tangem.tap.common.toggleWidget.IndeterminateProgressButtonWidget +import com.tangem.tap.common.toggleWidget.ViewStateWidget import com.tangem.tap.features.home.redux.HomeAction import com.tangem.tap.features.home.redux.HomeDialog import com.tangem.tap.features.home.redux.HomeState @@ -15,11 +23,36 @@ import com.tangem.tap.store import com.tangem.wallet.R import kotlinx.android.synthetic.main.fragment_home.* import org.rekotlin.StoreSubscriber +import java.lang.ref.WeakReference class HomeFragment : Fragment(R.layout.fragment_home), StoreSubscriber { + lateinit var btnScanCard: ViewStateWidget + private var dialog: Dialog? = null + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val inflater = TransitionInflater.from(requireContext()) + exitTransition = inflater.inflateTransition(R.transition.fade) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val transitionViews = mutableListOf() + transitionViews.add(ShareElement(WeakReference(imv_front_card), "imv_front_card")) + val shareTransition = FragmentShareTransition(transitionViews, DetailsTransition(), DetailsTransition()) + + store.dispatch(HomeAction.SetFragmentShareTransition(shareTransition)) + store.dispatch(HomeAction.Init) + + btn_scan_card?.setOnClickListener { store.dispatch(HomeAction.ReadCard) } + btn_get_new_card?.setOnClickListener { store.dispatch(HomeAction.GoToShop) } + + btnScanCard = IndeterminateProgressButtonWidget(btn_scan_card, progress) + } + override fun onStart() { super.onStart() store.subscribe(this) { state -> @@ -37,16 +70,23 @@ class HomeFragment : Fragment(R.layout.fragment_home), StoreSubscriber = { dispatch, state -> - { next -> - { action -> - when (action) { - is HomeAction.CheckIfFirstLaunch -> { - store.dispatch( - HomeAction.CheckIfFirstLaunch.Result(preferencesStorage.getCountOfLaunches() == 1) - ) - } - is HomeAction.ReadCard -> { - scope.launch { - val result = tangemSdkManager.scanNote(FirebaseAnalyticsHandler) - withContext(Dispatchers.Main) { - store.dispatch(GlobalAction.ScanFailsCounter.ChooseBehavior(result)) - when (result) { - is CompletionResult.Success -> { - tangemSdkManager.changeDisplayedCardIdNumbersCount(result.data.card) - store.dispatch(GlobalAction.RestoreAppCurrency) - store.state.globalState.tapWalletManager.onCardScanned(result.data) - showDisclaimerOrNavigateToWallet() - } - } - } - } - } - is HomeAction.GoToShop -> { - val uri = Uri.parse(CARD_SHOP_URI) - val intent = Intent(Intent.ACTION_VIEW, uri) - startActivity(action.context, intent, null) - } - } - next(action) - } - } - } - - private fun showDisclaimerOrNavigateToWallet() { - if (!preferencesStorage.wasDisclaimerAccepted()) { - store.dispatch(NavigationAction.NavigateTo(AppScreen.Disclaimer)) - return - } - if (store.state.walletState.twinCardsState != null) { - val showOnboarding = !preferencesStorage.wasTwinsOnboardingShown() - if (showOnboarding) { - store.dispatch(NavigationAction.NavigateTo(AppScreen.TwinsOnboarding)) - return - } - } - store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) - } - companion object { - private const val CARD_SHOP_URI = + val handler = homeMiddleware + + const val CARD_SHOP_URI = "https://shop.tangem.com/?afmc=1i&utm_campaign=1i&utm_source=leaddyno&utm_medium=affiliate" } } +private val homeMiddleware: Middleware = { dispatch, state -> + { next -> + { action -> + when (action) { + is HomeAction.Init -> { + store.dispatch(GlobalAction.RestoreAppCurrency) + store.dispatch(GlobalAction.GetMoonPayUserStatus) + store.dispatch(HomeAction.SetTermsOfUseState(preferencesStorage.wasDisclaimerAccepted())) + } + is HomeAction.ReadCard -> { + if (!preferencesStorage.wasDisclaimerAccepted()) { + store.dispatch(NavigationAction.NavigateTo(AppScreen.Disclaimer)) + } else { + launchOnboardingService() + } + } + is HomeAction.GoToShop -> store.dispatch(HomeAction.SetOpenUrl(HomeMiddleware.CARD_SHOP_URI)) + } + next(action) + } + } +} +private fun launchOnboardingService() { + fun changeButtonState(state: ButtonState) { + val btnState = IndeterminateProgressButton(state) + store.dispatch(HomeAction.ChangeScanCardButtonState(btnState)) + } + changeButtonState(ButtonState.PROGRESS) + val walletManager = store.state.globalState.tapWalletManager + val service = OnboardingService( + walletManager, + preferencesStorage.usedCardsPrefStorage + ) + + service.onReadyToProceed = { + store.dispatch(OnboardingAction.SetData(it)) + changeButtonState(ButtonState.ENABLED) + store.dispatch(NavigationAction.NavigateTo(it.onboardingScreen)) + } + + service.onFailedToProceedToOnboardingCase = { + changeButtonState(ButtonState.ENABLED) + walletManager.onCardScanned(it) + store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) + } + + service.onError = { + changeButtonState(ButtonState.ENABLED) + store.dispatch(GlobalAction.DebugShowError(TapError.CustomError(it))) + } + + store.dispatch(GlobalAction.ReadCard(service::onSuccess, { + changeButtonState(ButtonState.ENABLED) + store.dispatch(GlobalAction.DebugShowError(TapError.ScanCardError)) + })) +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt index 3c031ed7b2..6fe2462f08 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt @@ -13,18 +13,27 @@ private fun internalReduce(action: Action, state: AppState): HomeState { if (action !is HomeAction) return state.homeState - var homeState = state.homeState + var state = state.homeState when (action) { - is HomeAction.CheckIfFirstLaunch.Result -> { - homeState = homeState.copy(firstLaunch = action.firstLaunch) + is HomeAction.SetFragmentShareTransition -> { + state = state.copy(shareTransition = action.shareTransition) + } + is HomeAction.SetTermsOfUseState -> { + state = state.copy(isDisclaimerAccepted = action.isDisclaimerAccepted) + } + is HomeAction.SetOpenUrl -> { + state = state.copy(openUrl = action.url) + } + is HomeAction.ChangeScanCardButtonState -> { + state = state.copy(btnScanState = action.state) } is HomeAction.ShowDialog.ScanFails -> { - homeState = homeState.copy(dialog = HomeDialog.ScanFailsDialog) + state = state.copy(dialog = HomeDialog.ScanFailsDialog) } is HomeAction.HideDialog -> { - homeState = homeState.copy(dialog = null) + state = state.copy(dialog = null) } } - return homeState + return state } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt index c9fce9b492..9a261cd1bf 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt @@ -1,13 +1,19 @@ package com.tangem.tap.features.home.redux +import com.tangem.tap.common.entities.IndeterminateProgressButton import com.tangem.tap.common.redux.global.StateDialog +import com.tangem.tap.common.redux.navigation.FragmentShareTransition +import com.tangem.tap.features.send.redux.states.ButtonState import org.rekotlin.StateType data class HomeState( - val firstLaunch: Boolean = true, - val dialog: StateDialog? = null + val openUrl: String? = null, + val isDisclaimerAccepted: Boolean = false, + val shareTransition: FragmentShareTransition? = null, + val btnScanState: IndeterminateProgressButton = IndeterminateProgressButton(ButtonState.ENABLED), + val dialog: StateDialog? = null, ) : StateType -sealed class HomeDialog: StateDialog { - object ScanFailsDialog: HomeDialog() +sealed class HomeDialog : StateDialog { + object ScanFailsDialog : HomeDialog() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingService.kt b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingService.kt new file mode 100644 index 0000000000..c54cfc75e8 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingService.kt @@ -0,0 +1,168 @@ +package com.tangem.tap.features.onboarding + +import com.tangem.blockchain.common.AmountType +import com.tangem.blockchain.common.WalletManager +import com.tangem.blockchain.common.WalletManagerFactory +import com.tangem.common.card.Card +import com.tangem.common.extensions.guard +import com.tangem.common.services.Result +import com.tangem.operations.attestation.OnlineCardVerifier +import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.domain.TapWalletManager +import com.tangem.tap.domain.TapWorkarounds.isTangemNote +import com.tangem.tap.domain.TapWorkarounds.isTangemWallet +import com.tangem.tap.domain.UrlBitmapLoader +import com.tangem.tap.domain.extensions.getArtworkUrl +import com.tangem.tap.domain.extensions.hasWallets +import com.tangem.tap.domain.extensions.makePrimaryWalletManager +import com.tangem.tap.domain.tasks.ScanNoteResponse +import com.tangem.tap.domain.twins.isTangemTwin +import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteAction +import com.tangem.tap.features.onboarding.redux.OnboardingData +import com.tangem.tap.features.wallet.redux.Artwork +import com.tangem.tap.persistence.UsedCardsPrefStorage +import com.tangem.tap.store +import java.math.BigDecimal + +/** +[REDACTED_AUTHOR] + */ +class OnboardingService( + private val tapWalletManager: TapWalletManager, + private val cardInfoStorage: UsedCardsPrefStorage +) { + + var onReadyToProceed: ((OnboardingData) -> Unit)? = null + set(value) { + field = value + onboardingDataLinker.onReady = { value?.invoke(it) } + } + var onFailedToProceedToOnboardingCase: (suspend (ScanNoteResponse) -> Unit)? = null + var onError: ((String) -> Unit)? = null + + private val onboardingDataLinker = OnboardingDataLinker() + + suspend fun onSuccess(response: ScanNoteResponse) { + onboardingDataLinker.scanNoteResponse = response + val card = response.card + val hasWallets = card.hasWallets() + + if (!card.isTangemNote() && !card.isTangemWallet() && !card.isTangemTwin()) { + if (hasWallets) { + cardInfoStorage.activated(card.cardId) + onFailedToProceedToOnboardingCase?.invoke(response) + return + } else { + cardInfoStorage.activated(card.cardId) // вызвать только после того, как был создан кошелек + onboardingDataLinker.navigateTo = AppScreen.OnboardingOther + } + } + if (cardInfoStorage.wasActivated(card.cardId)) { + onFailedToProceedToOnboardingCase?.invoke(response) + return + } + + loadCardImage(card) { onboardingDataLinker.cardArtwork = it } + + if (hasWallets) { + val walletManagerFactory = tapWalletManager.walletManagerFactory + when { + card.isTangemNote() -> loadBalanceNote(walletManagerFactory, response) + card.isTangemWallet() -> loadBalanceWallet(walletManagerFactory, response) + card.isTangemTwin() -> loadBalanceTwins(walletManagerFactory, response) + } + } else { + onboardingDataLinker.valueOfWalletAmount = BigDecimal.ZERO + } + + onboardingDataLinker.navigateTo = when { + card.isTangemNote() -> AppScreen.OnboardingNote + card.isTangemWallet() -> AppScreen.OnboardingWallet + card.isTangemTwin() -> AppScreen.OnboardingTwins + else -> AppScreen.OnboardingOther + } + } + + private suspend fun loadBalanceNote(walletManagerFactory: WalletManagerFactory, response: ScanNoteResponse) { + val walletManager: WalletManager = walletManagerFactory.makePrimaryWalletManager(response).guard { + onError?.invoke("Can't proceed: primary wallet manager didn't create") + return + } + walletManager.update() + store.dispatch(OnboardingNoteAction.SetWalletManager(walletManager)) + + val wallet = walletManager.wallet + val valueOfAmount = wallet.amounts[AmountType.Coin]?.value + if (valueOfAmount == null) { + onError?.invoke("Amount can't be NULL") + return + } + + onboardingDataLinker.valueOfWalletAmount = valueOfAmount + } + + private suspend fun loadBalanceWallet(walletManagerFactory: WalletManagerFactory, response: ScanNoteResponse) { + + } + + private suspend fun loadBalanceTwins(walletManagerFactory: WalletManagerFactory, response: ScanNoteResponse) { + + } + + private suspend fun loadCardImage(card: Card, callback: (Artwork?) -> Unit) { + fun loadBitmap(url: String, callback: (Artwork?) -> Unit) { + UrlBitmapLoader().loadBitmap(url) { + when (it) { + is Result.Success -> callback(Artwork(url, it.data)) + is Result.Failure -> callback(null) + } + } + } + + when (val cardInfoResult = OnlineCardVerifier().getCardInfo(card.cardId, card.cardPublicKey)) { + is Result.Success -> { + val artworkUrl = card.getArtworkUrl(cardInfoResult.data.artwork?.id) ?: Artwork.DEFAULT_IMG_URL + loadBitmap(artworkUrl, callback) + } + is Result.Failure -> loadBitmap(Artwork.DEFAULT_IMG_URL, callback) + } + } + +} + +class OnboardingDataLinker { + var onReady: ((OnboardingData) -> Unit)? = null + + internal var scanNoteResponse: ScanNoteResponse? = null + set(value) { + field = value + tryToProceed() + } + internal var cardArtwork: Artwork? = null + set(value) { + field = value + tryToProceed() + } + internal var valueOfWalletAmount: BigDecimal? = null + set(value) { + field = value + tryToProceed() + } + internal var navigateTo: AppScreen? = null + set(value) { + field = value + tryToProceed() + } + + private fun tryToProceed() { + if (!isReady()) return + + val data = OnboardingData(scanNoteResponse!!, cardArtwork!!, valueOfWalletAmount, navigateTo!!) + onReady?.invoke(data) + } + + private fun isReady(): Boolean { + val objList = mutableListOf(scanNoteResponse, cardArtwork, valueOfWalletAmount, navigateTo) + return !objList.any { it == null } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt new file mode 100644 index 0000000000..730c8760fc --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt @@ -0,0 +1,133 @@ +package com.tangem.tap.features.onboarding.products.note + +import android.os.Bundle +import android.view.View +import androidx.core.view.isVisible +import androidx.transition.TransitionInflater +import com.tangem.tap.common.extensions.addOnBackPressedDispatcher +import com.tangem.tap.common.extensions.beginDelayedTransition +import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.common.redux.navigation.NavigationAction +import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteAction +import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteState +import com.tangem.tap.features.onboarding.redux.OnboardingAction +import com.tangem.tap.features.onboarding.redux.OnboardingNoteStep +import com.tangem.tap.features.send.BaseStoreFragment +import com.tangem.tap.store +import com.tangem.wallet.R +import kotlinx.android.synthetic.main.fragment_onboarding.* +import org.rekotlin.StoreSubscriber + +/** +[REDACTED_AUTHOR] + */ +class OnboardingNoteFragment : BaseStoreFragment(R.layout.fragment_onboarding), StoreSubscriber { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + activity?.addOnBackPressedDispatcher { + store.dispatch(NavigationAction.PopBackTo()) + store.dispatch(OnboardingAction.SetInitialStepOfScreen(OnboardingNoteStep.None)) + } + + val inflater = TransitionInflater.from(requireContext()) + enterTransition = inflater.inflateTransition(R.transition.fade) + exitTransition = inflater.inflateTransition(R.transition.fade) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + store.dispatch(OnboardingNoteAction.Init) + + imv_front_card.setImageBitmap(store.state.onboardingState.onboardingData?.cardArtwork?.artwork) + } + + override fun subscribeToStore() { + store.subscribe(this) { state -> + state.skipRepeats { oldState, newState -> + oldState.onboardingNoteState == newState.onboardingNoteState + }.select { it.onboardingNoteState } + } + } + + override fun newState(state: OnboardingNoteState) { + if (activity == null) return + + pb_state.max = state.steps.size - 1 + + onboarding_main_container.beginDelayedTransition() + when (state.currentStep) { + OnboardingNoteStep.None -> setupNoneState(state) + OnboardingNoteStep.CreateWallet -> setupCreateWalletState(state) + OnboardingNoteStep.TopUpWallet -> setupTopUpWalletState(state) + OnboardingNoteStep.Done -> setupSuccessState(state) + } + } + + private fun setupNoneState(state: OnboardingNoteState) { + pb_state.progress = state.progress + + btn_main_action.isVisible = true + btn_alternative_action.isVisible = true + btn_check_balance.isVisible = true + tv_header.isVisible = true + tv_body.isVisible = true + + btn_main_action.text = "NULL" + btn_alternative_action.text = "NULL" + btn_check_balance.text = "NULL balance" + tv_header.text = "NULL" + tv_body.text = "NULL" + } + + private fun setupCreateWalletState(state: OnboardingNoteState) { + pb_state.progress = state.progress + + btn_main_action.setText(R.string.onboarding_create_wallet_button_create_wallet) + btn_main_action.setOnClickListener { store.dispatch(OnboardingNoteAction.CreateWallet) } + + btn_alternative_action.setText(R.string.onboarding_button_what_does_it_mean) + btn_alternative_action.setOnClickListener { } + + btn_check_balance.isVisible = false + btn_check_balance.setOnClickListener { } + + tv_header.setText(R.string.onboarding_create_wallet_header) + tv_body.setText(R.string.onboarding_create_wallet_body) + } + + private fun setupTopUpWalletState(state: OnboardingNoteState) { + pb_state.progress = state.progress + + btn_main_action.setText(R.string.onboarding_top_up_button_but_crypto) + btn_main_action.setOnClickListener { store.dispatch(OnboardingNoteAction.TopUp) } + + btn_alternative_action.setText(R.string.onboarding_top_up_button_show_wallet_address) + btn_alternative_action.setOnClickListener { } + + btn_check_balance.isVisible = false + btn_check_balance.setOnClickListener { } + + tv_header.setText(R.string.onboarding_top_up_header) + tv_body.setText(R.string.onboarding_top_up_body) + } + + private fun setupSuccessState(state: OnboardingNoteState) { + pb_state.progress = state.progress + + btn_main_action.setText(R.string.onboarding_done_button_continue) + btn_main_action.setOnClickListener { store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) } + + btn_alternative_action.isVisible = false + btn_alternative_action.setText("") + btn_alternative_action.setOnClickListener { } + + btn_check_balance.isVisible = false + btn_check_balance.setOnClickListener { } + + tv_header.setText(R.string.onboarding_done_header) + tv_body.setText(R.string.onboarding_done_body) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt new file mode 100644 index 0000000000..bab556d68b --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt @@ -0,0 +1,16 @@ +package com.tangem.tap.features.onboarding.products.note.redux + +import com.tangem.blockchain.common.WalletManager +import org.rekotlin.Action + +/** +[REDACTED_AUTHOR] + */ +sealed class OnboardingNoteAction : Action { + object Init : OnboardingNoteAction() + object CreateWallet : OnboardingNoteAction() + object TopUp : OnboardingNoteAction() + object Done : OnboardingNoteAction() + + data class SetWalletManager(val walletManager: WalletManager) : OnboardingNoteAction() +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt new file mode 100644 index 0000000000..7f5ee16c3f --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt @@ -0,0 +1,72 @@ +package com.tangem.tap.features.onboarding.products.note.redux + +import com.tangem.common.CompletionResult +import com.tangem.tap.common.redux.AppState +import com.tangem.tap.features.onboarding.redux.OnboardingAction +import com.tangem.tap.features.onboarding.redux.OnboardingNoteStep +import com.tangem.tap.scope +import com.tangem.tap.store +import com.tangem.tap.tangemSdkManager +import kotlinx.coroutines.launch +import org.rekotlin.Action +import org.rekotlin.Middleware + +class OnboardingNoteMiddleware { + companion object { + private const val CARD_SHOP_URI = + "https://shop.tangem.com/?afmc=1i&utm_campaign=1i&utm_source=leaddyno&utm_medium=affiliate" + val handler = onboardingNoteMiddleware + } +} + +private val onboardingNoteMiddleware: Middleware = { dispatch, state -> + { next -> + { action -> + handleMainOnboardingAction(action, ::handleNoteAction) + next(action) + } + } +} + +private fun handleMainOnboardingAction(action: Action, isNotMainAction: (Action) -> Unit) { + val action = action as? OnboardingAction ?: isNotMainAction(action) + + when (action) { + } +} + +private fun handleNoteAction(action: Action) { + val onboardingState = store.state.onboardingState + val noteState = store.state.onboardingNoteState + + when (action) { + is OnboardingNoteAction.SetWalletManager -> { + } + is OnboardingNoteAction.Init -> { + val step = if (noteState.walletManager == null) { + OnboardingNoteStep.CreateWallet + } else { + OnboardingNoteStep.TopUpWallet + } + store.dispatch(OnboardingAction.SetInitialStepOfScreen(step)) + } + is OnboardingNoteAction.CreateWallet -> { + scope.launch { + val scanNoteResponse = onboardingState.onboardingData?.scanNoteResponse + val result = tangemSdkManager.createWallet(scanNoteResponse?.card?.cardId) + when (result) { + is CompletionResult.Success -> { +// val scanNoteResponse = scanNoteResponse!!.copy(card = result.data) +// store.dispatch(OnboardingAction.SetScanNoteResponse(scanNoteResponse)) + } + is CompletionResult.Failure -> { + store.dispatch(OnboardingAction.Error("Failed to create wallet")) + } + } + } + } + is OnboardingNoteAction.TopUp -> { + + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt new file mode 100644 index 0000000000..b6a50be846 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt @@ -0,0 +1,38 @@ +package com.tangem.tap.features.onboarding.products.note.redux + +import com.tangem.tap.common.redux.AppState +import com.tangem.tap.features.onboarding.redux.OnboardingAction +import com.tangem.tap.features.onboarding.redux.OnboardingNoteStep +import org.rekotlin.Action + +class OnboardingNoteReducer { + companion object { + fun reduce(action: Action, state: AppState): OnboardingNoteState = internalReduce(action, state) + } +} + +private fun internalReduce(action: Action, appState: AppState): OnboardingNoteState { + var state = appState.onboardingNoteState + + when (action) { + is OnboardingAction.SetInitialStepOfScreen -> { + (action.step as? OnboardingNoteStep)?.let { + if (it != state.currentStep && state.steps.contains(it)) { + state = state.copy(currentStep = it) + } + } + } + OnboardingAction.SwitchToNextStep -> { + val progress = state.progress + if (progress >= 0 && progress != state.steps.size - 1) { + state = state.copy(currentStep = state.steps[progress + 1]) + } + } + is OnboardingNoteAction.SetWalletManager -> { + state = state.copy(walletManager = action.walletManager) + } + + } + + return state +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt new file mode 100644 index 0000000000..ae5677779f --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt @@ -0,0 +1,18 @@ +package com.tangem.tap.features.onboarding.products.note.redux + +import com.tangem.blockchain.common.WalletManager +import com.tangem.tap.features.onboarding.redux.OnboardingNoteStep +import org.rekotlin.StateType + +/** +[REDACTED_AUTHOR] + */ +data class OnboardingNoteState( + val walletManager: WalletManager? = null, + val currentStep: OnboardingNoteStep = OnboardingNoteStep.None, + val steps: List = OnboardingNoteStep.values().toList(), +) : StateType { + + val progress: Int + get() = steps.indexOf(currentStep) +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/old/OnboardingOtherCardsFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/old/OnboardingOtherCardsFragment.kt new file mode 100644 index 0000000000..cada557578 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/old/OnboardingOtherCardsFragment.kt @@ -0,0 +1,50 @@ +package com.tangem.tap.features.onboarding.products.old + +import android.os.Bundle +import android.view.View +import androidx.transition.TransitionInflater +import com.tangem.tap.common.extensions.addOnBackPressedDispatcher +import com.tangem.tap.common.redux.navigation.NavigationAction +import com.tangem.tap.features.onboarding.redux.OnboardingAction +import com.tangem.tap.features.onboarding.redux.OnboardingOtherCardsState +import com.tangem.tap.features.onboarding.redux.OnboardingOtherStep +import com.tangem.tap.features.send.BaseStoreFragment +import com.tangem.tap.store +import com.tangem.wallet.R +import org.rekotlin.StoreSubscriber + +/** +[REDACTED_AUTHOR] + */ +class OnboardingOtherCardsFragment : BaseStoreFragment(R.layout.fragment_onboarding), StoreSubscriber { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + activity?.addOnBackPressedDispatcher { + store.dispatch(NavigationAction.PopBackTo()) + store.dispatch(OnboardingAction.SetInitialStepOfScreen(OnboardingOtherStep.Done)) + } + + val inflater = TransitionInflater.from(requireContext()) + enterTransition = inflater.inflateTransition(R.transition.fade) + exitTransition = inflater.inflateTransition(R.transition.fade) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + } + + override fun subscribeToStore() { +// store.subscribe(this) { state -> +// state.skipRepeats { oldState, newState -> +// oldState.onboardingOtherState == newState.onboardingOtherState +// }.select { it.onboardingOtherState } +// } + } + + override fun newState(state: OnboardingOtherCardsState) { + if (activity == null) return + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/OnboardingWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/OnboardingWalletFragment.kt new file mode 100644 index 0000000000..5b2eba2423 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/OnboardingWalletFragment.kt @@ -0,0 +1,52 @@ +package com.tangem.tap.features.onboarding.products.wallet + +import android.os.Bundle +import android.view.View +import androidx.transition.TransitionInflater +import com.tangem.tap.common.extensions.addOnBackPressedDispatcher +import com.tangem.tap.common.redux.navigation.NavigationAction +import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletState +import com.tangem.tap.features.onboarding.redux.OnboardingAction +import com.tangem.tap.features.onboarding.redux.OnboardingOtherStep +import com.tangem.tap.features.send.BaseStoreFragment +import com.tangem.tap.store +import com.tangem.wallet.R +import org.rekotlin.StoreSubscriber + +/** +[REDACTED_AUTHOR] + */ +class OnboardingWalletFragment : BaseStoreFragment(R.layout.fragment_onboarding_wallet), StoreSubscriber { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + activity?.addOnBackPressedDispatcher { + store.dispatch(NavigationAction.PopBackTo()) + store.dispatch(OnboardingAction.SetInitialStepOfScreen(OnboardingOtherStep.Done)) + } + + val inflater = TransitionInflater.from(requireContext()) + enterTransition = inflater.inflateTransition(R.transition.fade) + exitTransition = inflater.inflateTransition(R.transition.fade) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + store.dispatch(OnboardingAction.OnboardingOtherAction.Init) + } + + override fun subscribeToStore() { + store.subscribe(this) { state -> + state.skipRepeats { oldState, newState -> + oldState.onboardingWalletState == newState.onboardingWalletState + }.select { it.onboardingWalletState } + } + } + + override fun newState(state: OnboardingWalletState) { + if (activity == null) return + + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..5cfd213d57 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt @@ -0,0 +1,19 @@ +package com.tangem.tap.features.onboarding.products.wallet.redux + +import org.rekotlin.Action + +/** +[REDACTED_AUTHOR] + */ +sealed class OnboardingWalletAction : Action { + object Init : OnboardingWalletAction() + object CreateWallet : OnboardingWalletAction() + object TopUp : OnboardingWalletAction() + object Done : OnboardingWalletAction() + + object ProceedBackup : OnboardingWalletAction() + + sealed class Backup : Action { + + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..c442090de4 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt @@ -0,0 +1,18 @@ +package com.tangem.tap.features.onboarding.products.wallet.redux + +import com.tangem.tap.common.redux.AppState +import org.rekotlin.Middleware + +class OnboardingWalletMiddleware { + companion object { + val handler = onboardingWalletMiddleware + } +} + +private val onboardingWalletMiddleware: Middleware = { dispatch, state -> + { next -> + { action -> + next(action) + } + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..427f44048e --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt @@ -0,0 +1,20 @@ +package com.tangem.tap.features.onboarding.products.wallet.redux + +import com.tangem.tap.common.redux.AppState +import org.rekotlin.Action + +class OnboardingWalletReducer { + companion object { + fun reduce(action: Action, state: AppState): OnboardingWalletState = internalReduce(action, state) + } +} + +private fun internalReduce(action: Action, appState: AppState): OnboardingWalletState { + var state = appState.onboardingWalletState + + when (action) { + + } + + return state +} \ No newline at end of file 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 new file mode 100644 index 0000000000..9b8579171a --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt @@ -0,0 +1,15 @@ +package com.tangem.tap.features.onboarding.products.wallet.redux + +import com.tangem.tap.features.onboarding.redux.OnboardingStep +import org.rekotlin.StateType + +/** +[REDACTED_AUTHOR] + */ +data class OnboardingWalletState( + val any: String? = null +) : StateType + +enum class OnboardingWalletStep : OnboardingStep { + None, CreateWallet, TopUpWallet, Backup, Done +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingAction.kt new file mode 100644 index 0000000000..7a904b47ee --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingAction.kt @@ -0,0 +1,22 @@ +package com.tangem.tap.features.onboarding.redux + +import org.rekotlin.Action + +/** +[REDACTED_AUTHOR] + */ +sealed class OnboardingAction : Action { + + data class SetInitialStepOfScreen(val step: OnboardingStep) : OnboardingAction() + object SwitchToNextStep : OnboardingAction() + + data class SetData(val data: OnboardingData) : OnboardingAction() + + sealed class OnboardingOtherAction : Action { + object Init : OnboardingOtherAction() + object CreateWallet : OnboardingOtherAction() + object Done : OnboardingOtherAction() + } + + class Error(val message: String) : OnboardingAction() +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingMiddleware.kt new file mode 100644 index 0000000000..ea373a5f41 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingMiddleware.kt @@ -0,0 +1,22 @@ +package com.tangem.tap.features.onboarding.redux + +import com.tangem.tap.common.redux.AppState +import com.tangem.tap.store +import org.rekotlin.Middleware + +class OnboardingMiddleware { + companion object { + val handler = onboardingMiddleware + } +} + +private val onboardingMiddleware: Middleware = { dispatch, state -> + { next -> + { action -> + val onboardingState = store.state.onboardingState + when (action) { + } + next(action) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingReducer.kt new file mode 100644 index 0000000000..5ee024922d --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingReducer.kt @@ -0,0 +1,24 @@ +package com.tangem.tap.features.onboarding.redux + +import com.tangem.tap.common.redux.AppState +import org.rekotlin.Action + +class OnboardingReducer { + companion object { + fun reduce(action: Action, state: AppState): OnboardingState = internalReduce(action, state) + } +} + +private fun internalReduce(action: Action, appState: AppState): OnboardingState { + + if (action !is OnboardingAction) return appState.onboardingState + + var state = appState.onboardingState + when (action) { + is OnboardingAction.SetData -> { + state = state.copy(onboardingData = action.data) + } + } + + return state +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingState.kt new file mode 100644 index 0000000000..2a4909de43 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingState.kt @@ -0,0 +1,11 @@ +package com.tangem.tap.features.onboarding.redux + +import org.rekotlin.StateType + +data class OnboardingState( + val onboardingData: OnboardingData? = null, +) : StateType + +data class OnboardingOtherCardsState( + val any: String? = null +) : StateType \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/redux/StateSupportClasses.kt b/app/src/main/java/com/tangem/tap/features/onboarding/redux/StateSupportClasses.kt new file mode 100644 index 0000000000..cc422410a5 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/redux/StateSupportClasses.kt @@ -0,0 +1,26 @@ +package com.tangem.tap.features.onboarding.redux + +import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.domain.tasks.ScanNoteResponse +import com.tangem.tap.features.wallet.redux.Artwork +import java.math.BigDecimal + +/** +[REDACTED_AUTHOR] + */ +data class OnboardingData( + val scanNoteResponse: ScanNoteResponse, + val cardArtwork: Artwork, + val balance: BigDecimal?, + val onboardingScreen: AppScreen +) + +interface OnboardingStep + +enum class OnboardingNoteStep : OnboardingStep { + None, CreateWallet, TopUpWallet, Done +} + +enum class OnboardingOtherStep : OnboardingStep { + CreateWallet, Done +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/TwinsOnboardingFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/OnboardingTwinsFragment.kt similarity index 97% rename from app/src/main/java/com/tangem/tap/features/wallet/ui/TwinsOnboardingFragment.kt rename to app/src/main/java/com/tangem/tap/features/wallet/ui/OnboardingTwinsFragment.kt index 0d3abe02e2..6bfd2f28df 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/TwinsOnboardingFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/OnboardingTwinsFragment.kt @@ -16,7 +16,7 @@ import com.tangem.wallet.R import kotlinx.android.synthetic.main.fragment_twin_cards.* import kotlinx.android.synthetic.main.layout_twin_cards.* -class TwinsOnboardingFragment : Fragment(R.layout.fragment_twin_cards) { +class OnboardingTwinsFragment : Fragment(R.layout.fragment_twin_cards) { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt index b80b989b70..aac75ef60b 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt @@ -107,7 +107,7 @@ class SingleWalletView : WalletView { tv_twin_card_number.hide() } if (twinCardsState?.showTwinOnboarding == true) { - store.dispatch(NavigationAction.NavigateTo(AppScreen.TwinsOnboarding)) + store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingTwins)) } diff --git a/app/src/main/res/drawable/bg_circle_gray.xml b/app/src/main/res/drawable/bg_circle_gray.xml new file mode 100644 index 0000000000..94a585da83 --- /dev/null +++ b/app/src/main/res/drawable/bg_circle_gray.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_home_card_back.xml b/app/src/main/res/drawable/ic_home_card_back.xml new file mode 100644 index 0000000000..8a71b20ce5 --- /dev/null +++ b/app/src/main/res/drawable/ic_home_card_back.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_home_card_front.xml b/app/src/main/res/drawable/ic_home_card_front.xml new file mode 100644 index 0000000000..3bbf5a93dd --- /dev/null +++ b/app/src/main/res/drawable/ic_home_card_front.xml @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 33157a5fe6..1ebc971c29 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -2,95 +2,126 @@ + android:layout_height="match_parent" + app:layoutDescription="@xml/fragment_welcome_scene"> - - - + android:src="@drawable/ic_home_card_back" + android:translationY="-55dp" + app:layout_constraintEnd_toEndOf="@+id/imv_front_card" + app:layout_constraintStart_toStartOf="@+id/imv_front_card" + app:layout_constraintTop_toBottomOf="@+id/imv_front_card" /> - - - - + android:src="@drawable/ic_home_card_front" + android:transitionName="imv_front_card" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0.496" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/view" /> + android:layout_marginBottom="14dp" + android:fontFamily="sans-serif-medium" + android:text="@string/home_welcome_header" + android:textColor="#060606" + android:textSize="24sp" + app:layout_constraintBottom_toTopOf="@+id/tv_home_body" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0.508" + app:layout_constraintStart_toStartOf="parent" /> + + + + + + + + + + + app:layout_constraintGuide_percent="0.5" + app:layout_constraintTop_toTopOf="parent" /> - + app:layout_constraintHorizontal_bias="0.498" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> - \ No newline at end of file + + diff --git a/app/src/main/res/layout/fragment_onboarding.xml b/app/src/main/res/layout/fragment_onboarding.xml new file mode 100644 index 0000000000..710cf8a7c5 --- /dev/null +++ b/app/src/main/res/layout/fragment_onboarding.xml @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_onboarding_wallet.xml b/app/src/main/res/layout/fragment_onboarding_wallet.xml new file mode 100644 index 0000000000..77d9ef65f8 --- /dev/null +++ b/app/src/main/res/layout/fragment_onboarding_wallet.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file From 2f1c8959175c10143839865e9a32313d718dba11 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 23 Sep 2021 12:51:05 +0300 Subject: [PATCH 042/189] Updated on 2026-08-14 --- app/src/main/java/com/tangem/tap/MainActivity.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index 89ede4d80d..37682e2bc8 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -17,7 +17,6 @@ import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.TangemSdkManager import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction -import com.tangem.tap.features.home.redux.HomeAction import com.tangem.wallet.R import kotlinx.android.synthetic.main.activity_main.* import kotlinx.coroutines.CoroutineExceptionHandler @@ -73,10 +72,11 @@ class MainActivity : AppCompatActivity(), SnackbarHandler { override fun onResume() { super.onResume() notificationsHandler = NotificationsHandler(fragment_container) - if (supportFragmentManager.backStackEntryCount == 0 || - store.state.globalState.scanNoteResponse == null - ) { - store.dispatch(HomeAction.CheckIfFirstLaunch) + + val backStackIsEmpty = supportFragmentManager.backStackEntryCount == 0 + val isScannedBefore = store.state.globalState.scanNoteResponse != null + val isOnboardingStarted = store.state.onboardingState.onboardingData?.scanNoteResponse != null + if ((backStackIsEmpty || isScannedBefore) && !isOnboardingStarted) { store.dispatch(NavigationAction.NavigateTo(AppScreen.Home)) } intentHandler.handleIntent(intent) @@ -107,7 +107,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler { if (snackbar != null) return snackbar = Snackbar.make( - fragment_container, getString(text), Snackbar.LENGTH_INDEFINITE + fragment_container, getString(text), Snackbar.LENGTH_INDEFINITE ) if (buttonTitle != null && action != null) { snackbar?.setAction(getString(buttonTitle), action) From d8d81aee62eeaa1934c0de32ffdd5fd75267fc8c Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 23 Sep 2021 12:57:44 +0300 Subject: [PATCH 043/189] Updated on 2026-08-14 --- app/src/main/res/layout/fragment_home.xml | 3 +-- app/src/main/res/layout/fragment_onboarding.xml | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 1ebc971c29..f8d352de67 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -2,8 +2,7 @@ + android:layout_height="match_parent"> Date: Fri, 24 Sep 2021 17:44:21 +0300 Subject: [PATCH 044/189] Updated on 2026-08-14 --- .../main/java/com/tangem/tap/common/extensions/Navigation.kt | 1 + app/src/main/java/com/tangem/tap/common/images/Picasso.kt | 1 + app/src/main/java/com/tangem/tap/domain/UrlBitmapLoader.kt | 4 ++++ .../main/java/com/tangem/tap/features/home/HomeFragment.kt | 2 +- .../java/com/tangem/tap/features/home/redux/HomeMiddleware.kt | 3 ++- 5 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt index c7e04cf44f..3a119fd683 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt @@ -36,6 +36,7 @@ fun FragmentActivity.openFragment( fgShareTransition?.apply { fragment.sharedElementEnterTransition = enterTransitionSet fragment.sharedElementReturnTransition = exitTransitionSet + transaction.setReorderingAllowed(true) shareElements.forEach { shareElement -> shareElement.wView.get()?.let { view -> transaction.addSharedElement(view, shareElement.name) diff --git a/app/src/main/java/com/tangem/tap/common/images/Picasso.kt b/app/src/main/java/com/tangem/tap/common/images/Picasso.kt index c3b1d139a3..df364efdb7 100644 --- a/app/src/main/java/com/tangem/tap/common/images/Picasso.kt +++ b/app/src/main/java/com/tangem/tap/common/images/Picasso.kt @@ -23,6 +23,7 @@ class PicassoHelper { private fun getOkHttpForPicasso(application: Application): OkHttpClient { val okHttpBuilder = OkHttpClient.Builder() okHttpBuilder.cache(Cache(File(application.filesDir, "artworks"), Long.MAX_VALUE)) + okHttpBuilder.callTimeout(15000, TimeUnit.MILLISECONDS) okHttpBuilder.addInterceptor { chain -> val cacheControl = CacheControl.Builder() .maxStale(KEEP_CACHE_MAX_DAYS, TimeUnit.DAYS) diff --git a/app/src/main/java/com/tangem/tap/domain/UrlBitmapLoader.kt b/app/src/main/java/com/tangem/tap/domain/UrlBitmapLoader.kt index d09d8ed3bc..1f4897a487 100644 --- a/app/src/main/java/com/tangem/tap/domain/UrlBitmapLoader.kt +++ b/app/src/main/java/com/tangem/tap/domain/UrlBitmapLoader.kt @@ -5,6 +5,7 @@ import android.graphics.drawable.Drawable import com.squareup.picasso.Picasso import com.squareup.picasso.Target import com.tangem.common.services.Result +import timber.log.Timber /** [REDACTED_AUTHOR] @@ -26,13 +27,16 @@ open class DownloadTarget( ) : Target { override fun onBitmapLoaded(bitmap: Bitmap, from: Picasso.LoadedFrom) { + Timber.d("onBitmapLoaded") callback(Result.Success(bitmap)) } override fun onBitmapFailed(e: java.lang.Exception?, errorDrawable: Drawable?) { + Timber.d("onBitmapFailed") callback.invoke(Result.Failure(e)) } override fun onPrepareLoad(placeHolderDrawable: Drawable?) { + Timber.d("onPrepareLoad") } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt index 168941d2c5..457509098f 100644 --- a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt @@ -41,7 +41,7 @@ class HomeFragment : Fragment(R.layout.fragment_home), StoreSubscriber() - transitionViews.add(ShareElement(WeakReference(imv_front_card), "imv_front_card")) + transitionViews.add(ShareElement(WeakReference(imv_front_card), imv_front_card.transitionName)) val shareTransition = FragmentShareTransition(transitionViews, DetailsTransition(), DetailsTransition()) store.dispatch(HomeAction.SetFragmentShareTransition(shareTransition)) 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 0a6e7945f4..bbb37ceded 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 @@ -61,7 +61,8 @@ private fun launchOnboardingService() { service.onReadyToProceed = { store.dispatch(OnboardingAction.SetData(it)) changeButtonState(ButtonState.ENABLED) - store.dispatch(NavigationAction.NavigateTo(it.onboardingScreen)) + val sharedTransition = store.state.homeState.shareTransition + store.dispatch(NavigationAction.NavigateTo(it.onboardingScreen, fragmentShareTransition = sharedTransition)) } service.onFailedToProceedToOnboardingCase = { From ed8aa753ce1d51873744e6c9a0239fedf08eb754 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 24 Sep 2021 18:28:40 +0300 Subject: [PATCH 045/189] Updated on 2026-08-14 --- .../redux/navigation/NavigationAction.kt | 27 +++++-------------- .../transitions/OnboardingTransitions.kt | 27 +++++++++++++++++++ .../tangem/tap/features/home/HomeFragment.kt | 11 +++++--- 3 files changed, 41 insertions(+), 24 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/common/transitions/OnboardingTransitions.kt diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt index 375b16e785..d77f97b263 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt @@ -2,18 +2,15 @@ package com.tangem.tap.common.redux.navigation import android.view.View import androidx.fragment.app.FragmentActivity -import androidx.transition.ChangeBounds -import androidx.transition.ChangeImageTransform -import androidx.transition.ChangeTransform import androidx.transition.TransitionSet import org.rekotlin.Action import java.lang.ref.WeakReference sealed class NavigationAction : Action { data class NavigateTo( - val screen: AppScreen, - val addToBackstack: Boolean = true, - val fragmentShareTransition: FragmentShareTransition? = null + val screen: AppScreen, + val addToBackstack: Boolean = true, + val fragmentShareTransition: FragmentShareTransition? = null ) : NavigationAction() data class PopBackTo(val screen: AppScreen? = null) : NavigationAction() @@ -25,19 +22,9 @@ sealed class NavigationAction : Action { } data class FragmentShareTransition( - val shareElements: List, - val enterTransitionSet: TransitionSet, - val exitTransitionSet: TransitionSet + val shareElements: List, + val enterTransitionSet: TransitionSet, + val exitTransitionSet: TransitionSet ) -data class ShareElement(val wView: WeakReference, val name: String) - -//TODO: remove transition example -class DetailsTransition : TransitionSet() { - init { - ordering = ORDERING_TOGETHER; - addTransition(ChangeBounds()) - .addTransition(ChangeTransform()) - .addTransition(ChangeImageTransform()) - } -} \ No newline at end of file +data class ShareElement(val wView: WeakReference, val name: String) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/transitions/OnboardingTransitions.kt b/app/src/main/java/com/tangem/tap/common/transitions/OnboardingTransitions.kt new file mode 100644 index 0000000000..7d9334e123 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/transitions/OnboardingTransitions.kt @@ -0,0 +1,27 @@ +package com.tangem.tap.common.transitions + +import androidx.transition.ChangeBounds +import androidx.transition.ChangeImageTransform +import androidx.transition.TransitionSet + +/** +[REDACTED_AUTHOR] + */ +class FrontCardEnterTransition : TransitionSet() { + init { + ordering = ORDERING_TOGETHER; + addTransition(ChangeImageTransform()) + addTransition(ChangeBounds()) +// addTransition(Fade()) +// addTransition(Fade(Visibility.MODE_IN)) +// addTransition(ChangeTransform()) + } +} + +class FrontCardExitTransition : TransitionSet() { + init { + ordering = ORDERING_TOGETHER; + addTransition(ChangeImageTransform()) + addTransition(ChangeBounds()) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt index 457509098f..e9f86f005b 100644 --- a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt @@ -10,11 +10,12 @@ import androidx.transition.TransitionInflater import com.tangem.tap.common.extensions.safeStartActivity import com.tangem.tap.common.extensions.toast import com.tangem.tap.common.redux.global.StateDialog -import com.tangem.tap.common.redux.navigation.DetailsTransition import com.tangem.tap.common.redux.navigation.FragmentShareTransition import com.tangem.tap.common.redux.navigation.ShareElement import com.tangem.tap.common.toggleWidget.IndeterminateProgressButtonWidget import com.tangem.tap.common.toggleWidget.ViewStateWidget +import com.tangem.tap.common.transitions.FrontCardEnterTransition +import com.tangem.tap.common.transitions.FrontCardExitTransition import com.tangem.tap.features.home.redux.HomeAction import com.tangem.tap.features.home.redux.HomeDialog import com.tangem.tap.features.home.redux.HomeState @@ -40,9 +41,11 @@ class HomeFragment : Fragment(R.layout.fragment_home), StoreSubscriber() - transitionViews.add(ShareElement(WeakReference(imv_front_card), imv_front_card.transitionName)) - val shareTransition = FragmentShareTransition(transitionViews, DetailsTransition(), DetailsTransition()) + val shareTransition = FragmentShareTransition( + listOf(ShareElement(WeakReference(imv_front_card), imv_front_card.transitionName)), + FrontCardEnterTransition(), + FrontCardExitTransition() + ) store.dispatch(HomeAction.SetFragmentShareTransition(shareTransition)) store.dispatch(HomeAction.Init) From 63eaedff8aef5a5424f436ff4faaa66c53501e48 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sat, 25 Sep 2021 01:51:33 +0300 Subject: [PATCH 046/189] Updated on 2026-08-14 --- .../com/tangem/tap/common/DialogManager.kt | 43 +++++++++++-------- .../com/tangem/tap/common/extensions/Store.kt | 36 ++++++++++++++-- .../tangem/tap/common/redux/StateDialog.kt | 10 +++++ .../tap/common/redux/global/GlobalAction.kt | 14 ++++-- .../common/redux/global/GlobalMidlleware.kt | 6 +-- .../tap/common/redux/global/GlobalState.kt | 6 +-- .../redux/walletconnect/WalletConnectState.kt | 2 +- .../tangem/tap/features/home/HomeFragment.kt | 38 +--------------- .../tap/features/home/redux/HomeAction.kt | 14 +++--- .../tap/features/home/redux/HomeMiddleware.kt | 3 +- .../tap/features/home/redux/HomeReducer.kt | 9 ---- .../tap/features/home/redux/HomeState.kt | 15 ++----- .../features/send/redux/SendScreenAction.kt | 4 +- .../features/send/redux/states/SendState.kt | 2 +- .../tap/features/wallet/redux/WalletAction.kt | 1 - .../tap/features/wallet/redux/WalletState.kt | 9 ++-- .../wallet/redux/reducers/WalletReducer.kt | 37 ++++++++-------- .../wallet/ui/WalletDetailsFragment.kt | 2 +- .../wallet/ui/dialogs/ScanFailsDialog.kt | 8 +--- .../wallet/ui/wallet/MultiWalletView.kt | 6 +-- .../wallet/ui/wallet/SingleWalletView.kt | 5 +-- 21 files changed, 130 insertions(+), 140 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/common/redux/StateDialog.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 f58e132937..218c96776f 100644 --- a/app/src/main/java/com/tangem/tap/common/DialogManager.kt +++ b/app/src/main/java/com/tangem/tap/common/DialogManager.kt @@ -2,9 +2,13 @@ package com.tangem.tap.common import android.app.Dialog import android.content.Context +import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.common.redux.global.GlobalState import com.tangem.tap.features.details.redux.walletconnect.WalletConnectDialog import com.tangem.tap.features.details.ui.walletconnect.dialogs.* +import com.tangem.tap.features.onboarding.products.note.OnboardingAddressInfoDialog +import com.tangem.tap.features.onboarding.redux.OnboardingAction +import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog import com.tangem.tap.store import com.tangem.wallet.R import org.rekotlin.StoreSubscriber @@ -37,35 +41,40 @@ class DialogManager : StoreSubscriber { val context = context ?: return if (dialog != null) return - when (state.dialog) { + dialog = when (state.dialog) { + is AppDialog.ScanFailsDialog -> ScanFailsDialog.create(context) is WalletConnectDialog.UnsupportedCard -> - dialog = SimpleAlertDialog.create( - titleRes = R.string.wallet_connect, - messageRes = R.string.wallet_connect_scanner_error_no_ethereum_wallet, - context = context + SimpleAlertDialog.create( + titleRes = R.string.wallet_connect, + messageRes = R.string.wallet_connect_scanner_error_no_ethereum_wallet, + context = context ) is WalletConnectDialog.OpeningSessionRejected -> { - dialog = SimpleAlertDialog.create( - titleRes = R.string.wallet_connect, - messageRes = R.string.wallet_connect_same_wcuri, - context = context + SimpleAlertDialog.create( + titleRes = R.string.wallet_connect, + messageRes = R.string.wallet_connect_same_wcuri, + context = context ) } is WalletConnectDialog.SessionTimeout -> { - dialog = SimpleAlertDialog.create( - titleRes = R.string.wallet_connect, - messageRes = R.string.wallet_connect_error_timeout, - context = context + SimpleAlertDialog.create( + titleRes = R.string.wallet_connect, + messageRes = R.string.wallet_connect_error_timeout, + context = context ) } is WalletConnectDialog.ApproveWcSession -> - dialog = ApproveWcSessionDialog.create(state.dialog.session, context) + ApproveWcSessionDialog.create(state.dialog.session, context) is WalletConnectDialog.ClipboardOrScanQr -> - dialog = ClipboardOrScanQrDialog.create(state.dialog.clipboardUri, context) + ClipboardOrScanQrDialog.create(state.dialog.clipboardUri, context) is WalletConnectDialog.RequestTransaction -> - dialog = TransactionDialog.create(state.dialog.dialogData, context) + TransactionDialog.create(state.dialog.dialogData, context) is WalletConnectDialog.PersonalSign -> - dialog = PersonalSignDialog.create(state.dialog.data, context) + PersonalSignDialog.create(state.dialog.data, context) + is OnboardingAction.Dialog.AddressInfo -> { + OnboardingAddressInfoDialog(state.dialog, context) + } + else -> null } dialog?.show() } 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 99e6d8fe5d..06cff9e110 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 @@ -1,6 +1,8 @@ package com.tangem.tap.common.extensions -import com.tangem.tap.common.redux.AppState +import com.tangem.tap.common.redux.StateDialog +import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.scope import com.tangem.tap.store import kotlinx.coroutines.Dispatchers @@ -8,8 +10,36 @@ import kotlinx.coroutines.launch import org.rekotlin.Action import org.rekotlin.Store -fun Store.dispatchOnMain(action: Action) { +fun Store<*>.dispatchOnMain(action: Action) { scope.launch(Dispatchers.Main) { store.dispatch(action) } -} \ No newline at end of file +} + +fun Store<*>.dispatchOpenUrl(url: String) { + store.dispatch(NavigationAction.OpenUrl(url)) +} + +fun Store<*>.dispatchNotification(resId: Int) { + scope.launch(Dispatchers.Main) { + store.dispatch(GlobalAction.ShowNotification(resId)) + } +} + +fun Store<*>.dispatchToastNotification(resId: Int) { + scope.launch(Dispatchers.Main) { + store.dispatch(GlobalAction.ShowToastNotification(resId)) + } +} + +fun Store<*>.dispatchDialogShow(dialog: StateDialog) { + scope.launch(Dispatchers.Main) { + store.dispatch(GlobalAction.ShowDialog(dialog)) + } +} + +fun Store<*>.dispatchDialogHide() { + scope.launch(Dispatchers.Main) { + store.dispatch(GlobalAction.HideDialog) + } +} diff --git a/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt b/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt new file mode 100644 index 0000000000..e643ceb207 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt @@ -0,0 +1,10 @@ +package com.tangem.tap.common.redux + +/** +[REDACTED_AUTHOR] + */ +interface StateDialog + +sealed class AppDialog : StateDialog { + object ScanFailsDialog : AppDialog() +} \ No newline at end of file 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 928cb7b341..4c4675eb96 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 @@ -4,6 +4,9 @@ import com.tangem.blockchain.common.WalletManager import com.tangem.common.CompletionResult import com.tangem.common.core.TangemError import com.tangem.tap.common.redux.DebugErrorAction +import com.tangem.tap.common.redux.NotificationAction +import com.tangem.tap.common.redux.StateDialog +import com.tangem.tap.common.redux.ToastNotificationAction import com.tangem.tap.domain.TapError import com.tangem.tap.domain.configurable.config.ConfigManager import com.tangem.tap.domain.configurable.warningMessage.WarningMessage @@ -17,6 +20,14 @@ import org.rekotlin.Action sealed class GlobalAction : Action { + // notifications + data class ShowNotification(override val messageResource: Int) : GlobalAction(), NotificationAction + data class ShowToastNotification(override val messageResource: Int) : GlobalAction(), ToastNotificationAction + + // dialogs + data class ShowDialog(val stateDialog: StateDialog) : GlobalAction() + object HideDialog : GlobalAction() + data class DebugShowError(override val error: TapError) : GlobalAction(), DebugErrorAction data class ReadCard( @@ -56,9 +67,6 @@ sealed class GlobalAction : Action { data class SendFeedback(val emailData: EmailData) : GlobalAction() data class UpdateFeedbackInfo(val walletManagers: List) : GlobalAction() - data class ShowDialog(val stateDialog: StateDialog) : GlobalAction() - object HideDialog : GlobalAction() - object GetMoonPayUserStatus : GlobalAction() { data class Success(val moonPayUserStatus: MoonPayUserStatus) : 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 115329af90..f6e8152a77 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,10 +4,11 @@ import com.tangem.common.CompletionResult import com.tangem.common.core.TangemSdkError import com.tangem.common.services.Result 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.redux.AppDialog import com.tangem.tap.common.redux.AppState import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager -import com.tangem.tap.features.home.redux.HomeAction import com.tangem.tap.features.send.redux.SendAction import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.network.moonpay.MoonpayService @@ -37,8 +38,7 @@ private val globalMiddlewareHandler: Middleware = { dispatch, appState if (action.result.error is TangemSdkError.UserCancelled) { store.dispatch(GlobalAction.ScanFailsCounter.Increment) if (store.state.globalState.scanCardFailsCounter >= 2) { - store.dispatch(HomeAction.ShowDialog.ScanFails) - store.dispatch(WalletAction.ShowDialog.ScanFails) + store.dispatchDialogShow(AppDialog.ScanFailsDialog) } } else { store.dispatch(GlobalAction.ScanFailsCounter.Reset) 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 f370fca90c..558dcaab95 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,6 +1,7 @@ package com.tangem.tap.common.redux.global import com.tangem.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY +import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.domain.PayIdManager import com.tangem.tap.domain.TapWalletManager import com.tangem.tap.domain.configurable.config.ConfigManager @@ -27,7 +28,4 @@ data class GlobalState( ) : StateType typealias CryptoCurrencyName = String -typealias FiatCurrencyName = String - - -interface StateDialog \ No newline at end of file +typealias FiatCurrencyName = String \ No newline at end of file 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 757584cc46..38aeaabc84 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 @@ -2,7 +2,7 @@ package com.tangem.tap.features.details.redux.walletconnect import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.common.WalletManager -import com.tangem.tap.common.redux.global.StateDialog +import com.tangem.tap.common.redux.StateDialog 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 diff --git a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt index e9f86f005b..7633a60893 100644 --- a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt @@ -1,15 +1,9 @@ package com.tangem.tap.features.home -import android.app.Dialog -import android.content.Intent -import android.net.Uri import android.os.Bundle import android.view.View import androidx.fragment.app.Fragment import androidx.transition.TransitionInflater -import com.tangem.tap.common.extensions.safeStartActivity -import com.tangem.tap.common.extensions.toast -import com.tangem.tap.common.redux.global.StateDialog import com.tangem.tap.common.redux.navigation.FragmentShareTransition import com.tangem.tap.common.redux.navigation.ShareElement import com.tangem.tap.common.toggleWidget.IndeterminateProgressButtonWidget @@ -17,9 +11,7 @@ import com.tangem.tap.common.toggleWidget.ViewStateWidget import com.tangem.tap.common.transitions.FrontCardEnterTransition import com.tangem.tap.common.transitions.FrontCardExitTransition import com.tangem.tap.features.home.redux.HomeAction -import com.tangem.tap.features.home.redux.HomeDialog import com.tangem.tap.features.home.redux.HomeState -import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog import com.tangem.tap.store import com.tangem.wallet.R import kotlinx.android.synthetic.main.fragment_home.* @@ -28,9 +20,7 @@ import java.lang.ref.WeakReference class HomeFragment : Fragment(R.layout.fragment_home), StoreSubscriber { - lateinit var btnScanCard: ViewStateWidget - - private var dialog: Dialog? = null + private lateinit var btnScanCard: ViewStateWidget override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -74,36 +64,10 @@ class HomeFragment : Fragment(R.layout.fragment_home), StoreSubscriber { - if (dialog == null) dialog = ScanFailsDialog.create(requireContext()).apply { show() } - } - else -> { - dialog?.dismiss() - dialog = null - } - } - } - override fun onDestroyView() { store.dispatch(HomeAction.SetFragmentShareTransition(null)) super.onDestroyView() diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt index b38e74f995..c6b4cda59c 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt @@ -5,18 +5,14 @@ import com.tangem.tap.common.redux.navigation.FragmentShareTransition import org.rekotlin.Action sealed class HomeAction : Action { - object Init : HomeAction() - data class SetFragmentShareTransition(val shareTransition: FragmentShareTransition?) : HomeAction() - + // from ui object ReadCard : HomeAction() object GoToShop : HomeAction() - class SetOpenUrl(val url: String?) : HomeAction() - class SetTermsOfUseState(val isDisclaimerAccepted: Boolean) : HomeAction() + // internal + object Init : HomeAction() + data class SetFragmentShareTransition(val shareTransition: FragmentShareTransition?) : HomeAction() + data class SetTermsOfUseState(val isDisclaimerAccepted: Boolean) : HomeAction() data class ChangeScanCardButtonState(val state: IndeterminateProgressButton) : HomeAction() - object ShowDialog : HomeAction() { - object ScanFails : HomeAction() - } - object HideDialog : HomeAction() } 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 bbb37ceded..d364664c17 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,6 +1,7 @@ package com.tangem.tap.features.home.redux import com.tangem.tap.common.entities.IndeterminateProgressButton +import com.tangem.tap.common.extensions.dispatchOpenUrl import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.AppScreen @@ -38,7 +39,7 @@ private val homeMiddleware: Middleware = { dispatch, state -> launchOnboardingService() } } - is HomeAction.GoToShop -> store.dispatch(HomeAction.SetOpenUrl(HomeMiddleware.CARD_SHOP_URI)) + is HomeAction.GoToShop -> store.dispatchOpenUrl(HomeMiddleware.CARD_SHOP_URI) } next(action) } diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt index 6fe2462f08..0b9b044622 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt @@ -21,18 +21,9 @@ private fun internalReduce(action: Action, state: AppState): HomeState { is HomeAction.SetTermsOfUseState -> { state = state.copy(isDisclaimerAccepted = action.isDisclaimerAccepted) } - is HomeAction.SetOpenUrl -> { - state = state.copy(openUrl = action.url) - } is HomeAction.ChangeScanCardButtonState -> { state = state.copy(btnScanState = action.state) } - is HomeAction.ShowDialog.ScanFails -> { - state = state.copy(dialog = HomeDialog.ScanFailsDialog) - } - is HomeAction.HideDialog -> { - state = state.copy(dialog = null) - } } return state diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt index 9a261cd1bf..29a7825aae 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt @@ -1,19 +1,12 @@ package com.tangem.tap.features.home.redux import com.tangem.tap.common.entities.IndeterminateProgressButton -import com.tangem.tap.common.redux.global.StateDialog import com.tangem.tap.common.redux.navigation.FragmentShareTransition import com.tangem.tap.features.send.redux.states.ButtonState import org.rekotlin.StateType data class HomeState( - val openUrl: String? = null, - val isDisclaimerAccepted: Boolean = false, - val shareTransition: FragmentShareTransition? = null, - val btnScanState: IndeterminateProgressButton = IndeterminateProgressButton(ButtonState.ENABLED), - val dialog: StateDialog? = null, -) : StateType - -sealed class HomeDialog : StateDialog { - object ScanFailsDialog : HomeDialog() -} \ No newline at end of file + val isDisclaimerAccepted: Boolean = false, + val shareTransition: FragmentShareTransition? = null, + val btnScanState: IndeterminateProgressButton = IndeterminateProgressButton(ButtonState.ENABLED), +) : StateType \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt index 6f20155539..b0b9263034 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt @@ -5,13 +5,13 @@ import com.tangem.blockchain.common.Amount import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.WalletManager import com.tangem.tap.common.redux.ErrorAction +import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.common.redux.ToastNotificationAction -import com.tangem.tap.common.redux.global.StateDialog import com.tangem.tap.domain.TapError import com.tangem.tap.domain.configurable.warningMessage.WarningMessage +import com.tangem.tap.features.send.redux.states.ButtonState import com.tangem.tap.features.send.redux.states.FeeType import com.tangem.tap.features.send.redux.states.MainCurrencyType -import com.tangem.tap.features.send.redux.states.ButtonState import com.tangem.wallet.R import org.rekotlin.Action import java.math.BigDecimal diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt index 3f3d32df5e..ed98986ae3 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt @@ -7,7 +7,7 @@ import com.tangem.common.extensions.isZero import com.tangem.tap.common.CurrencyConverter import com.tangem.tap.common.entities.IndeterminateProgressButton import com.tangem.tap.common.entities.TapCurrency -import com.tangem.tap.common.redux.global.StateDialog +import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.common.text.DecimalDigitsInputFilter import com.tangem.tap.domain.TapError import com.tangem.tap.domain.configurable.warningMessage.WarningMessage 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 488adca48d..51d6c3740d 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 @@ -122,7 +122,6 @@ sealed class WalletAction : Action { object ShowDialog : WalletAction() { object QrCode : WalletAction() - object ScanFails : WalletAction() object SignedHashesMultiWalletDialog : WalletAction() object ChooseTradeActionDialog : WalletAction() } 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 0d81713483..cdabd1500c 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 @@ -5,8 +5,9 @@ import com.tangem.blockchain.common.* import com.tangem.blockchain.common.address.AddressType import com.tangem.blockchain.extensions.isAboveZero import com.tangem.tap.common.entities.Button +import com.tangem.tap.common.extensions.toQrCode +import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.common.redux.global.CryptoCurrencyName -import com.tangem.tap.common.redux.global.StateDialog import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.extensions.toSendableAmounts import com.tangem.tap.domain.topup.TradeCryptoHelper @@ -141,7 +142,6 @@ sealed class WalletDialog: StateDialog { ) : WalletDialog() data class SelectAmountToSendDialog(val amounts: List?) : WalletDialog() - object ScanFailsDialog : WalletDialog() object SignedHashesMultiWalletDialog : WalletDialog() object ChooseTradeActionDialog : WalletDialog() } @@ -165,8 +165,9 @@ data class AddressData( val type: AddressType, val shareUrl: String, val exploreUrl: String, - val qrCode: Bitmap? = null -) +) { + val qrCode: Bitmap by lazy { shareUrl.toQrCode() } +} data class Artwork( val artworkId: String, 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 8c6db9d9a2..d999216bba 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 @@ -232,9 +232,6 @@ private fun internalReduce(action: Action, state: AppState): WalletState { ) ) } - is WalletAction.ShowDialog.ScanFails -> { - newState = newState.copy(walletDialog = WalletDialog.ScanFailsDialog) - } is WalletAction.ShowDialog.SignedHashesMultiWalletDialog -> { newState = newState.copy(walletDialog = WalletDialog.SignedHashesMultiWalletDialog) } @@ -276,30 +273,34 @@ private fun internalReduce(action: Action, state: AppState): WalletState { fun createAddressList(wallet: Wallet?, walletAddresses: WalletAddresses? = null): WalletAddresses? { if (wallet == null) return null + val listOfAddressData = wallet.createAddressesData() + // restore a selected wallet address + var indexOfSelectedWallet = 0 + walletAddresses?.let { + val index = + listOfAddressData.indexOfFirst { it.address == walletAddresses.selectedAddress.address } + if (index != -1) indexOfSelectedWallet = index + } + return WalletAddresses(listOfAddressData[indexOfSelectedWallet], listOfAddressData) +} + +fun Wallet.createAddressesData(): List { val listOfAddressData = mutableListOf() // put a defaultAddress at the first place - wallet.addresses.forEach { + addresses.forEach { val addressData = AddressData( - it.value, - it.type, - wallet.getShareUri(it.value), - wallet.getExploreUrl(it.value) + it.value, + it.type, + getShareUri(it.value), + getExploreUrl(it.value) ) - if (it.type == wallet.blockchain.defaultAddressType()) { + if (it.type == blockchain.defaultAddressType()) { listOfAddressData.add(0, addressData) } else { listOfAddressData.add(addressData) } } - - // restore a selected wallet address - var indexOfSelectedWallet = 0 - walletAddresses?.let { - val index = - listOfAddressData.indexOfFirst { it.address == walletAddresses.selectedAddress.address } - if (index != -1) indexOfSelectedWallet = index - } - return WalletAddresses(listOfAddressData[indexOfSelectedWallet], listOfAddressData) + return listOfAddressData } private fun handleCheckSignedHashesActions( 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 c2ff298244..1592ae4eb2 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 @@ -12,7 +12,7 @@ import androidx.transition.TransitionInflater import com.squareup.picasso.Picasso import com.tangem.tap.common.SnackbarHandler import com.tangem.tap.common.extensions.* -import com.tangem.tap.common.redux.global.StateDialog +import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.features.wallet.models.PendingTransaction import com.tangem.tap.features.wallet.redux.* diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt index b3f63a0236..0f164965d4 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt @@ -2,10 +2,9 @@ package com.tangem.tap.features.wallet.ui.dialogs import android.content.Context import androidx.appcompat.app.AlertDialog +import com.tangem.tap.common.extensions.dispatchDialogHide import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.features.feedback.ScanFailsEmail -import com.tangem.tap.features.home.redux.HomeAction -import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.store import com.tangem.wallet.R @@ -23,10 +22,7 @@ class ScanFailsDialog { store.dispatch(GlobalAction.SendFeedback(ScanFailsEmail())) } setNegativeButton(R.string.common_cancel) { _, _ -> } - setOnDismissListener { - store.dispatch(HomeAction.HideDialog) - store.dispatch(WalletAction.HideDialog) - } + setOnDismissListener { store.dispatchDialogHide() } }.create() } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt index d40eaa4496..aa0aaee36c 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt @@ -4,7 +4,7 @@ import android.app.Dialog import androidx.recyclerview.widget.LinearLayoutManager import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.show -import com.tangem.tap.common.redux.global.StateDialog +import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.features.tokens.redux.TokensAction @@ -14,7 +14,6 @@ import com.tangem.tap.features.wallet.redux.WalletState import com.tangem.tap.features.wallet.ui.BalanceStatus import com.tangem.tap.features.wallet.ui.WalletFragment import com.tangem.tap.features.wallet.ui.adapters.WalletAdapter -import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog import com.tangem.tap.features.wallet.ui.dialogs.SignedHashesWarningDialog import com.tangem.tap.store import com.tangem.wallet.R @@ -134,9 +133,6 @@ class MultiWalletView : WalletView { val fragment = fragment ?: return val context = fragment.context ?: return when (walletDialog) { - is WalletDialog.ScanFailsDialog -> { - if (dialog == null) dialog = ScanFailsDialog.create(context).apply { show() } - } is WalletDialog.SignedHashesMultiWalletDialog -> { if (dialog == null) { dialog = SignedHashesWarningDialog.create(context).apply { show() } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt index aac75ef60b..d2e4e77cc5 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt @@ -6,7 +6,7 @@ import android.view.ViewGroup import android.widget.Button import androidx.recyclerview.widget.LinearLayoutManager import com.tangem.tap.common.extensions.* -import com.tangem.tap.common.redux.global.StateDialog +import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.twins.TwinCardNumber @@ -248,9 +248,6 @@ class SingleWalletView : WalletView { this.show(walletDialog.amounts) } } - is WalletDialog.ScanFailsDialog -> { - if (dialog == null) dialog = ScanFailsDialog.create(context).apply { show() } - } is WalletDialog.SignedHashesMultiWalletDialog -> { if (dialog == null) { dialog = SignedHashesWarningDialog.create(context).apply { show() } From 34a364fca10d7017c62eff114c116ca5b6997093 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sat, 25 Sep 2021 03:57:57 +0300 Subject: [PATCH 047/189] Updated on 2026-08-14 --- .../com/tangem/tap/common/images/Picasso.kt | 23 +++++++++++++++++++ .../com/tangem/tap/domain/UrlBitmapLoader.kt | 15 +++++++----- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/common/images/Picasso.kt b/app/src/main/java/com/tangem/tap/common/images/Picasso.kt index df364efdb7..7d2a956bde 100644 --- a/app/src/main/java/com/tangem/tap/common/images/Picasso.kt +++ b/app/src/main/java/com/tangem/tap/common/images/Picasso.kt @@ -3,9 +3,12 @@ package com.tangem.tap.common.images import android.app.Application import com.squareup.picasso.OkHttp3Downloader import com.squareup.picasso.Picasso +import com.tangem.wallet.BuildConfig import okhttp3.Cache import okhttp3.CacheControl import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import timber.log.Timber import java.io.File import java.util.concurrent.TimeUnit @@ -17,6 +20,7 @@ class PicassoHelper { val picasso = Picasso.Builder(application) .downloader(OkHttp3Downloader(getOkHttpForPicasso(application))) .build() + picasso.isLoggingEnabled = BuildConfig.DEBUG Picasso.setSingletonInstance(picasso) } @@ -24,6 +28,7 @@ class PicassoHelper { val okHttpBuilder = OkHttpClient.Builder() okHttpBuilder.cache(Cache(File(application.filesDir, "artworks"), Long.MAX_VALUE)) okHttpBuilder.callTimeout(15000, TimeUnit.MILLISECONDS) + okHttpBuilder.addInterceptor { chain -> val cacheControl = CacheControl.Builder() .maxStale(KEEP_CACHE_MAX_DAYS, TimeUnit.DAYS) @@ -34,7 +39,25 @@ class PicassoHelper { .build() chain.proceed(neverExpireRequest) } + addDebugInterceptors(okHttpBuilder) + return okHttpBuilder.build() } + + private fun addDebugInterceptors(okHttpBuilder: OkHttpClient.Builder) { + if (!BuildConfig.DEBUG) return + + val picassoInterceptor = HttpLoggingInterceptor(PicassoOkHttpLogger()).apply { + level = HttpLoggingInterceptor.Level.BODY + } + okHttpBuilder.addInterceptor(picassoInterceptor) + } + + } +} + +private class PicassoOkHttpLogger : HttpLoggingInterceptor.Logger { + override fun log(message: String) { + Timber.d(message) } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/UrlBitmapLoader.kt b/app/src/main/java/com/tangem/tap/domain/UrlBitmapLoader.kt index 1f4897a487..fe202ed0ca 100644 --- a/app/src/main/java/com/tangem/tap/domain/UrlBitmapLoader.kt +++ b/app/src/main/java/com/tangem/tap/domain/UrlBitmapLoader.kt @@ -5,38 +5,41 @@ import android.graphics.drawable.Drawable import com.squareup.picasso.Picasso import com.squareup.picasso.Target import com.tangem.common.services.Result -import timber.log.Timber /** [REDACTED_AUTHOR] */ class UrlBitmapLoader { - fun loadBitmap(url: String, callback: (Result) -> Unit) { - Picasso.get().load(url).into(DownloadTarget(callback)) + val target = DownloadTarget(callback) + protectedFromGarbageCollectorTargets.add(target) + Picasso.get().load(url).into(target) } fun loadBitmap(url: String, target: DownloadTarget) { + protectedFromGarbageCollectorTargets.add(target) Picasso.get().load(url).into(target) } + } +private val protectedFromGarbageCollectorTargets = mutableListOf() + // It adds the ability to trigger multiple downloads with a unique callback. open class DownloadTarget( val callback: (Result) -> Unit, ) : Target { override fun onBitmapLoaded(bitmap: Bitmap, from: Picasso.LoadedFrom) { - Timber.d("onBitmapLoaded") callback(Result.Success(bitmap)) + protectedFromGarbageCollectorTargets.remove(this) } override fun onBitmapFailed(e: java.lang.Exception?, errorDrawable: Drawable?) { - Timber.d("onBitmapFailed") callback.invoke(Result.Failure(e)) + protectedFromGarbageCollectorTargets.remove(this) } override fun onPrepareLoad(placeHolderDrawable: Drawable?) { - Timber.d("onPrepareLoad") } } \ No newline at end of file From 3658f492403549e32051718d0d69508bd3b06b2f Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 28 Sep 2021 06:49:52 +0300 Subject: [PATCH 048/189] Updated on 2026-08-14 --- app/build.gradle | 6 +++--- .../com/tangem/tap/features/details/redux/DetailsReducer.kt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 923c2c5326..e33f448654 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -76,9 +76,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-38' - implementation 'com.tangem.tangem-sdk-kotlin:core:develop-78' - implementation 'com.tangem.tangem-sdk-kotlin:android:develop-78' + implementation 'com.tangem:blockchain:develop-41' + implementation 'com.tangem.tangem-sdk-kotlin:core:develop-79' + implementation 'com.tangem.tangem-sdk-kotlin:android:develop-79' // WebView implementation "androidx.browser:browser:1.3.0" diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt index 62253c27f2..61b94447a1 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt @@ -185,7 +185,7 @@ private fun handleSecurityAction( private fun prepareAllowedSecurityOptions( card: Card?, currentSecurityOption: SecurityOption? ): EnumSet { - val prohibitDefaultPin = card?.settings?.isRemovingAccessCodeAllowed != true + val prohibitDefaultPin = card?.settings?.isResettingUserCodesAllowed != true val allowedSecurityOptions = EnumSet.noneOf(SecurityOption::class.java) if ((currentSecurityOption == SecurityOption.LongTap) || !prohibitDefaultPin) { From 0ec0d790280977f5575f28060666a69c0365b3b4 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 28 Sep 2021 07:07:40 +0300 Subject: [PATCH 049/189] Updated on 2026-08-14 --- .../com/tangem/tap/features/onboarding/OnboardingService.kt | 4 ++-- .../java/com/tangem/tap/persistence/UsedCardsPrefStorage.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingService.kt b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingService.kt index c54cfc75e8..2fc0925f2e 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingService.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingService.kt @@ -49,11 +49,11 @@ class OnboardingService( if (!card.isTangemNote() && !card.isTangemWallet() && !card.isTangemTwin()) { if (hasWallets) { - cardInfoStorage.activated(card.cardId) + cardInfoStorage.activate(card.cardId) onFailedToProceedToOnboardingCase?.invoke(response) return } else { - cardInfoStorage.activated(card.cardId) // вызвать только после того, как был создан кошелек + cardInfoStorage.activate(card.cardId) // вызвать только после того, как был создан кошелек onboardingDataLinker.navigateTo = AppScreen.OnboardingOther } } diff --git a/app/src/main/java/com/tangem/tap/persistence/UsedCardsPrefStorage.kt b/app/src/main/java/com/tangem/tap/persistence/UsedCardsPrefStorage.kt index b6ebfd8a32..f903e6425e 100644 --- a/app/src/main/java/com/tangem/tap/persistence/UsedCardsPrefStorage.kt +++ b/app/src/main/java/com/tangem/tap/persistence/UsedCardsPrefStorage.kt @@ -48,7 +48,7 @@ class UsedCardsPrefStorage( return findCardInfo(cardId)?.isActivationStarted ?: false } - fun activated(cardId: String) { + fun activate(cardId: String) { val restoredList = restore() val foundItem = findCardInfo(cardId, restoredList)?.copy(isActivated = true) ?: UsedCardInfo(cardId, isActivated = true) From 9034dcd682ed1cf74f097f9b0609c498c1f78e4f Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 30 Sep 2021 01:05:15 +0300 Subject: [PATCH 050/189] Updated on 2026-08-14 --- app/src/main/java/com/tangem/tap/common/entities/Button.kt | 6 +++--- .../toggleWidget/IndeterminateProgressButtonWidget.kt | 7 ++++--- .../com/tangem/tap/common/toggleWidget/ViewStateWidget.kt | 5 ----- .../send/ui/stateSubscribers/SendStateSubscriber.kt | 1 - .../com/tangem/tap/features/wallet/redux/WalletState.kt | 3 ++- 5 files changed, 9 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/common/entities/Button.kt b/app/src/main/java/com/tangem/tap/common/entities/Button.kt index 3255e490ca..a2d4127c8f 100644 --- a/app/src/main/java/com/tangem/tap/common/entities/Button.kt +++ b/app/src/main/java/com/tangem/tap/common/entities/Button.kt @@ -1,7 +1,7 @@ package com.tangem.tap.common.entities -import com.tangem.tap.common.toggleWidget.ProgressState import com.tangem.tap.features.send.redux.states.ButtonState +import com.tangem.tap.features.wallet.redux.ProgressState open class Button(val enabled: Boolean) @@ -11,7 +11,7 @@ open class IndeterminateProgressButton( val progressState: ProgressState get() = when (state) { - ButtonState.PROGRESS -> ProgressState.Progress() - else -> ProgressState.None + ButtonState.PROGRESS -> ProgressState.Loading + else -> ProgressState.Done } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/toggleWidget/IndeterminateProgressButtonWidget.kt b/app/src/main/java/com/tangem/tap/common/toggleWidget/IndeterminateProgressButtonWidget.kt index 153f7c662d..b4eacfff4e 100644 --- a/app/src/main/java/com/tangem/tap/common/toggleWidget/IndeterminateProgressButtonWidget.kt +++ b/app/src/main/java/com/tangem/tap/common/toggleWidget/IndeterminateProgressButtonWidget.kt @@ -5,6 +5,7 @@ import android.view.View import com.google.android.material.button.MaterialButton import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.show +import com.tangem.tap.features.wallet.redux.ProgressState /** [REDACTED_AUTHOR] @@ -12,7 +13,7 @@ import com.tangem.tap.common.extensions.show open class IndeterminateProgressButtonWidget( private val button: MaterialButton, private val progress: View, - initialState: ProgressState = ProgressState.None + initialState: ProgressState = ProgressState.Done ) : ViewStateWidget { private var initialButtonText: CharSequence = button.text @@ -29,8 +30,8 @@ open class IndeterminateProgressButtonWidget( val progressState = state as? ProgressState ?: return when (progressState) { - is ProgressState.None -> switchToNone() - is ProgressState.Progress -> switchToProgress() + ProgressState.Done, ProgressState.Error -> switchToNone() + ProgressState.Loading -> switchToProgress() } } diff --git a/app/src/main/java/com/tangem/tap/common/toggleWidget/ViewStateWidget.kt b/app/src/main/java/com/tangem/tap/common/toggleWidget/ViewStateWidget.kt index 0c06410ace..48ba0a7291 100644 --- a/app/src/main/java/com/tangem/tap/common/toggleWidget/ViewStateWidget.kt +++ b/app/src/main/java/com/tangem/tap/common/toggleWidget/ViewStateWidget.kt @@ -11,9 +11,4 @@ interface WidgetState interface ViewStateWidget { val mainView: View fun changeState(state: WidgetState) -} - -sealed class ProgressState : WidgetState { - object None : ProgressState() - data class Progress(val progress: Int = 0) : ProgressState() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt index 39b4eefe8f..fb2633ff3a 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt @@ -10,7 +10,6 @@ import androidx.core.text.bold import com.tangem.tap.common.extensions.* import com.tangem.tap.common.redux.getMessageString import com.tangem.tap.common.text.DecimalDigitsInputFilter -import com.tangem.tap.common.toggleWidget.ProgressState import com.tangem.tap.domain.MultiMessageError import com.tangem.tap.domain.assembleErrors import com.tangem.tap.features.send.BaseStoreFragment 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 cdabd1500c..f57259ac3c 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 @@ -8,6 +8,7 @@ import com.tangem.tap.common.entities.Button import com.tangem.tap.common.extensions.toQrCode import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.common.redux.global.CryptoCurrencyName +import com.tangem.tap.common.toggleWidget.WidgetState import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.extensions.toSendableAmounts import com.tangem.tap.domain.topup.TradeCryptoHelper @@ -146,7 +147,7 @@ sealed class WalletDialog: StateDialog { object ChooseTradeActionDialog : WalletDialog() } -enum class ProgressState { Loading, Done, Error } +enum class ProgressState: WidgetState { Loading, Done, Error } enum class ErrorType { NoInternetConnection } From 318efa8d214bcdf8ea42da1e8da8d527965a5f08 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sat, 2 Oct 2021 18:01:56 +0300 Subject: [PATCH 051/189] Updated on 2026-08-14 --- app/src/main/java/com/tangem/tap/domain/UrlBitmapLoader.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/tangem/tap/domain/UrlBitmapLoader.kt b/app/src/main/java/com/tangem/tap/domain/UrlBitmapLoader.kt index fe202ed0ca..6a63eaf0b8 100644 --- a/app/src/main/java/com/tangem/tap/domain/UrlBitmapLoader.kt +++ b/app/src/main/java/com/tangem/tap/domain/UrlBitmapLoader.kt @@ -2,6 +2,8 @@ package com.tangem.tap.domain import android.graphics.Bitmap import android.graphics.drawable.Drawable +import android.os.Handler +import android.os.Looper import com.squareup.picasso.Picasso import com.squareup.picasso.Target import com.tangem.common.services.Result @@ -10,10 +12,12 @@ import com.tangem.common.services.Result [REDACTED_AUTHOR] */ class UrlBitmapLoader { + private val mainHandler = Handler(Looper.getMainLooper()) + fun loadBitmap(url: String, callback: (Result) -> Unit) { val target = DownloadTarget(callback) protectedFromGarbageCollectorTargets.add(target) - Picasso.get().load(url).into(target) + mainHandler.post { Picasso.get().load(url).into(target) } } fun loadBitmap(url: String, target: DownloadTarget) { From baaa3173811008e13951f853497fa77c6a6a6582 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 4 Oct 2021 17:20:16 +0300 Subject: [PATCH 052/189] Updated on 2026-08-14 --- .../tap/common/extensions/WalletManager.kt | 32 +++++++++++++++ .../com/tangem/tap/domain/TapWalletManager.kt | 40 +++++++++---------- 2 files changed, 50 insertions(+), 22 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt 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 new file mode 100644 index 0000000000..d8b5b136dd --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt @@ -0,0 +1,32 @@ +package com.tangem.tap.common.extensions + +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.network.NetworkConnectivity + +/** +[REDACTED_AUTHOR] + */ +suspend fun WalletManager.loadWalletData(): Result = try { + update() + Result.Success(wallet) +} catch (exception: Exception) { + if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) { + Result.Failure(TapError.NoInternetConnection) + } else { + val blockchain = wallet.blockchain + val amountToCreateAccount = blockchain.amountToCreateAccount(wallet.getFirstToken()) + + if (blockchain.isNoAccountError(exception) && amountToCreateAccount != null) { + Result.Failure(TapError.NoAccount(amountToCreateAccount.toString())) + } else { + val message = exception.localizedMessage ?: "Unknown exception during WalletManager update" + Result.Failure(TapError.CustomError(message)) + } + } +} \ No newline at end of file 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 0193a9d2a1..6e4dec24f5 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -5,6 +5,8 @@ import com.tangem.common.card.Card 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.loadWalletData import com.tangem.tap.common.redux.global.FiatCurrencyName import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.currenciesRepository @@ -38,13 +40,7 @@ class TapWalletManager { by lazy { WalletManagerFactory(blockchainSdkConfig) } suspend fun loadWalletData(walletManager: WalletManager) { - val result = try { - walletManager.update() - Result.Success(walletManager.wallet) - } catch (exception: Exception) { - Result.Failure(exception) - } - handleUpdateWalletResult(result, walletManager) + handleUpdateWalletResult(walletManager.loadWalletData(), walletManager) } suspend fun updateWallet(walletManager: WalletManager) { @@ -261,23 +257,23 @@ class TapWalletManager { when (result) { is Result.Success -> store.dispatch(WalletAction.LoadWallet.Success(result.data)) is Result.Failure -> { - if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) { - store.dispatch(WalletAction.LoadData.Failure(TapError.NoInternetConnection)) - return@withContext - } - val error = result.error - val blockchain = walletManager.wallet.blockchain - if (error != null && blockchain.isNoAccountError(error)) { - val token = walletManager.wallet.getFirstToken() - val amountToCreateAccount = blockchain.amountToCreateAccount(token) - if (amountToCreateAccount != null) { - store.dispatch(WalletAction.LoadWallet.NoAccount( - walletManager.wallet, - amountToCreateAccount.toString())) - return@withContext + val error = (result.error as? TapError) ?: TapError.UnknownError + when (error) { + TapError.NoInternetConnection -> { + store.dispatch(WalletAction.LoadData.Failure(error)) + } + is TapError.NoAccount -> { + store.dispatch(WalletAction.LoadWallet + .NoAccount(walletManager.wallet, error.amountToCreateAccount)) + } + is TapError.CustomError -> { + store.dispatch(WalletAction.LoadWallet + .Failure(walletManager.wallet, error.customMessage)) + } + else -> { + store.dispatchDebugErrorNotification(error) } } - store.dispatch(WalletAction.LoadWallet.Failure(walletManager.wallet, result.error?.localizedMessage)) } } } From ca51d6803aa9cf9dfa8d29f3a8c516fa1c943618 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 6 Oct 2021 22:12:14 +0300 Subject: [PATCH 053/189] Updated on 2026-08-14 --- app/build.gradle | 8 +- .../tap/common/extensions/Collections.kt | 9 +- .../tangem/tap/common/extensions/Coroutine.kt | 16 +++ .../tap/common/extensions/Navigation.kt | 2 +- .../com/tangem/tap/common/extensions/Store.kt | 22 ++++ .../tangem/tap/common/extensions/ViewGroup.kt | 8 +- .../redux/navigation/NavigationAction.kt | 4 +- .../toggleWidget/RefreshBalanceWidget.kt | 100 ++++++++++++++++++ .../transitions/OnboardingTransitions.kt | 33 ++++-- .../com/tangem/tap/domain/UrlBitmapLoader.kt | 4 +- .../tap/features/wallet/redux/WalletState.kt | 2 + .../tap/persistence/PreferencesStorage.kt | 7 +- .../tap/persistence/UsedCardsPrefStorage.kt | 19 ++-- 13 files changed, 198 insertions(+), 36 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/common/extensions/Coroutine.kt create mode 100644 app/src/main/java/com/tangem/tap/common/toggleWidget/RefreshBalanceWidget.kt diff --git a/app/build.gradle b/app/build.gradle index e33f448654..69493ced41 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -76,9 +76,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-41' - implementation 'com.tangem.tangem-sdk-kotlin:core:develop-79' - implementation 'com.tangem.tangem-sdk-kotlin:android:develop-79' + implementation 'com.tangem:blockchain:develop-42' + implementation 'com.tangem.tangem-sdk-kotlin:core:develop-80' + implementation 'com.tangem.tangem-sdk-kotlin:android:develop-80' // WebView implementation "androidx.browser:browser:1.3.0" @@ -122,6 +122,8 @@ dependencies { // //Crypto implementation "com.madgag.spongycastle:core:1.58.0.0" + // animation + implementation "com.airbnb.android:lottie:3.4.0" testImplementation 'junit:junit:4.13.2' testImplementation "com.google.truth:truth:1.1.3" diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Collections.kt b/app/src/main/java/com/tangem/tap/common/extensions/Collections.kt index b264485c44..8492ae44f1 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Collections.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Collections.kt @@ -16,9 +16,16 @@ fun MutableList.removeBy(predicate: (T) -> Boolean): Boolean { return toRemove.isNotEmpty() } -fun MutableList.replaceBy(item: T, predicate: (T) -> Boolean) { +fun MutableList.replaceBy(item: T, predicate: (T) -> Boolean): Boolean { val toRemove = this.filter(predicate) + if (toRemove.isEmpty()) return false + val indexes = toRemove.map { indexOf(it) } this.removeAll(toRemove) indexes.forEach { this.add(it, item) } + return true +} + +fun MutableList.replaceByOrAdd(item: T, predicate: (T) -> Boolean) { + if (!replaceBy(item, predicate)) add(item) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Coroutine.kt b/app/src/main/java/com/tangem/tap/common/extensions/Coroutine.kt new file mode 100644 index 0000000000..63c62dfdc2 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/extensions/Coroutine.kt @@ -0,0 +1,16 @@ +package com.tangem.tap.common.extensions + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +/** +[REDACTED_AUTHOR] + */ +suspend fun withMainContext(block: suspend CoroutineScope.() -> T) { + withContext(Dispatchers.Main, block) +} + +suspend fun withIOContext(block: suspend CoroutineScope.() -> T) { + withContext(Dispatchers.IO, block) +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt index 3a119fd683..02c794329c 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt @@ -17,7 +17,7 @@ import com.tangem.tap.features.details.ui.walletconnect.WalletConnectSessionsFra import com.tangem.tap.features.disclaimer.ui.DisclaimerFragment import com.tangem.tap.features.home.HomeFragment import com.tangem.tap.features.onboarding.products.note.OnboardingNoteFragment -import com.tangem.tap.features.onboarding.products.old.OnboardingOtherCardsFragment +import com.tangem.tap.features.onboarding.products.otherCards.OnboardingOtherCardsFragment import com.tangem.tap.features.onboarding.products.wallet.OnboardingWalletFragment import com.tangem.tap.features.send.ui.SendFragment import com.tangem.tap.features.tokens.ui.AddTokensFragment 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 06cff9e110..f5fd832f2b 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 @@ -3,6 +3,7 @@ package com.tangem.tap.common.extensions import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.NavigationAction +import com.tangem.tap.domain.TapError import com.tangem.tap.scope import com.tangem.tap.store import kotlinx.coroutines.Dispatchers @@ -32,6 +33,27 @@ fun Store<*>.dispatchToastNotification(resId: Int) { } } + +fun Store<*>.dispatchErrorNotification(error: TapError) { + scope.launch(Dispatchers.Main) { + store.dispatch(GlobalAction.ShowErrorNotification(error)) + } +} + +/** + * @param fatal used to indicate errors that should not normally have occurred + */ +fun Store<*>.dispatchDebugErrorNotification(message: String, fatal: Boolean = false) { + val prefix = if (fatal) "FATAL ERROR: " else "DEBUG ERROR: " + dispatchDebugErrorNotification(TapError.CustomError("$prefix $message")) +} + +fun Store<*>.dispatchDebugErrorNotification(error: TapError) { + scope.launch(Dispatchers.Main) { + store.dispatch(GlobalAction.DebugShowErrorNotification(error)) + } +} + fun Store<*>.dispatchDialogShow(dialog: StateDialog) { scope.launch(Dispatchers.Main) { store.dispatch(GlobalAction.ShowDialog(dialog)) diff --git a/app/src/main/java/com/tangem/tap/common/extensions/ViewGroup.kt b/app/src/main/java/com/tangem/tap/common/extensions/ViewGroup.kt index fb9c20bfdb..81be964cb1 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/ViewGroup.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/ViewGroup.kt @@ -1,6 +1,7 @@ package com.tangem.tap.common.extensions import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup import android.view.ViewParent import androidx.core.view.forEach @@ -15,11 +16,8 @@ import timber.log.Timber /** [REDACTED_AUTHOR] */ -fun ViewGroup.inflate(viewToInflate: Int, rootView: ViewGroup?, parent: ViewGroup) { - if (rootView == null) { - val inflatedView = LayoutInflater.from(context).inflate(viewToInflate, rootView) - parent.addView(inflatedView) - } +fun ViewGroup.inflate(viewToInflate: Int, attachToRoot: Boolean = false): View { + return LayoutInflater.from(context).inflate(viewToInflate, this, attachToRoot) } fun ViewParent?.beginDelayedTransition(transition: Transition = AutoTransition()) { diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt index d77f97b263..331dc07e1a 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt @@ -9,8 +9,8 @@ import java.lang.ref.WeakReference sealed class NavigationAction : Action { data class NavigateTo( val screen: AppScreen, - val addToBackstack: Boolean = true, - val fragmentShareTransition: FragmentShareTransition? = null + val fragmentShareTransition: FragmentShareTransition? = null, + val addToBackstack: Boolean = true ) : NavigationAction() data class PopBackTo(val screen: AppScreen? = null) : NavigationAction() diff --git a/app/src/main/java/com/tangem/tap/common/toggleWidget/RefreshBalanceWidget.kt b/app/src/main/java/com/tangem/tap/common/toggleWidget/RefreshBalanceWidget.kt new file mode 100644 index 0000000000..e7ef08e026 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/toggleWidget/RefreshBalanceWidget.kt @@ -0,0 +1,100 @@ +package com.tangem.tap.common.toggleWidget + +import android.view.View +import android.view.ViewGroup +import android.view.animation.* +import android.widget.ViewSwitcher +import com.tangem.tap.features.wallet.redux.ProgressState +import com.tangem.wallet.R + +/** +[REDACTED_AUTHOR] + */ +class RefreshBalanceWidget( + private val root: ViewGroup +) : ViewStateWidget { + + var isShowing: Boolean? = null + private set + + override val mainView: View = root.findViewById(R.id.btn_refresh_balance) + + + private val viewSwitcher: ViewSwitcher by lazy { mainView.findViewById(R.id.switcher_refresh_arrow_to_progress) } + + private val arrowView = mainView.findViewById(R.id.imv_arrow_refresh) + + private var progressViewAnimation: Animation? = null + + init { + viewSwitcher.inAnimation = AlphaAnimation(0f, 1f).apply { + duration = 400 + interpolator = LinearInterpolator() + } + viewSwitcher.outAnimation = AlphaAnimation(1f, 0f).apply { + duration = 400 + interpolator = LinearInterpolator() + } + } + + override fun changeState(state: WidgetState) { + val progressState = state as? ProgressState ?: return + + val currentStateByView = getState() + + if (progressState == currentStateByView) return + + animateState(progressState) + viewSwitcher.showNext() + + } + + private fun animateState(state: ProgressState) { + when (state) { + ProgressState.Done, ProgressState.Error -> { + progressViewAnimation?.cancel() + } + ProgressState.Loading -> { + val animation = RotateAnimation( + 0f, 360f, + Animation.RELATIVE_TO_SELF, 0.5f, + Animation.RELATIVE_TO_SELF, 0.5f + ) + animation.duration = 700 + animation.interpolator = AccelerateInterpolator() + animation.repeatCount = -1 + arrowView.startAnimation(animation) + progressViewAnimation = animation + } + } + } + + fun show(show: Boolean) { + if (show == isShowing) return + + isShowing = show + if (show) { + mainView.startAnimation(ShowAnimation()) + } else { + mainView.startAnimation(AlphaAnimation(1f, 0f).apply { fillAfter = true }) + } + } + + private fun isArrowRefreshActive(): Boolean = viewSwitcher.currentView.id == R.id.fl_arrow_refresh_container + + private fun getState(): ProgressState = if (isArrowRefreshActive()) ProgressState.Done else ProgressState.Loading +} + +class ShowAnimation : AnimationSet(true) { + init { + addAnimation(ScaleAnimation( + 0f, 1f, + 0f, 1f, + Animation.RELATIVE_TO_SELF, 0.5f, + Animation.RELATIVE_TO_SELF, 0.5f) + ) + addAnimation(AlphaAnimation(0f, 1f)) + duration = 300 + interpolator = AnticipateOvershootInterpolator() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/transitions/OnboardingTransitions.kt b/app/src/main/java/com/tangem/tap/common/transitions/OnboardingTransitions.kt index 7d9334e123..32785f0147 100644 --- a/app/src/main/java/com/tangem/tap/common/transitions/OnboardingTransitions.kt +++ b/app/src/main/java/com/tangem/tap/common/transitions/OnboardingTransitions.kt @@ -1,8 +1,6 @@ package com.tangem.tap.common.transitions -import androidx.transition.ChangeBounds -import androidx.transition.ChangeImageTransform -import androidx.transition.TransitionSet +import androidx.transition.* /** [REDACTED_AUTHOR] @@ -10,18 +8,37 @@ import androidx.transition.TransitionSet class FrontCardEnterTransition : TransitionSet() { init { ordering = ORDERING_TOGETHER; - addTransition(ChangeImageTransform()) - addTransition(ChangeBounds()) -// addTransition(Fade()) -// addTransition(Fade(Visibility.MODE_IN)) -// addTransition(ChangeTransform()) + addTransition(Fade()) + addTransition(ChangeImageTransform()) // изменения внутри ImageView + addTransition(ChangeTransform()) // изменение размеров, углов накона + addTransition(ChangeBounds()) // изменение положения } } class FrontCardExitTransition : TransitionSet() { init { ordering = ORDERING_TOGETHER; + addTransition(Fade()) addTransition(ChangeImageTransform()) + addTransition(ChangeTransform()) addTransition(ChangeBounds()) } +} + +class HomeToOnboardingTransition : TransitionSet() { + init { + ordering = ORDERING_TOGETHER; + addTransition(Fade()) + addTransition(ChangeTransform()) + addTransition(ChangeBounds()) + } +} + +class InternalNoteLayoutTransition : TransitionSet() { + init { + ordering = ORDERING_TOGETHER; + addTransition(ChangeTransform()) + addTransition(ChangeBounds()) + addTransition(Fade()) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/UrlBitmapLoader.kt b/app/src/main/java/com/tangem/tap/domain/UrlBitmapLoader.kt index 6a63eaf0b8..c06d9b0001 100644 --- a/app/src/main/java/com/tangem/tap/domain/UrlBitmapLoader.kt +++ b/app/src/main/java/com/tangem/tap/domain/UrlBitmapLoader.kt @@ -39,8 +39,8 @@ open class DownloadTarget( protectedFromGarbageCollectorTargets.remove(this) } - override fun onBitmapFailed(e: java.lang.Exception?, errorDrawable: Drawable?) { - callback.invoke(Result.Failure(e)) + override fun onBitmapFailed(e: Exception?, errorDrawable: Drawable?) { + callback.invoke(Result.Failure(e ?: Exception("Unknown exception"))) protectedFromGarbageCollectorTargets.remove(this) } 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 f57259ac3c..6c3db01f3c 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 @@ -182,6 +182,8 @@ data class Artwork( const val MARTA_CARD_ID = "BC02" const val TWIN_CARD_1 = "https://app.tangem.com/cards/card_tg085.png" const val TWIN_CARD_2 = "https://app.tangem.com/cards/card_tg086.png" + + const val TEMP_CARDANO = "https://verify.tangem.com/card/artwork?artworkId=card_ru039&CID=CB19000000040976&publicKey=0416E29423A6CC77CD07CBA52873E8F6F894B1AFB18EB3688ACC2C8D8E5AC84B80B0BA1B17B85E578E47044CE96BCFF3FB4499FA4941CAD3C1EF300A492B5B9659" } } diff --git a/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt b/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt index 77c8be322d..12d5eaa695 100644 --- a/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt +++ b/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt @@ -102,13 +102,16 @@ class AppRatingLaunchObserver( private val K_FUNDS_FOUND_DATE = "fundsFoundDate" private val K_USER_WAS_INTERACT_WITH_RATING = "userWasInteractWithRating" + private val FUNDS_FOUND_DATE_UNDEFINED = -1L private val deferShowing = 20 private val firstShowing = 3 private var fundsFoundDate: Calendar? = null init { - val dateTimeMs = preferences.getLong(K_FUNDS_FOUND_DATE, -1) - if (dateTimeMs > 0) fundsFoundDate = Calendar.getInstance().apply { timeInMillis = dateTimeMs } + val msWhenFundsWasFound = preferences.getLong(K_FUNDS_FOUND_DATE, FUNDS_FOUND_DATE_UNDEFINED) + if (msWhenFundsWasFound != FUNDS_FOUND_DATE_UNDEFINED) { + fundsFoundDate = Calendar.getInstance().apply { timeInMillis = msWhenFundsWasFound } + } } fun foundWalletWithFunds() { diff --git a/app/src/main/java/com/tangem/tap/persistence/UsedCardsPrefStorage.kt b/app/src/main/java/com/tangem/tap/persistence/UsedCardsPrefStorage.kt index f903e6425e..17238689f5 100644 --- a/app/src/main/java/com/tangem/tap/persistence/UsedCardsPrefStorage.kt +++ b/app/src/main/java/com/tangem/tap/persistence/UsedCardsPrefStorage.kt @@ -3,7 +3,7 @@ package com.tangem.tap.persistence import android.content.SharedPreferences import androidx.core.content.edit import com.tangem.common.json.MoshiJsonConverter -import com.tangem.tap.common.extensions.replaceBy +import com.tangem.tap.common.extensions.replaceByOrAdd import timber.log.Timber /** @@ -48,18 +48,14 @@ class UsedCardsPrefStorage( return findCardInfo(cardId)?.isActivationStarted ?: false } - fun activate(cardId: String) { + fun activationFinished(cardId: String) { val restoredList = restore() - val foundItem = findCardInfo(cardId, restoredList)?.copy(isActivated = true) - ?: UsedCardInfo(cardId, isActivated = true) + val foundItem = findCardInfo(cardId, restoredList)?.copy(isActivationStarted = false) + ?: UsedCardInfo(cardId, isActivationStarted = false) save(foundItem, restoredList) } - fun wasActivated(cardId: String): Boolean { - return findCardInfo(cardId)?.isActivated ?: false - } - private fun findCardInfo(cardId: String, list: MutableList? = null): UsedCardInfo? { val findInList = list ?: restore() return findInList.firstOrNull { it.cardId == cardId } @@ -68,7 +64,7 @@ class UsedCardsPrefStorage( private fun save(usedCardInfo: UsedCardInfo?, usedCardsInfo: MutableList) { val info = usedCardInfo ?: return - usedCardsInfo.replaceBy(info) { it.cardId == info.cardId } + usedCardsInfo.replaceByOrAdd(info) { it.cardId == info.cardId } save(usedCardsInfo) } @@ -86,13 +82,13 @@ class UsedCardsPrefStorage( private fun restore(): MutableList { val json = preferences.getString(USED_CARDS_INFO, null) ?: return mutableListOf() return try { - jsonConverter.fromJson(json)!! + jsonConverter.fromJson(json, jsonConverter.typedList(UsedCardInfo::class.java))!! } catch (ex: Exception) { preferences.edit(true) { remove(USED_CARDS_INFO) } mutableListOf() } } - + companion object { private const val USED_CARDS_INFO = "usedCardsInfo" private const val SCANNED_CARDS_IDS_KEY = "scannedCardIds" @@ -103,5 +99,4 @@ private data class UsedCardInfo( val cardId: String, val isScanned: Boolean = false, val isActivationStarted: Boolean = false, - val isActivated: Boolean = false, ) \ No newline at end of file From 3abd3268ff302f776c99dc94c925edbece441f30 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 6 Oct 2021 22:15:59 +0300 Subject: [PATCH 054/189] Updated on 2026-08-14 --- .../res/drawable/anim_progress_balance.xml | 9 + app/src/main/res/drawable/bg_circle_gray.xml | 9 - .../res/drawable/ic_activation_success.xml | 24 +++ .../main/res/drawable/ic_arrow_refresh.xml | 9 + app/src/main/res/drawable/ic_copy_green.xml | 19 ++ .../main/res/drawable/ic_home_back_card.xml | 102 +++++++++++ .../main/res/drawable/ic_home_card_back.xml | 108 ----------- .../main/res/drawable/ic_home_card_front.xml | 126 ------------- .../main/res/drawable/ic_home_front_card.xml | 118 ++++++++++++ .../main/res/drawable/ic_progress_balance.xml | 10 ++ app/src/main/res/drawable/ic_share_green.xml | 12 ++ app/src/main/res/drawable/ripple.xml | 32 ++++ app/src/main/res/drawable/shape_circle.xml | 11 +- .../drawable/shape_rectangle_rounded_100.xml | 8 + .../drawable/shape_rectangle_rounded_8.xml | 8 + .../res/drawable/shape_refresh_button.xml | 15 ++ .../layout/dialog_onboarding_address_info.xml | 112 ++++++++++++ app/src/main/res/layout/fragment_home.xml | 170 ++++++------------ .../main/res/layout/fragment_onboarding.xml | 160 ----------------- .../res/layout/fragment_onboarding_main.xml | 57 ++++++ .../layout_onboarding_bottom_action_views.xml | 81 +++++++++ .../res/layout/layout_onboarding_note.xml | 85 +++++++++ .../main/res/layout/layout_pseudo_toolbar.xml | 23 +++ app/src/main/res/layout/layout_token.xml | 2 +- .../layout/lp_onboarding_create_wallet.xml | 85 +++++++++ .../main/res/layout/lp_onboarding_done.xml | 83 +++++++++ .../res/layout/lp_onboarding_topup_wallet.xml | 83 +++++++++ app/src/main/res/layout/view_confetti.xml | 13 ++ .../view_onboarding_refresh_balance.xml | 62 +++++++ .../res/layout/view_onboarding_tv_balance.xml | 27 +++ app/src/main/res/raw/anim_confetti.json | 1 + app/src/main/res/values/colors.xml | 4 +- .../main/res/values/strings_untranslated.xml | 1 + app/src/main/res/values/styles.xml | 20 ++- 34 files changed, 1166 insertions(+), 523 deletions(-) create mode 100644 app/src/main/res/drawable/anim_progress_balance.xml delete mode 100644 app/src/main/res/drawable/bg_circle_gray.xml create mode 100644 app/src/main/res/drawable/ic_activation_success.xml create mode 100644 app/src/main/res/drawable/ic_arrow_refresh.xml create mode 100644 app/src/main/res/drawable/ic_copy_green.xml create mode 100644 app/src/main/res/drawable/ic_home_back_card.xml delete mode 100644 app/src/main/res/drawable/ic_home_card_back.xml delete mode 100644 app/src/main/res/drawable/ic_home_card_front.xml create mode 100644 app/src/main/res/drawable/ic_home_front_card.xml create mode 100644 app/src/main/res/drawable/ic_progress_balance.xml create mode 100644 app/src/main/res/drawable/ic_share_green.xml create mode 100644 app/src/main/res/drawable/ripple.xml create mode 100644 app/src/main/res/drawable/shape_rectangle_rounded_100.xml create mode 100644 app/src/main/res/drawable/shape_rectangle_rounded_8.xml create mode 100644 app/src/main/res/drawable/shape_refresh_button.xml create mode 100644 app/src/main/res/layout/dialog_onboarding_address_info.xml delete mode 100644 app/src/main/res/layout/fragment_onboarding.xml create mode 100644 app/src/main/res/layout/fragment_onboarding_main.xml create mode 100644 app/src/main/res/layout/layout_onboarding_bottom_action_views.xml create mode 100644 app/src/main/res/layout/layout_onboarding_note.xml create mode 100644 app/src/main/res/layout/layout_pseudo_toolbar.xml create mode 100644 app/src/main/res/layout/lp_onboarding_create_wallet.xml create mode 100644 app/src/main/res/layout/lp_onboarding_done.xml create mode 100644 app/src/main/res/layout/lp_onboarding_topup_wallet.xml create mode 100644 app/src/main/res/layout/view_confetti.xml create mode 100644 app/src/main/res/layout/view_onboarding_refresh_balance.xml create mode 100644 app/src/main/res/layout/view_onboarding_tv_balance.xml create mode 100644 app/src/main/res/raw/anim_confetti.json diff --git a/app/src/main/res/drawable/anim_progress_balance.xml b/app/src/main/res/drawable/anim_progress_balance.xml new file mode 100644 index 0000000000..ada4670265 --- /dev/null +++ b/app/src/main/res/drawable/anim_progress_balance.xml @@ -0,0 +1,9 @@ + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_circle_gray.xml b/app/src/main/res/drawable/bg_circle_gray.xml deleted file mode 100644 index 94a585da83..0000000000 --- a/app/src/main/res/drawable/bg_circle_gray.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_activation_success.xml b/app/src/main/res/drawable/ic_activation_success.xml new file mode 100644 index 0000000000..7957c82493 --- /dev/null +++ b/app/src/main/res/drawable/ic_activation_success.xml @@ -0,0 +1,24 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_arrow_refresh.xml b/app/src/main/res/drawable/ic_arrow_refresh.xml new file mode 100644 index 0000000000..1acc645cec --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_refresh.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_copy_green.xml b/app/src/main/res/drawable/ic_copy_green.xml new file mode 100644 index 0000000000..a4145be642 --- /dev/null +++ b/app/src/main/res/drawable/ic_copy_green.xml @@ -0,0 +1,19 @@ + + + + diff --git a/app/src/main/res/drawable/ic_home_back_card.xml b/app/src/main/res/drawable/ic_home_back_card.xml new file mode 100644 index 0000000000..f15c47ba3f --- /dev/null +++ b/app/src/main/res/drawable/ic_home_back_card.xml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_home_card_back.xml b/app/src/main/res/drawable/ic_home_card_back.xml deleted file mode 100644 index 8a71b20ce5..0000000000 --- a/app/src/main/res/drawable/ic_home_card_back.xml +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_home_card_front.xml b/app/src/main/res/drawable/ic_home_card_front.xml deleted file mode 100644 index 3bbf5a93dd..0000000000 --- a/app/src/main/res/drawable/ic_home_card_front.xml +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_home_front_card.xml b/app/src/main/res/drawable/ic_home_front_card.xml new file mode 100644 index 0000000000..19969e806e --- /dev/null +++ b/app/src/main/res/drawable/ic_home_front_card.xml @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_progress_balance.xml b/app/src/main/res/drawable/ic_progress_balance.xml new file mode 100644 index 0000000000..d31defe1e7 --- /dev/null +++ b/app/src/main/res/drawable/ic_progress_balance.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_share_green.xml b/app/src/main/res/drawable/ic_share_green.xml new file mode 100644 index 0000000000..57ee232b90 --- /dev/null +++ b/app/src/main/res/drawable/ic_share_green.xml @@ -0,0 +1,12 @@ + + + diff --git a/app/src/main/res/drawable/ripple.xml b/app/src/main/res/drawable/ripple.xml new file mode 100644 index 0000000000..c6e07a265f --- /dev/null +++ b/app/src/main/res/drawable/ripple.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_circle.xml b/app/src/main/res/drawable/shape_circle.xml index f857819f4d..59a662d141 100644 --- a/app/src/main/res/drawable/shape_circle.xml +++ b/app/src/main/res/drawable/shape_circle.xml @@ -1,6 +1,9 @@ - - - + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_rectangle_rounded_100.xml b/app/src/main/res/drawable/shape_rectangle_rounded_100.xml new file mode 100644 index 0000000000..7f26fb8be5 --- /dev/null +++ b/app/src/main/res/drawable/shape_rectangle_rounded_100.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_rectangle_rounded_8.xml b/app/src/main/res/drawable/shape_rectangle_rounded_8.xml new file mode 100644 index 0000000000..a788f39093 --- /dev/null +++ b/app/src/main/res/drawable/shape_rectangle_rounded_8.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_refresh_button.xml b/app/src/main/res/drawable/shape_refresh_button.xml new file mode 100644 index 0000000000..ac9508438d --- /dev/null +++ b/app/src/main/res/drawable/shape_refresh_button.xml @@ -0,0 +1,15 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_onboarding_address_info.xml b/app/src/main/res/layout/dialog_onboarding_address_info.xml new file mode 100644 index 0000000000..92c780e969 --- /dev/null +++ b/app/src/main/res/layout/dialog_onboarding_address_info.xml @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index f8d352de67..710f915add 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -1,126 +1,72 @@ - + android:layout_height="match_parent" + android:background="@color/backgroundWhite" + android:fitsSystemWindows="true" + android:orientation="vertical"> - + android:fitsSystemWindows="false" + app:liftOnScroll="true"> - + - + - + - + - + - + - + - + - - - - - - + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_onboarding.xml b/app/src/main/res/layout/fragment_onboarding.xml deleted file mode 100644 index 6924a50981..0000000000 --- a/app/src/main/res/layout/fragment_onboarding.xml +++ /dev/null @@ -1,160 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_onboarding_main.xml b/app/src/main/res/layout/fragment_onboarding_main.xml new file mode 100644 index 0000000000..bfd04532b4 --- /dev/null +++ b/app/src/main/res/layout/fragment_onboarding_main.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_onboarding_bottom_action_views.xml b/app/src/main/res/layout/layout_onboarding_bottom_action_views.xml new file mode 100644 index 0000000000..f9542e5c56 --- /dev/null +++ b/app/src/main/res/layout/layout_onboarding_bottom_action_views.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_onboarding_note.xml b/app/src/main/res/layout/layout_onboarding_note.xml new file mode 100644 index 0000000000..9804063722 --- /dev/null +++ b/app/src/main/res/layout/layout_onboarding_note.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_pseudo_toolbar.xml b/app/src/main/res/layout/layout_pseudo_toolbar.xml new file mode 100644 index 0000000000..f9ae5fe2ab --- /dev/null +++ b/app/src/main/res/layout/layout_pseudo_toolbar.xml @@ -0,0 +1,23 @@ + + + + + + diff --git a/app/src/main/res/layout/layout_token.xml b/app/src/main/res/layout/layout_token.xml index 8e03d11ca1..fc9486f79c 100644 --- a/app/src/main/res/layout/layout_token.xml +++ b/app/src/main/res/layout/layout_token.xml @@ -20,7 +20,7 @@ android:id="@+id/v_token_background" android:layout_width="match_parent" android:layout_height="0dp" - android:background="@color/backgroundGray" + android:background="@color/backgroundLightGray" app:layout_constraintTop_toBottomOf="@id/tv_token_title" app:layout_constraintBottom_toBottomOf="@id/tv_token_fiat_amount"/> diff --git a/app/src/main/res/layout/lp_onboarding_create_wallet.xml b/app/src/main/res/layout/lp_onboarding_create_wallet.xml new file mode 100644 index 0000000000..7bccae6a3e --- /dev/null +++ b/app/src/main/res/layout/lp_onboarding_create_wallet.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/lp_onboarding_done.xml b/app/src/main/res/layout/lp_onboarding_done.xml new file mode 100644 index 0000000000..803e41075e --- /dev/null +++ b/app/src/main/res/layout/lp_onboarding_done.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/lp_onboarding_topup_wallet.xml b/app/src/main/res/layout/lp_onboarding_topup_wallet.xml new file mode 100644 index 0000000000..ab70711896 --- /dev/null +++ b/app/src/main/res/layout/lp_onboarding_topup_wallet.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_confetti.xml b/app/src/main/res/layout/view_confetti.xml new file mode 100644 index 0000000000..ad07b6e0bd --- /dev/null +++ b/app/src/main/res/layout/view_confetti.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_onboarding_refresh_balance.xml b/app/src/main/res/layout/view_onboarding_refresh_balance.xml new file mode 100644 index 0000000000..c9bf3aacb2 --- /dev/null +++ b/app/src/main/res/layout/view_onboarding_refresh_balance.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_onboarding_tv_balance.xml b/app/src/main/res/layout/view_onboarding_tv_balance.xml new file mode 100644 index 0000000000..3c353e9948 --- /dev/null +++ b/app/src/main/res/layout/view_onboarding_tv_balance.xml @@ -0,0 +1,27 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/raw/anim_confetti.json b/app/src/main/res/raw/anim_confetti.json new file mode 100644 index 0000000000..f2d1e7f974 --- /dev/null +++ b/app/src/main/res/raw/anim_confetti.json @@ -0,0 +1 @@ +{"v":"5.6.3","fr":30,"ip":0,"op":120,"w":1242,"h":2688,"nm":"Comp 1","ddd":1,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 14","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[692,1011.9999999999999,0],"ix":2},"a":{"a":0,"k":[71,-332,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-48,204],[72.555,109.763],[-9.41,53.993],[11.376,60.133],[-20.778,48.214],[-43.71,34.149],[-28,80]],"o":[[48,-204],[-46.308,-70.056],[6.44,-36.951],[-18.438,-97.46],[10.775,-25.002],[128,-100],[28,-80]],"v":[[71,-332],[151,-620],[209.484,-736.613],[141,-856],[199.096,-959.097],[193,-1114],[371,-1280]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":14.077,"s":[0]},{"t":46.384765625,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":11.923,"s":[0]},{"t":42.0771484375,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim In Out Path","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":2,"op":112,"st":2,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 13","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[648,1040,0],"ix":2},"a":{"a":0,"k":[27,-304,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[28,112],[154,-50],[122,52],[60,164]],"o":[[0,0],[-24,-96],[-150.386,48.827],[-141.68,-60.388],[-60,-164]],"v":[[27,-304],[-107,-530],[-249,-720],[-335,-908],[-537,-1284]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3.308,"s":[0]},{"t":35.615234375,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-1,"s":[0]},{"t":27,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim In Out Path","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":112,"st":-5,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 12","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[674,1228,0],"ix":2},"a":{"a":0,"k":[53,-116,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-34,-98],[21,-81],[-20,-38],[-18,-48],[-8,-34],[-62,-38],[-24,-36],[-62,-30],[-84,-22]],"o":[[34,98],[-11.008,42.46],[12.721,24.17],[18,48],[9.222,39.192],[62,38],[24,36],[62,30],[84,22]],"v":[[53,-116],[134,25],[94,107],[181,157],[150,265],[237,323],[250,463],[383,540],[480,694]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254901960784,0.807843137254902,0.3607843137254902,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":22.692,"s":[0]},{"t":55,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":20.538,"s":[0]},{"t":50.6923828125,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim In Out Path","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":6,"op":112,"st":6,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 11","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[704,1180,0],"ix":2},"a":{"a":0,"k":[83,-164,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-38,-60],[-40.126,23.279],[42.5,-0.5],[-35,2.5],[-47.28,-26.863],[-20,-30],[-21.113,-24.13],[-34,-42],[-16,-40]],"o":[[38,60],[65.5,-38],[-20.024,0.236],[41.06,-2.933],[44,25],[20,30],[14,16],[34,42],[16,40]],"v":[[83,-164],[166.5,-75],[217.5,-41.5],[222.5,-81.5],[377,-77],[378,1],[458,17],[478,91],[616,178]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":7.615,"s":[0]},{"t":39.9228515625,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3.308,"s":[0]},{"t":31.3076171875,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim In Out Path","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":112,"st":-3,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Shape Layer 10","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[730,1100,0],"ix":2},"a":{"a":0,"k":[109,-244,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-72,-10],[-51,10],[13,-16],[-24.905,-7.729],[-10,10]],"o":[[72,10],[51,-10],[-10.168,12.514],[29,9],[10,-10]],"v":[[109,-244],[483,-178],[533,-129],[556,-83],[618,-93]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":16.23,"s":[0]},{"t":48.5380859375,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":14.077,"s":[0]},{"t":44.23046875,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim In Out Path","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":3,"op":112,"st":3,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Shape Layer 9","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[678,1086,0],"ix":2},"a":{"a":0,"k":[57,-258,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-106,94],[-78,82],[-2,37],[-15,15],[-27.82,25.594],[-23,24],[-38.877,17.279],[-24,-14]],"o":[[106,-94],[78,-82],[2.405,-44.488],[18.028,-18.028],[25,-23],[19.594,-20.446],[36,-16],[24,14]],"v":[[57,-258],[204,-404],[337,-493],[346,-597],[426,-624],[454,-699],[542,-728],[611,-805]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":7.615,"s":[0]},{"t":39.9228515625,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3.308,"s":[0]},{"t":31.3076171875,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim In Out Path","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":112,"st":-3,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Shape Layer 8","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[640,1170,0],"ix":2},"a":{"a":0,"k":[19,-174,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[92,-22],[38,-38],[78,-8],[38,-10],[10,-20]],"o":[[-92,22],[-38,38],[-78,8],[-38,10],[-10,20]],"v":[[19,-174],[-115,-120],[-283,-46],[-423,-50],[-517,32]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":7.615,"s":[0]},{"t":39.9228515625,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5.462,"s":[0]},{"t":35.615234375,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim In Out Path","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Shape Layer 7","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[661,1191,0],"ix":2},"a":{"a":0,"k":[40,-153,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[16.398,-37.618],[29.992,-37.49],[41,-30],[60.195,-37.77],[80.591,1.465],[-31.635,-5.678],[109.189,24.816],[22,-30]],"o":[[-34,78],[-44,55],[-59.359,43.434],[-51,32],[-55,-1],[39,7],[-132,-30],[-22,30]],"v":[[40,-153],[-41,-58],[-104,84],[-253,140],[-359,228],[-381,175],[-459,204],[-652,321]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":18.385,"s":[0]},{"t":50.6923828125,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":14.077,"s":[0]},{"t":42.0771484375,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim In Out Path","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":2,"op":112,"st":2,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"Shape Layer 6","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[646,1126,0],"ix":2},"a":{"a":0,"k":[25,-218,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[90,52],[20,-18],[44,46],[60,-36],[34,64],[-46,-10],[34,-50],[52,12],[28,10]],"o":[[-90,-52],[-20,18],[-44,-46],[-60,36],[-34,-64],[46,10],[-34,50],[-52,-12],[-28,-10]],"v":[[25,-218],[-175,-248],[-271,-286],[-379,-328],[-487,-310],[-535,-444],[-469,-396],[-547,-342],[-627,-366]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5.462,"s":[0]},{"t":37.76953125,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3.308,"s":[0]},{"t":35.615234375,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim In Out Path","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":0,"ind":10,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":184,"ix":10},"p":{"a":0,"k":[633,1188,0],"ix":2},"a":{"a":0,"k":[12,-198,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":112,"st":0,"bm":0},{"ddd":1,"ind":11,"ty":4,"nm":"Shape Layer 41","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-11,"s":[0]},{"t":87.9189453125,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10,"x":"var $bm_rt;\n$bm_rt = transform.rotation;"},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0.179,"s":[6.395,-188.007,0],"to":[3.99,-626.075,396.962],"ti":[-3.99,626.075,-396.962]},{"t":34.7802734375,"s":[30.337,-3944.458,2381.774]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7,-27],[35,7],[-41.906,32.862],[0,0]],"o":[[-7,27],[32,27],[23.167,-18.167],[0,0]],"v":[[72,-317],[4,-279],[91,-263.167],[107.167,-289.667]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.345,0.381,0.671,0.5,0.491,0.265,0.624,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":12,"ty":4,"nm":"Shape Layer 40","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":80.50390625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0.179,"s":[6.395,-188.007,0],"to":[172.235,-647.46,153.434],"ti":[-172.235,647.46,-153.434]},{"t":80,"s":[1039.806,-4072.764,920.605]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":13,"ty":4,"nm":"Shape Layer 39","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0.179,"s":[0]},{"t":91.6259765625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0.179,"s":[6.395,-188.007,0],"to":[140.553,-166.199,-42.398],"ti":[-140.553,166.199,42.398]},{"t":15.0078125,"s":[849.715,-1185.203,-254.39]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":0,"ind":14,"ty":3,"nm":"Null 2","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":-49,"ix":10},"p":{"a":0,"k":[621,1344,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":112,"st":-4,"bm":0},{"ddd":0,"ind":15,"ty":3,"nm":"Null 3","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":82,"ix":10},"p":{"a":0,"k":[621,1344,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":112,"st":-4,"bm":0},{"ddd":1,"ind":16,"ty":4,"nm":"Shape Layer 105","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-11,"s":[0]},{"t":87.6826171875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10,"x":"var $bm_rt;\n$bm_rt = transform.rotation;"},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[727.321,51.433,435.455],"ti":[-727.321,-51.433,-435.455]},{"t":34.544921875,"s":[4250.962,186.167,2612.729]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7,-27],[35,7],[-41.906,32.862],[0,0]],"o":[[-7,27],[32,27],[23.167,-18.167],[0,0]],"v":[[72,-317],[4,-279],[91,-263.167],[107.167,-289.667]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.345,0.381,0.671,0.5,0.491,0.265,0.624,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":17,"ty":4,"nm":"Shape Layer 104","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":80.50390625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[527.848,55.985,153.434],"ti":[-527.848,-55.985,-153.434]},{"t":54.552734375,"s":[3054.124,213.477,920.605]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":18,"ty":4,"nm":"Shape Layer 103","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":91.6259765625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[4.145,138.496,-42.398],"ti":[-4.145,-138.496,42.398]},{"t":15.0078125,"s":[-88.093,708.543,-254.39]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":19,"ty":4,"nm":"Shape Layer 102","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":94.09765625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":-0.179,"s":[-112.965,-122.431,0],"to":[-61.615,187.567,26.907],"ti":[61.615,-187.567,-26.907]},{"t":31.0732421875,"s":[-482.654,1002.974,161.442]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":20,"ty":4,"nm":"Shape Layer 101","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-1.057,"s":[0]},{"t":84.2109375,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.824},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[-90.863,90.863,0],"ti":[88.083,-357.903,-768.642]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.164},"t":17.479,"s":[-658.144,422.748,0],"to":[-84.157,341.951,734.383],"ti":[-714.89,-84.078,-197.259]},{"t":86.6826171875,"s":[3631.196,927.217,1183.551]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":21,"ty":4,"nm":"Shape Layer 100","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":100.2763671875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.796},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[-134.704,42.073,0],"ti":[7.203,-205.415,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.157},"t":28,"s":[-921.188,130.006,0],"to":[-19.567,558.035,0],"ti":[-540.328,-229.432,0]},{"t":116,"s":[2320.778,1506.598,0]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":22,"ty":4,"nm":"Shape Layer 99","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":86.6826171875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.775},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[-115.257,9.245,0],"ti":[52.068,-163.912,-53.249]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.155},"t":19.951,"s":[-804.504,-66.963,0],"to":[-48.777,153.551,49.883],"ti":[-156.371,-109.917,-25.485]},{"t":91,"s":[2733.865,1051.661,319.492]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":23,"ty":4,"nm":"Shape Layer 98","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":146,"s":[1440]}],"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":146,"s":[1440]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.855},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[-138.996,-53.461,0],"ti":[-421.763,15.031,16.673]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.174},"t":21.187,"s":[-946.942,-443.198,0],"to":[421.763,-15.031,-16.673],"ti":[-560.76,-38.43,16.673]},{"t":100.2763671875,"s":[2417.616,-212.616,-100.037]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":24,"ty":4,"nm":"Shape Layer 97","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":144.7646484375,"s":[1080]}],"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":144.7646484375,"s":[59]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.673},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[-87.16,25.736,-45.544],"ti":[-273.542,-218.754,122.377]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.14},"t":31.073,"s":[-660.653,26.246,-273.265],"to":[129.994,103.957,-58.156],"ti":[-320.287,-255.677,-112.981]},{"t":111.3984375,"s":[3683.402,989.215,1150.251]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[3.444,9.757]],"o":[[0,0],[0,0],[0,0],[-4.941,-14]],"v":[[72,-317],[4,-279],[24.151,-217.936],[88.307,-269.617]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.345,0.381,0.671,0.5,0.491,0.265,0.624,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":25,"ty":4,"nm":"Shape Layer 96","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-9,"s":[0]},{"t":89.154296875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.853},"o":{"x":0.167,"y":0.167},"t":0,"s":[-89.038,-0.153,0],"to":[-552.568,-84.135,-9.152],"ti":[-1514.035,-462.134,43.281]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.183},"t":38,"s":[95.207,-392.324,-54.91],"to":[684.779,209.018,-19.576],"ti":[-315.276,-115.122,160.297]},{"t":97,"s":[1986.864,298.408,-1016.689]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":26,"ty":4,"nm":"Shape Layer 95","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-14,"s":[0]},{"t":81.740234375,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.774},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[-110.347,-144.949,-65.532],"ti":[-365.662,-268.839,-214.28]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.147},"t":26,"s":[-799.777,-997.863,-393.192],"to":[365.662,268.839,214.28],"ti":[-476.009,-413.788,-279.812]},{"t":76.796875,"s":[2056.275,1484.866,1285.683]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":27,"ty":4,"nm":"Shape Layer 94","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-9,"s":[0]},{"t":100.2763671875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[-90.481,-247.593,46.629],"ti":[90.481,247.593,-46.629]},{"t":44,"s":[-680.583,-1613.73,279.773]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":28,"ty":4,"nm":"Shape Layer 93","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":91.6259765625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[69.003,-110.281,-16.168],"ti":[-69.003,110.281,16.168]},{"t":32,"s":[276.323,-789.855,-97.005]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":29,"ty":4,"nm":"Shape Layer 92","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-10,"s":[0]},{"t":81.740234375,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[305.417,119.743,41.862],"ti":[-305.417,-119.743,-41.862]},{"t":27,"s":[1694.805,590.289,251.17]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":30,"ty":4,"nm":"Shape Layer 91","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":86.6826171875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[318.521,75.426,-43.518],"ti":[-318.521,-75.426,43.518]},{"t":24,"s":[1773.433,324.385,-261.107]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":31,"ty":4,"nm":"Shape Layer 90","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-4.765,"s":[0]},{"t":89.154296875,"s":[1080]}],"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":89.154296875,"s":[1800]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[77.354,134.863,-215.584],"ti":[-77.354,-134.863,215.584]},{"t":27.3662109375,"s":[326.43,681.005,-1293.506]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":32,"ty":4,"nm":"Shape Layer 89","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-1.057,"s":[0]},{"t":102.748046875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10,"x":"var $bm_rt;\n$bm_rt = transform.rotation;"},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[301.698,164.851,-47.459],"ti":[-301.698,-164.851,47.459]},{"t":103.9833984375,"s":[1697.223,866.674,-284.755]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[84.659,-333.362],[4,-279],[45.665,-243.617],[119.735,-297.935]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.345,0.381,0.671,0.5,0.491,0.265,0.624,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":33,"ty":4,"nm":"Shape Layer 88","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-17,"s":[0]},{"t":95.3330078125,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[159.503,223.195,-0.572],"ti":[-159.503,-223.195,0.572]},{"t":37.251953125,"s":[844.051,1216.741,-3.43]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":34,"ty":4,"nm":"Shape Layer 87","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-11,"s":[0]},{"t":101.51171875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[33.76,196.346,-7.096],"ti":[-33.76,-196.346,7.096]},{"t":40.958984375,"s":[89.594,1055.648,-42.576]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":35,"ty":4,"nm":"Shape Layer 86","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-11,"s":[0]},{"t":85.447265625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[-16.649,163.663,0],"ti":[16.649,-163.663,0]},{"t":24.89453125,"s":[-212.861,859.547,0]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":36,"ty":4,"nm":"Shape Layer 85","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":101.51171875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[-71.747,128.05,0],"ti":[71.747,-128.05,0]},{"t":49.609375,"s":[-543.449,645.87,0]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":37,"ty":4,"nm":"Shape Layer 84","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-4.765,"s":[0]},{"t":112.6337890625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.728},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[-81.837,27.324,33.544],"ti":[-153.275,-262.436,-33.544]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.131},"t":21,"s":[-603.986,41.512,201.262],"to":[153.275,262.436,33.544],"ti":[-235.112,-235.112,0]},{"t":50.845703125,"s":[806.688,1452.185,201.262]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":38,"ty":4,"nm":"Shape Layer 83","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-6,"s":[0]},{"t":85.447265625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.652},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[-171.831,20.57,3.486],"ti":[602.026,-193.886,-848.553]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.142},"t":35,"s":[-1143.953,0.988,20.917],"to":[-778.519,946.911,884.934],"ti":[-1189.71,-488.979,-884.978]},{"t":139,"s":[5994.306,2934.863,5330.786]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":39,"ty":4,"nm":"Shape Layer 82","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-23,"s":[0]},{"t":97.8046875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.883},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[-120.682,-96.313,17.582],"ti":[57.484,-266.769,15.802]},{"i":{"x":0.833,"y":0.817},"o":{"x":0.167,"y":0.198},"t":15.008,"s":[-837.059,-700.31,105.493],"to":[-25.433,118.027,-6.991],"ti":[-511.749,-461.825,46.841]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.143},"t":54.553,"s":[271.713,386.113,403.89],"to":[308.693,278.579,-28.255],"ti":[-142.63,-100.689,10.833]},{"t":76.796875,"s":[1060.877,1065.009,-67.253]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":40,"ty":4,"nm":"Shape Layer 81","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-12,"s":[0]},{"t":102.748046875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[135.751,-389.001,217.62],"ti":[-135.751,389.001,-217.62]},{"t":27,"s":[676.808,-2462.175,1305.721]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7,-27],[35,7],[-41.906,32.862],[0,0]],"o":[[-7,27],[32,27],[23.167,-18.167],[0,0]],"v":[[72,-317],[4,-279],[91,-263.167],[107.167,-289.667]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.345,0.381,0.671,0.5,0.491,0.265,0.624,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":41,"ty":4,"nm":"Shape Layer 80","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-25,"s":[0]},{"t":81.740234375,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.849},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[-70.649,26.407,0],"ti":[-291.085,-122.356,90.248]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.171},"t":16,"s":[-561.59,30.269,0],"to":[291.085,122.356,-90.248],"ti":[-361.735,-95.95,90.248]},{"t":82.9755859375,"s":[1608.818,605.966,-541.488]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":42,"ty":4,"nm":"Shape Layer 79","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":95.3330078125,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.704},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[-49.159,-63.557,4.223],"ti":[-0.005,148.838,-20.562]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.144},"t":24,"s":[-432.65,-509.511,25.34],"to":[0.017,-477.286,65.936],"ti":[-415.767,197.78,-61.713]},{"t":91.6259765625,"s":[2061.954,-1696.192,395.618]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":43,"ty":4,"nm":"Shape Layer 78","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-20,"s":[0]},{"t":87.9189453125,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[33.977,-166.361,22.596],"ti":[-33.977,166.361,-22.596]},{"t":30,"s":[66.167,-1126.338,135.575]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":44,"ty":4,"nm":"Shape Layer 77","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-16,"s":[0]},{"t":99.041015625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[345.992,-256.663,189.679],"ti":[-345.992,256.663,-189.679]},{"t":27,"s":[1938.258,-1668.151,1138.074]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":45,"ty":4,"nm":"Shape Layer 76","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":45.90234375,"s":[1080]}],"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":105.2197265625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.826},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[120.875,-60.552,0],"ti":[-340.061,254.939,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.163},"t":27.366,"s":[587.554,-491.48,0],"to":[340.061,-254.939,0],"ti":[-219.187,194.387,0]},{"t":82.9755859375,"s":[1902.674,-1657.802,0]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":46,"ty":4,"nm":"Shape Layer 75","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-18,"s":[0]},{"t":86.6826171875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.926},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[208.471,-17.277,73.2],"ti":[-257.939,74.184,269.358]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.228},"t":15.897,"s":[1113.129,-231.834,439.203],"to":[257.939,-74.184,-269.358],"ti":[-49.469,56.907,342.559]},{"t":90,"s":[1409.942,-573.277,-1616.149]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":47,"ty":4,"nm":"Shape Layer 74","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-24,"s":[0]},{"t":94.09765625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[308.185,154.066,17.938],"ti":[-308.185,-154.066,-17.938]},{"t":24.89453125,"s":[1711.415,796.226,107.63]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":48,"ty":4,"nm":"Shape Layer 73","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-11,"s":[0]},{"t":87.9189453125,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10,"x":"var $bm_rt;\n$bm_rt = transform.rotation;"},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-47.653,624.272,396.962],"ti":[47.653,-624.272,-396.962]},{"t":34.7802734375,"s":[-267.632,3580.053,2381.774]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7,-27],[35,7],[-41.906,32.862],[0,0]],"o":[[-7,27],[32,27],[23.167,-18.167],[0,0]],"v":[[72,-317],[4,-279],[91,-263.167],[107.167,-289.667]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.345,0.381,0.671,0.5,0.491,0.265,0.624,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":49,"ty":4,"nm":"Shape Layer 72","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":80.50390625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-143.363,403.815,153.434],"ti":[143.363,-403.815,-153.434]},{"t":54.552734375,"s":[-841.892,2257.314,920.605]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":50,"ty":4,"nm":"Shape Layer 71","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":91.6259765625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-196.222,199.787,-42.398],"ti":[196.222,-199.787,42.398]},{"t":32.30859375,"s":[-1159.042,1033.147,-254.39]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":51,"ty":4,"nm":"Shape Layer 70","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":94.09765625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.868},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-181.982,76.554,26.907],"ti":[353.39,-64.568,-26.907]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.207},"t":31.073,"s":[-1073.604,293.746,161.442],"to":[-353.39,64.568,26.907],"ti":[171.408,11.986,0]},{"t":73.08984375,"s":[-2102.054,221.83,161.442]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":52,"ty":4,"nm":"Shape Layer 69","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-1.057,"s":[0]},{"t":84.2109375,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-128.187,-8.964,0],"ti":[128.187,8.964,0]},{"t":33,"s":[-750.834,-219.36,0]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":53,"ty":4,"nm":"Shape Layer 68","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":100.2763671875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-120.126,-74.06,0],"ti":[120.126,74.06,0]},{"t":39,"s":[-702.471,-609.938,0]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":54,"ty":4,"nm":"Shape Layer 67","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":86.6826171875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.723},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-82.592,-80.92,0],"ti":[157.866,-68.24,-53.249]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.139},"t":27,"s":[-477.265,-651.099,0],"to":[-147.887,63.926,49.883],"ti":[-19.633,-190.127,-25.485]},{"t":80.50390625,"s":[-147.831,2175.133,319.492]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":55,"ty":4,"nm":"Shape Layer 66","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":146,"s":[1440]}],"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":146,"s":[1440]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.749},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-50.842,-139.975,0],"ti":[92.088,-447.218,16.673]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.14},"t":36,"s":[-286.764,-1005.43,0],"to":[-92.088,447.218,-16.673],"ti":[41.246,-587.193,16.673]},{"t":100.2763671875,"s":[-534.238,2517.728,-100.037]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":56,"ty":4,"nm":"Shape Layer 65","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":144.7646484375,"s":[1080]}],"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":144.7646484375,"s":[59]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.767},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[-6.333,-132.667,0],"ti":[83.687,200.398,-334.198]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.153},"t":26,"s":[-31.605,-984.007,0],"to":[-63.434,-151.901,253.321],"ti":[-17.165,-409.463,-112.981]},{"t":111.3984375,"s":[283.153,3184.715,1150.251]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[3.444,9.757]],"o":[[0,0],[0,0],[0,0],[-4.941,-14]],"v":[[72,-317],[4,-279],[24.151,-217.936],[88.307,-269.617]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.671,0.345,0.345,0.5,0.655,0.247,0.461,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":57,"ty":4,"nm":"Shape Layer 64","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-9,"s":[0]},{"t":89.154296875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.836},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[64.5,-169.594,-9.152],"ti":[-113.79,-117.061,113.731]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.168},"t":27,"s":[393.395,-1205.571,-54.91],"to":[155.887,160.368,-155.806],"ti":[28,-314.6,160.297]},{"t":79,"s":[225.395,682.029,-1016.689]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":58,"ty":4,"nm":"Shape Layer 63","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-14,"s":[0]},{"t":81.740234375,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.787},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[37,-178.375,-65.532],"ti":[-37,-452.343,-214.28]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.151},"t":28,"s":[228.395,-1258.256,-393.192],"to":[37,452.343,214.28],"ti":[0,-630.718,-279.812]},{"t":88,"s":[228.395,2526.051,1285.683]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":59,"ty":4,"nm":"Shape Layer 62","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-9,"s":[0]},{"t":100.2763671875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[127.5,-230.723,46.629],"ti":[-127.5,230.723,-46.629]},{"t":38,"s":[771.395,-1572.344,279.773]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":60,"ty":4,"nm":"Shape Layer 61","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":91.6259765625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[259.979,-20.273,-16.168],"ti":[-259.979,20.273,16.168]},{"t":19,"s":[1566.267,-309.648,-97.005]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":61,"ty":4,"nm":"Shape Layer 60","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-10,"s":[0]},{"t":81.740234375,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[110,309.06,41.862],"ti":[-110,-309.06,-41.862]},{"t":31,"s":[666.395,1666.35,251.17]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":62,"ty":4,"nm":"Shape Layer 59","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":86.6826171875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[111,205.897,-43.518],"ti":[-111,-205.897,43.518]},{"t":33,"s":[672.395,1047.376,-261.107]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":63,"ty":4,"nm":"Shape Layer 58","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-4.765,"s":[0]},{"t":89.154296875,"s":[1080]}],"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":89.154296875,"s":[1800]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[14.845,244.879,-215.584],"ti":[-14.845,-244.879,215.584]},{"t":27.3662109375,"s":[95.463,1281.264,-1293.506]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":64,"ty":4,"nm":"Shape Layer 57","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-1.057,"s":[0]},{"t":102.748046875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10,"x":"var $bm_rt;\n$bm_rt = transform.rotation;"},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[44.909,224.342,-47.459],"ti":[-44.909,-224.342,47.459]},{"t":58.259765625,"s":[287.74,1180.472,-284.755]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[84.659,-333.362],[4,-279],[45.665,-243.617],[119.735,-297.935]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.345,0.381,0.671,0.5,0.491,0.265,0.624,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":65,"ty":4,"nm":"Shape Layer 56","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-17,"s":[0]},{"t":95.3330078125,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-63.805,266.808,-0.572],"ti":[63.805,-266.808,0.572]},{"t":51,"s":[-364.539,1435.268,-3.43]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":66,"ty":4,"nm":"Shape Layer 55","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-11,"s":[0]},{"t":101.51171875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.877},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-126.036,154.294,-7.096],"ti":[142.905,-395.535,7.096]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.203},"t":39,"s":[-737.928,760.184,-42.576],"to":[-142.905,395.535,-7.096],"ti":[16.869,-241.241,0]},{"t":117,"s":[-839.144,2207.633,-42.576]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":67,"ty":4,"nm":"Shape Layer 54","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-11,"s":[0]},{"t":85.447265625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-233.472,311.697,-17.966],"ti":[233.472,-311.697,17.966]},{"t":24,"s":[-1382.547,1704.606,-107.798]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":68,"ty":4,"nm":"Shape Layer 53","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":101.51171875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.762},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-130.292,21.146,0],"ti":[485.802,-51.233,12.363]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.14},"t":22,"s":[-763.467,-38.702,0],"to":[-485.802,51.233,-12.363],"ti":[355.509,-30.087,12.363]},{"t":57,"s":[-2896.522,141.82,-74.176]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":69,"ty":4,"nm":"Shape Layer 52","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-4.765,"s":[0]},{"t":112.6337890625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.78},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-74.311,-43.837,33.544],"ti":[97.505,-287.852,-33.544]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.149},"t":34,"s":[-427.58,-428.6,201.262],"to":[-97.505,287.852,33.544],"ti":[23.194,-331.689,0]},{"t":102,"s":[-566.743,1561.534,201.262]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":70,"ty":4,"nm":"Shape Layer 51","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-6,"s":[0]},{"t":85.447265625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-124.762,-149.834,3.486],"ti":[124.762,149.834,-3.486]},{"t":26,"s":[-730.286,-1064.579,20.917]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":71,"ty":4,"nm":"Shape Layer 50","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-23,"s":[0]},{"t":97.8046875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.77},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-6.486,-154.267,17.582],"ti":[239.046,-131.632,15.802]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.142},"t":30,"s":[-20.629,-1091.18,105.493],"to":[-105.762,58.238,-6.991],"ti":[-17.583,-173.702,10.833]},{"t":76.796875,"s":[-107.774,1499.363,-67.253]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":72,"ty":4,"nm":"Shape Layer 49","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-12,"s":[0]},{"t":102.748046875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[382.643,-152.755,217.62],"ti":[-382.643,152.755,-217.62]},{"t":33,"s":[2302.253,-1104.538,1305.721]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7,-27],[35,7],[-41.906,32.862],[0,0]],"o":[[-7,27],[32,27],[23.167,-18.167],[0,0]],"v":[[72,-317],[4,-279],[91,-263.167],[107.167,-289.667]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.345,0.381,0.671,0.5,0.491,0.265,0.624,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":73,"ty":4,"nm":"Shape Layer 48","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-25,"s":[0]},{"t":81.740234375,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.831},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[64.5,-93.5,0],"ti":[-64.5,-194.5,90.248]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.166},"t":25,"s":[393.395,-749.007,0],"to":[64.5,194.5,-90.248],"ti":[0,-288,90.248]},{"t":82.9755859375,"s":[393.395,978.993,-541.488]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":74,"ty":4,"nm":"Shape Layer 47","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":95.3330078125,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.885},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[65.626,-110,4.223],"ti":[-147.468,-10.238,-26.905]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.19},"t":20,"s":[400.149,-848.007,25.34],"to":[361.407,25.09,65.936],"ti":[-121.292,-241.908,-61.713]},{"t":91.6259765625,"s":[1127.898,603.439,395.618]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":75,"ty":4,"nm":"Shape Layer 46","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-20,"s":[0]},{"t":87.9189453125,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.816},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[147.845,-83.5,22.596],"ti":[-263.557,-41.975,24.609]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.159},"t":24,"s":[893.467,-689.007,135.575],"to":[408.719,65.094,-38.164],"ti":[88.693,-68.281,27.128]},{"t":74,"s":[-447.485,1243.193,-405.659]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":76,"ty":4,"nm":"Shape Layer 45","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-16,"s":[0]},{"t":99.041015625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.875},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[217.737,24,189.679],"ti":[-563.841,-88.203,-189.679]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.201},"t":26,"s":[1312.816,-44.007,1138.074],"to":[563.841,88.203,189.679],"ti":[-346.104,-64.203,0]},{"t":76.796875,"s":[3389.44,341.208,1138.074]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":77,"ty":4,"nm":"Shape Layer 44","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":45.90234375,"s":[1080]}],"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":105.2197265625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.866},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[125,51.5,0],"ti":[-125,-69.882,151.388]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.196},"t":27.366,"s":[756.395,120.993,0],"to":[125,69.882,-151.388],"ti":[0,-18.382,151.388]},{"t":72,"s":[756.395,231.284,-908.328]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":78,"ty":4,"nm":"Shape Layer 43","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-18,"s":[0]},{"t":86.6826171875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[149.808,146,73.2],"ti":[-149.808,-146,-73.2]},{"t":26,"s":[905.246,687.993,439.203]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":79,"ty":4,"nm":"Shape Layer 42","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-24,"s":[0]},{"t":94.09765625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[85.912,333.667,17.938],"ti":[-85.912,-333.667,-17.938]},{"t":24.89453125,"s":[521.87,1813.993,107.63]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":80,"ty":4,"nm":"Shape Layer 38","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":94.09765625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[176.199,-89.062,26.907],"ti":[-176.199,89.062,-26.907]},{"t":31.0732421875,"s":[1063.587,-722.379,161.442]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":81,"ty":4,"nm":"Shape Layer 37","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-1.057,"s":[0]},{"t":84.2109375,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[128.5,0,0],"ti":[-128.5,0,0]},{"t":30,"s":[777.395,-188.007,0]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":82,"ty":4,"nm":"Shape Layer 36","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":100.2763671875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[125,65.5,0],"ti":[-125,-65.5,0]},{"t":22,"s":[756.395,204.993,0]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":83,"ty":4,"nm":"Shape Layer 35","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":86.6826171875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.782},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[88.036,74.962,0],"ti":[-235.276,83.183,-29.696]},{"i":{"x":0.833,"y":0.693},"o":{"x":0.167,"y":0.155},"t":30,"s":[534.609,261.763,0],"to":[283.126,-100.101,35.736],"ti":[32.848,188.294,-25.485]},{"t":123,"s":[-62.525,-2846.235,319.492]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":84,"ty":4,"nm":"Shape Layer 33","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":454.311,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":144.7646484375,"s":[59]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.737},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[42.167,-122.667,0],"ti":[-345.153,74.903,-198.64]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.151},"t":28,"s":[880.395,419.993,0],"to":[432.939,-93.953,249.162],"ti":[-17.165,-409.463,-112.981]},{"t":127,"s":[1064.167,5582.141,1150.251]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[3.444,9.757]],"o":[[0,0],[0,0],[0,0],[-4.941,-14]],"v":[[72,-317],[4,-279],[24.151,-217.936],[88.307,-269.617]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":85,"ty":4,"nm":"Shape Layer 32","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-9,"s":[0]},{"t":89.154296875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.841},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[21.5,-126.594,-9.152],"ti":[-109.46,-148.367,74.781]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.169},"t":25,"s":[756.395,396.429,-54.90999999999999],"to":[113.887,154.368,-77.806],"ti":[7.464,-520.935,-7.703]},{"t":124,"s":[711.613,3522.04,-8.689]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":86,"ty":4,"nm":"Shape Layer 31","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-14,"s":[0]},{"t":81.740234375,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.843},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[37,-178.375,-65.532],"ti":[-48.635,-750.211,-214.28]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.169},"t":29,"s":[849.395,85.744,-393.192],"to":[48.635,750.211,214.28],"ti":[-11.635,-928.586,-279.812]},{"t":145,"s":[919.2079999999999,5657.262,1285.683]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":87,"ty":4,"nm":"Shape Layer 30","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-9,"s":[0]},{"t":100.2763671875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[127.5,-230.723,46.629],"ti":[-127.5,230.723,-46.629]},{"t":25,"s":[1392.395,-228.34400000000002,279.773]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":88,"ty":4,"nm":"Shape Layer 29","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":91.6259765625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[128.5,-20.273,-16.168],"ti":[-128.5,20.273,16.168]},{"t":44,"s":[1398.395,1034.352,-97.005]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":89,"ty":4,"nm":"Shape Layer 28","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-10,"s":[0]},{"t":81.740234375,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[110,309.06,41.862],"ti":[-110,-309.06,-41.862]},{"t":41,"s":[1287.395,3010.35,251.17]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":90,"ty":4,"nm":"Shape Layer 27","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":86.6826171875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[111,205.897,-43.518],"ti":[-111,-205.897,43.518]},{"t":31,"s":[1293.395,2391.376,-261.107]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":91,"ty":4,"nm":"Shape Layer 26","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-4.765,"s":[0]},{"t":89.154296875,"s":[1080]}],"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":89.154296875,"s":[1800]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[1,335.659,-215.584],"ti":[-1,-335.659,215.584]},{"t":58,"s":[633.395,3169.948,-1293.506]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":92,"ty":4,"nm":"Shape Layer 25","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-1.057,"s":[0]},{"t":102.748046875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10,"x":"var $bm_rt;\n$bm_rt = transform.rotation;"},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[-147.439,-471.248,-47.459],"ti":[147.439,471.248,47.459]},{"t":137.349609375,"s":[-878.24,-3015.497,-284.755]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[84.659,-333.362],[4,-279],[45.665,-243.617],[119.735,-297.935]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.345,0.381,0.671,0.5,0.491,0.265,0.624,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":93,"ty":4,"nm":"Shape Layer 24","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-17,"s":[0]},{"t":95.3330078125,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[53.803,-474.312,-0.572],"ti":[-53.803,474.312,0.572]},{"t":71,"s":[329.216,-3033.882,-3.43]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":0,"ind":94,"ty":4,"nm":"Shape Layer 23","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[436.949,-283.973,0],"ti":[-436.949,283.973,0]},{"t":71,"s":[2628.09,-1891.843,0]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":95,"ty":4,"nm":"Shape Layer 22","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-11,"s":[0]},{"t":85.447265625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[127.5,-103.954,0],"ti":[-127.5,103.954,0]},{"t":34,"s":[771.395,-811.733,0]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":96,"ty":4,"nm":"Shape Layer 21","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":101.51171875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[128.5,-30.183,0],"ti":[-128.5,30.183,0]},{"t":26,"s":[777.395,-369.106,0]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":97,"ty":4,"nm":"Shape Layer 20","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-4.765,"s":[0]},{"t":112.6337890625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.716},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[77.188,38.547,33.544],"ti":[-71.564,470.424,-33.544]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.136},"t":35,"s":[469.524,43.272,201.262],"to":[71.564,-470.424,33.544],"ti":[5.624,508.971,0]},{"t":100,"s":[435.782,-3010.554,201.262]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":98,"ty":4,"nm":"Shape Layer 19","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-6,"s":[0]},{"t":85.447265625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[134.91,140.766,3.486],"ti":[-134.91,-140.766,-3.486]},{"t":21,"s":[815.857,656.587,20.917]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":99,"ty":4,"nm":"Shape Layer 18","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-23,"s":[0]},{"t":97.8046875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.828},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[17.232,153.439,17.582],"ti":[-229.282,147.986,15.802]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.165},"t":21,"s":[109.785,732.626,105.493],"to":[101.442,-65.474,-6.991],"ti":[29.657,172.052,10.833]},{"t":76.796875,"s":[76.884,-1982.01,-729.705]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":100,"ty":4,"nm":"Shape Layer 17","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-12,"s":[0]},{"t":102.748046875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[382.643,-152.755,217.62],"ti":[-382.643,152.755,-217.62]},{"t":25,"s":[2923.253,239.462,1305.721]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7,-27],[35,7],[-41.906,32.862],[0,0]],"o":[[-7,27],[32,27],[23.167,-18.167],[0,0]],"v":[[72,-317],[4,-279],[91,-263.167],[107.167,-289.667]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.345,0.381,0.671,0.5,0.491,0.265,0.624,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":101,"ty":4,"nm":"Shape Layer 16","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-25,"s":[0]},{"t":81.740234375,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.829},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[64.5,-93.5,0],"ti":[-78.679,-339.952,90.248]},{"i":{"x":0.833,"y":0.714},"o":{"x":0.167,"y":0.165},"t":30,"s":[1014.395,594.993,0],"to":[78.679,339.952,-90.248],"ti":[-14.179,-433.452,90.248]},{"t":121,"s":[1099.471,3195.705,-541.488]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":102,"ty":4,"nm":"Shape Layer 15","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.822]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":102,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.828},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[65.626,-110,4.223],"ti":[-147.468,-10.238,-26.905]},{"i":{"x":0.833,"y":0.887},"o":{"x":0.167,"y":0.163},"t":28,"s":[1021.149,495.993,25.34],"to":[361.407,25.09,65.936],"ti":[-126.131,-151.254,-61.713]},{"t":71,"s":[1777.9360000000001,1403.514,395.618]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":103,"ty":4,"nm":"Shape Layer 5","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-20,"s":[0]},{"t":87.9189453125,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[147.845,-83.5,22.596],"ti":[-147.845,83.5,-22.596]},{"t":19,"s":[1514.467,654.993,135.575]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":104,"ty":4,"nm":"Shape Layer 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-16,"s":[0]},{"t":99.041015625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[393.671,83.751,189.679],"ti":[-393.671,-83.751,-189.679]},{"t":58,"s":[2989.419,1658.499,1138.074]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":105,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":45.90234375,"s":[1080]}],"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":105.2197265625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[249,109.5,0],"ti":[-249,-109.5,0]},{"t":68,"s":[2121.395,1812.9929999999997,0]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":106,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-18,"s":[0]},{"t":86.6826171875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[499.008,399.431,73.2],"ti":[-499.008,-399.431,-73.2]},{"t":96,"s":[3621.4449999999997,3552.5799999999995,439.20300000000003]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":107,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-24,"s":[0]},{"t":94.09765625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[535.305,103.127,17.938],"ti":[-535.305,-103.127,-17.938]},{"t":82,"s":[3839.226,1774.756,107.63000000000001]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":0,"ind":108,"ty":1,"nm":"Medium Gray-Blue Solid 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[621,1344,0],"ix":2},"a":{"a":0,"k":[621,1344,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"sw":1242,"sh":2688,"sc":"#272d41","ip":112,"op":112,"st":0,"bm":0,"hidden":0}],"markers":[]} diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 509d2b72be..b4334ef500 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -16,6 +16,7 @@ #C4291C #FFB71B + #FFFFFF #8E8E93 #636366 #48484A @@ -23,7 +24,7 @@ #2C2C2E #1C1C1E - #F9F9FA + #FFFFFF #F9F9F9 #F4F5F6 #DE000000 @@ -31,6 +32,7 @@ #14181D + #F3F3F3 #F8F8FB #C7C7CC #D1D1D6 diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml index 888918f16b..17edb1b769 100644 --- a/app/src/main/res/values/strings_untranslated.xml +++ b/app/src/main/res/values/strings_untranslated.xml @@ -191,6 +191,7 @@ this wallet. Done! Your crypto card is activated and ready to be used Continue + Balance Internal error: can\'t create wallet manager diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 3642ef243c..703a685858 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -5,9 +5,11 @@ @color/colorPrimary @color/colorSecondary @color/darkGray6 - true @color/backgroundLightGray - false + + + + + + + + + \ No newline at end of file From a2883dbdd47eea47fe8db7ca556e81407f699758 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 6 Oct 2021 22:22:45 +0300 Subject: [PATCH 055/189] Updated on 2026-08-14 --- .../tap/common/redux/NotificationsMiddleware.kt | 2 +- .../main/java/com/tangem/tap/domain/TapErrors.kt | 13 +++++++++---- .../send/ui/stateSubscribers/SendStateSubscriber.kt | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt b/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt index c797764bbd..2ace551205 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt @@ -84,7 +84,7 @@ private fun handleNotificationAction(action: Action) { } else -> { val args = (action.error as? ArgError)?.args ?: listOf() - notificationsHandler?.showNotification(action.error.localizedMessage, args) + notificationsHandler?.showNotification(action.error.messageResource, args) } } } diff --git a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt index 8ad394c14f..c84e3f0d29 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt @@ -16,13 +16,13 @@ interface MultiMessageError : TapErrors { } sealed class TapError( - @StringRes val localizedMessage: Int, - override val args: List? = null + @StringRes val messageResource: Int, + override val args: List? = null ) : Throwable(), TapErrors, ArgError { object UnknownError : TapError(R.string.send_error_unknown) + open class CustomError(val customMessage: String) : TapError(R.string.common_custom_string, listOf(customMessage)) object ScanCardError : TapError(R.string.scan_card_error) - data class CustomError(val customMessage: String) : TapError(R.string.common_custom_string, listOf(customMessage)) object PayIdAlreadyCreated : TapError(R.string.wallet_create_payid_error_already_created) object PayIdCreatingError : TapError(R.string.wallet_create_payid_error_message) object PayIdEmptyField : TapError(R.string.wallet_create_payid_empty) @@ -43,6 +43,11 @@ sealed class TapError( object AssetAccountNotCreated : TapError(R.string.send_error_no_account_xlm) } + sealed class WalletManagerUpdate { + class NoAccountError(amountToCreateAccount: String): CustomError(amountToCreateAccount) + class InternalError(message: String): CustomError(message) + } + data class ValidateTransactionErrors( override val errorList: List, override val builder: (List) -> String @@ -61,7 +66,7 @@ fun TapErrors.assembleErrors(): MutableList?>> { val idList = mutableListOf?>>() when (this) { is MultiMessageError -> this.errorList.forEach { idList.addAll(it.assembleErrors()) } - is TapError -> idList.add(Pair(this.localizedMessage, this.args)) + is TapError -> idList.add(Pair(this.messageResource, this.args)) } return idList } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt index fb2633ff3a..2471b257de 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt @@ -172,7 +172,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber val messageList = multiError.assembleErrors().map { getMessageString(context, it.first, it.second) } multiError.builder(messageList) } - else -> context.getString(state.error.localizedMessage) + else -> context.getString(state.error.messageResource) } fg.tilAmountToSend.enableError(true, message) } else { From 261a16113fce2affa60e60792bf27a14b49c0298 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 6 Oct 2021 22:29:57 +0300 Subject: [PATCH 056/189] Updated on 2026-08-14 --- .../main/java/com/tangem/tap/domain/extensions/Blockchain.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 13946dd481..d658847574 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 @@ -16,7 +16,7 @@ fun Blockchain.isNoAccountError(exception: Throwable): Boolean { fun Blockchain.amountToCreateAccount(token: Token? = null): Double? { return when (this) { Blockchain.Stellar -> if (token?.symbol == NODL) 1.5 else 1.toDouble() - Blockchain.XRP -> 20.toDouble() + Blockchain.XRP -> 10.toDouble() else -> null } } From 5036f8a383293acf05101fee15f086e53b465c10 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 6 Oct 2021 22:30:19 +0300 Subject: [PATCH 057/189] Updated on 2026-08-14 --- .../main/java/com/tangem/tap/MainActivity.kt | 4 +- .../com/tangem/tap/common/DialogManager.kt | 7 +- .../tap/common/extensions/WalletManager.kt | 6 +- .../com/tangem/tap/common/redux/AppReducer.kt | 2 - .../com/tangem/tap/common/redux/AppState.kt | 4 - .../tangem/tap/common/redux/StateDialog.kt | 7 + .../tap/common/redux/global/GlobalAction.kt | 17 +- .../common/redux/global/GlobalMidlleware.kt | 13 +- .../tap/common/redux/global/GlobalReducer.kt | 6 + .../tap/common/redux/global/GlobalState.kt | 4 +- .../com/tangem/tap/domain/TangemSdkManager.kt | 21 +- .../com/tangem/tap/domain/TapWalletManager.kt | 19 +- .../com/tangem/tap/domain/TapWorkarounds.kt | 2 + .../tangem/tap/domain/tasks/ScanNoteTask.kt | 2 +- .../CreateProductWalletAndRescanTask.kt | 139 +++++++++ .../tasks/product/ProductCommandProcessor.kt | 16 + .../domain/tasks/product/ScanProductTask.kt | 141 +++++++++ .../tangem/tap/features/home/HomeFragment.kt | 12 +- .../tap/features/home/redux/HomeMiddleware.kt | 109 ++++--- .../AddressInfoBottomSheetDialog.kt | 44 +++ .../features/onboarding/OnboardingService.kt | 168 ---------- .../products/BaseOnboardingFragment.kt | 80 +++++ .../products/note/OnboardingNoteFragment.kt | 146 +++++---- .../note/redux/OnboardingNoteAction.kt | 21 +- .../note/redux/OnboardingNoteMiddleware.kt | 108 +++++-- .../note/redux/OnboardingNoteReducer.kt | 51 ++- .../note/redux/OnboardingNoteState.kt | 32 +- .../OnboardingOtherCardsFragment.kt | 19 +- .../wallet/OnboardingWalletFragment.kt | 11 - .../wallet/redux/OnboardingWalletState.kt | 3 +- .../onboarding/redux/OnboardingAction.kt | 22 -- .../onboarding/redux/OnboardingMiddleware.kt | 22 -- .../onboarding/redux/OnboardingReducer.kt | 24 -- .../onboarding/redux/OnboardingState.kt | 11 - .../onboarding/redux/StateSupportClasses.kt | 26 -- .../onboarding/service/OnboardingHelper.kt | 33 ++ .../service/OnboardingNoteService.kt | 21 ++ .../service/ProductOnboardingService.kt | 295 ++++++++++++++++++ .../tap/features/send/BaseStoreFragment.kt | 3 + 39 files changed, 1168 insertions(+), 503 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletAndRescanTask.kt create mode 100644 app/src/main/java/com/tangem/tap/domain/tasks/product/ProductCommandProcessor.kt create mode 100644 app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/AddressInfoBottomSheetDialog.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/OnboardingService.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/BaseOnboardingFragment.kt rename app/src/main/java/com/tangem/tap/features/onboarding/products/{old => otherCards}/OnboardingOtherCardsFragment.kt (62%) delete mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingAction.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingMiddleware.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingReducer.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingState.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/redux/StateSupportClasses.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingHelper.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingNoteService.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/service/ProductOnboardingService.kt diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index 37682e2bc8..49856db42b 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -75,8 +75,8 @@ class MainActivity : AppCompatActivity(), SnackbarHandler { val backStackIsEmpty = supportFragmentManager.backStackEntryCount == 0 val isScannedBefore = store.state.globalState.scanNoteResponse != null - val isOnboardingStarted = store.state.onboardingState.onboardingData?.scanNoteResponse != null - if ((backStackIsEmpty || isScannedBefore) && !isOnboardingStarted) { + val isOnboardingServiceActive = store.state.globalState.onboardingService != null + if (backStackIsEmpty || (!isOnboardingServiceActive && !isScannedBefore)) { store.dispatch(NavigationAction.NavigateTo(AppScreen.Home)) } intentHandler.handleIntent(intent) 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 218c96776f..114c17c7cd 100644 --- a/app/src/main/java/com/tangem/tap/common/DialogManager.kt +++ b/app/src/main/java/com/tangem/tap/common/DialogManager.kt @@ -6,8 +6,7 @@ import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.common.redux.global.GlobalState import com.tangem.tap.features.details.redux.walletconnect.WalletConnectDialog import com.tangem.tap.features.details.ui.walletconnect.dialogs.* -import com.tangem.tap.features.onboarding.products.note.OnboardingAddressInfoDialog -import com.tangem.tap.features.onboarding.redux.OnboardingAction +import com.tangem.tap.features.onboarding.AddressInfoBottomSheetDialog import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog import com.tangem.tap.store import com.tangem.wallet.R @@ -43,6 +42,7 @@ class DialogManager : StoreSubscriber { dialog = when (state.dialog) { is AppDialog.ScanFailsDialog -> ScanFailsDialog.create(context) + is AppDialog.AddressInfoDialog -> AddressInfoBottomSheetDialog(state.dialog, context) is WalletConnectDialog.UnsupportedCard -> SimpleAlertDialog.create( titleRes = R.string.wallet_connect, @@ -71,9 +71,6 @@ class DialogManager : StoreSubscriber { TransactionDialog.create(state.dialog.dialogData, context) is WalletConnectDialog.PersonalSign -> PersonalSignDialog.create(state.dialog.data, context) - is OnboardingAction.Dialog.AddressInfo -> { - OnboardingAddressInfoDialog(state.dialog, context) - } else -> null } dialog?.show() 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 d8b5b136dd..683f1574a3 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 @@ -12,7 +12,7 @@ import com.tangem.tap.network.NetworkConnectivity /** [REDACTED_AUTHOR] */ -suspend fun WalletManager.loadWalletData(): Result = try { +suspend fun WalletManager.safeUpdate(): Result = try { update() Result.Success(wallet) } catch (exception: Exception) { @@ -23,10 +23,10 @@ suspend fun WalletManager.loadWalletData(): Result = try { val amountToCreateAccount = blockchain.amountToCreateAccount(wallet.getFirstToken()) if (blockchain.isNoAccountError(exception) && amountToCreateAccount != null) { - Result.Failure(TapError.NoAccount(amountToCreateAccount.toString())) + Result.Failure(TapError.WalletManagerUpdate.NoAccountError(amountToCreateAccount.toString())) } else { val message = exception.localizedMessage ?: "Unknown exception during WalletManager update" - Result.Failure(TapError.CustomError(message)) + Result.Failure(TapError.WalletManagerUpdate.InternalError(message)) } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt index e60b9cade1..4de2f24938 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt @@ -8,7 +8,6 @@ import com.tangem.tap.features.disclaimer.redux.DisclaimerReducer import com.tangem.tap.features.home.redux.HomeReducer import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteReducer import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletReducer -import com.tangem.tap.features.onboarding.redux.OnboardingReducer import com.tangem.tap.features.send.redux.reducers.SendScreenReducer import com.tangem.tap.features.tokens.redux.TokensReducer import com.tangem.tap.features.wallet.redux.reducers.WalletReducer @@ -22,7 +21,6 @@ fun appReducer(action: Action, state: AppState?): AppState { navigationState = NavigationReducer.reduce(action, state), globalState = globalReducer(action, state), homeState = HomeReducer.reduce(action, state), - onboardingState = OnboardingReducer.reduce(action, state), onboardingNoteState = OnboardingNoteReducer.reduce(action, state), onboardingWalletState = OnboardingWalletReducer.reduce(action, state), walletState = WalletReducer.reduce(action, state), diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt index 985a2a75ba..ba9aa021d1 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt @@ -16,8 +16,6 @@ import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteMidd import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteState import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletMiddleware import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletState -import com.tangem.tap.features.onboarding.redux.OnboardingMiddleware -import com.tangem.tap.features.onboarding.redux.OnboardingState import com.tangem.tap.features.send.redux.middlewares.SendMiddleware import com.tangem.tap.features.send.redux.states.SendState import com.tangem.tap.features.tokens.redux.TokensMiddleware @@ -31,7 +29,6 @@ data class AppState( val navigationState: NavigationState = NavigationState(), val globalState: GlobalState = GlobalState(), val homeState: HomeState = HomeState(), - val onboardingState: OnboardingState = OnboardingState(), val onboardingNoteState: OnboardingNoteState = OnboardingNoteState(), val onboardingWalletState: OnboardingWalletState = OnboardingWalletState(), val walletState: WalletState = WalletState(), @@ -48,7 +45,6 @@ data class AppState( logMiddleware, navigationMiddleware, notificationsMiddleware, GlobalMiddleware.handler, HomeMiddleware.handler, - OnboardingMiddleware.handler, OnboardingNoteMiddleware.handler, OnboardingWalletMiddleware.handler, WalletMiddleware().walletMiddleware, diff --git a/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt b/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt index e643ceb207..36e1a749a5 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt @@ -1,5 +1,7 @@ package com.tangem.tap.common.redux +import com.tangem.tap.features.wallet.redux.AddressData + /** [REDACTED_AUTHOR] */ @@ -7,4 +9,9 @@ interface StateDialog sealed class AppDialog : StateDialog { object ScanFailsDialog : AppDialog() + data class AddressInfoDialog( + val addressData: AddressData, + val onCopyAddress: () -> Unit, + val onExploreAddress: () -> Unit + ) : AppDialog() } \ No newline at end of file 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 4c4675eb96..4021557fac 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,10 +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.redux.DebugErrorAction -import com.tangem.tap.common.redux.NotificationAction -import com.tangem.tap.common.redux.StateDialog -import com.tangem.tap.common.redux.ToastNotificationAction +import com.tangem.tap.common.redux.* import com.tangem.tap.domain.TapError import com.tangem.tap.domain.configurable.config.ConfigManager import com.tangem.tap.domain.configurable.warningMessage.WarningMessage @@ -15,6 +12,7 @@ import com.tangem.tap.domain.tasks.ScanNoteResponse import com.tangem.tap.features.details.redux.SecurityOption import com.tangem.tap.features.feedback.EmailData import com.tangem.tap.features.feedback.FeedbackManager +import com.tangem.tap.features.onboarding.service.ProductOnboardingService import com.tangem.tap.network.moonpay.MoonPayUserStatus import org.rekotlin.Action @@ -23,16 +21,21 @@ sealed class GlobalAction : Action { // notifications data class ShowNotification(override val messageResource: Int) : GlobalAction(), NotificationAction data class ShowToastNotification(override val messageResource: Int) : GlobalAction(), ToastNotificationAction + data class ShowErrorNotification(override val error: TapError) : GlobalAction(), ErrorAction + data class DebugShowErrorNotification(override val error: TapError) : GlobalAction(), DebugErrorAction // dialogs data class ShowDialog(val stateDialog: StateDialog) : GlobalAction() object HideDialog : GlobalAction() - data class DebugShowError(override val error: TapError) : GlobalAction(), DebugErrorAction + sealed class Onboarding { + data class Activate(val onboardingService: ProductOnboardingService) : GlobalAction() + object Deactivate : GlobalAction() + } data class ReadCard( - val onSuccess: (suspend (ScanNoteResponse) -> Unit)? = null, - val onFailure: (suspend (TangemError) -> Unit)? = null, + val onSuccess: ((ScanNoteResponse) -> Unit)? = null, + val onFailure: ((TangemError) -> Unit)? = null, val messageResId: Int? = null, ) : 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 f6e8152a77..2f8cbfffc4 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 @@ -6,6 +6,7 @@ import com.tangem.common.services.Result 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 import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.common.redux.AppState import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager @@ -16,9 +17,7 @@ import com.tangem.tap.preferencesStorage import com.tangem.tap.scope import com.tangem.tap.store import com.tangem.tap.tangemSdkManager -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import org.rekotlin.Middleware class GlobalMiddleware { @@ -73,7 +72,7 @@ private val globalMiddlewareHandler: Middleware = { dispatch, appState } is GlobalAction.UpdateFeedbackInfo -> { store.state.globalState.feedbackManager?.infoHolder - ?.setWalletsInfo(action.walletManagers) + ?.setWalletsInfo(action.walletManagers) } is GlobalAction.GetMoonPayUserStatus -> { val apiKey = appState()?.globalState?.configManager?.config?.moonPayApiKey @@ -82,7 +81,7 @@ private val globalMiddlewareHandler: Middleware = { dispatch, appState val userStatusResponse = MoonpayService().getUserStatus(apiKey) if (userStatusResponse is Result.Success) { store.dispatchOnMain( - GlobalAction.GetMoonPayUserStatus.Success(userStatusResponse.data) + GlobalAction.GetMoonPayUserStatus.Success(userStatusResponse.data) ) } } @@ -90,9 +89,9 @@ private val globalMiddlewareHandler: Middleware = { dispatch, appState } is GlobalAction.ReadCard -> { scope.launch { - val result = tangemSdkManager.scanNote(FirebaseAnalyticsHandler, action.messageResId) - withContext(Dispatchers.Main) { - store.dispatch(GlobalAction.ScanFailsCounter.ChooseBehavior(result)) + val result = tangemSdkManager.scanProduct(FirebaseAnalyticsHandler, action.messageResId) + store.dispatch(GlobalAction.ScanFailsCounter.ChooseBehavior(result)) + withMainContext { when (result) { is CompletionResult.Success -> { tangemSdkManager.changeDisplayedCardIdNumbersCount(result.data.card) 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 e1f1fa2217..380fe6847f 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 @@ -10,6 +10,12 @@ fun globalReducer(action: Action, state: AppState): GlobalState { val globalState = state.globalState return when (action) { + is GlobalAction.Onboarding.Activate -> { + globalState.copy(onboardingService = action.onboardingService) + } + is GlobalAction.Onboarding.Deactivate -> { + globalState.copy(onboardingService = null) + } is GlobalAction.ScanFailsCounter.Increment -> { globalState.copy(scanCardFailsCounter = globalState.scanCardFailsCounter + 1) } 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 558dcaab95..0753b6d472 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 @@ -8,6 +8,7 @@ import com.tangem.tap.domain.configurable.config.ConfigManager import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager import com.tangem.tap.domain.tasks.ScanNoteResponse import com.tangem.tap.features.feedback.FeedbackManager +import com.tangem.tap.features.onboarding.service.ProductOnboardingService import com.tangem.tap.network.coinmarketcap.CoinMarketCapService import com.tangem.tap.network.moonpay.MoonPayUserStatus import org.rekotlin.StateType @@ -24,7 +25,8 @@ data class GlobalState( val appCurrency: FiatCurrencyName = DEFAULT_FIAT_CURRENCY, val scanCardFailsCounter: Int = 0, val dialog: StateDialog? = null, - val moonPayUserStatus: MoonPayUserStatus? = null + val moonPayUserStatus: MoonPayUserStatus? = null, + val onboardingService: ProductOnboardingService? = null, ) : StateType typealias CryptoCurrencyName = String 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 adc0404f24..4cc78b775b 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -1,5 +1,6 @@ package com.tangem.tap.domain +import CreateProductWalletAndRescanTask import android.content.Context import com.tangem.Message import com.tangem.TangemSdk @@ -22,6 +23,7 @@ import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler import com.tangem.tap.domain.tasks.CreateWalletAndRescanTask import com.tangem.tap.domain.tasks.ScanNoteResponse import com.tangem.tap.domain.tasks.ScanNoteTask +import com.tangem.tap.domain.tasks.product.ScanProductTask import com.tangem.tap.domain.twins.isTangemTwin import com.tangem.wallet.R import kotlinx.coroutines.Dispatchers @@ -31,8 +33,10 @@ import kotlin.coroutines.suspendCoroutine class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Context) { + @Deprecated("Use can product") suspend fun scanNote( - analyticsHandler: AnalyticsHandler, messageRes: Int? = null, + analyticsHandler: AnalyticsHandler, + messageRes: Int? = null, ): CompletionResult { analyticsHandler.triggerEvent(AnalyticsEvent.READY_TO_SCAN, null) @@ -42,6 +46,21 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co ).also { sendScanFailuresToAnalytics(analyticsHandler, it) } } + suspend fun scanProduct( + analyticsHandler: AnalyticsHandler, + messageRes: Int? = null, + ): CompletionResult { + analyticsHandler.triggerEvent(AnalyticsEvent.READY_TO_SCAN, null) + + val message = Message(context.getString(messageRes ?: R.string.initial_message_scan_header)) + return runTaskAsyncReturnOnMain(ScanProductTask(), null, message) + .also { sendScanFailuresToAnalytics(analyticsHandler, it) } + } + + suspend fun createProductWallet(cardId: String?): CompletionResult { + return runTaskAsync(CreateProductWalletAndRescanTask(), cardId, Message(context.getString(R.string.initial_message_create_wallet_body))) + } + private fun sendScanFailuresToAnalytics( analyticsHandler: AnalyticsHandler, result: CompletionResult 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 6e4dec24f5..507db2ad21 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -6,7 +6,7 @@ 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.loadWalletData +import com.tangem.tap.common.extensions.safeUpdate import com.tangem.tap.common.redux.global.FiatCurrencyName import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.currenciesRepository @@ -40,22 +40,17 @@ class TapWalletManager { by lazy { WalletManagerFactory(blockchainSdkConfig) } suspend fun loadWalletData(walletManager: WalletManager) { - handleUpdateWalletResult(walletManager.loadWalletData(), walletManager) + handleUpdateWalletResult(walletManager.safeUpdate(), walletManager) } suspend fun updateWallet(walletManager: WalletManager) { - val result = try { - walletManager.update() - Result.Success(walletManager.wallet) - } catch (exception: Exception) { - Result.Failure(exception) - } + val result = walletManager.safeUpdate() withContext(Dispatchers.Main) { when (result) { is Result.Success -> store.dispatch(WalletAction.UpdateWallet.Success(result.data)) is Result.Failure -> - store.dispatch(WalletAction.UpdateWallet.Failure(result.error?.localizedMessage)) + store.dispatch(WalletAction.UpdateWallet.Failure(result.error.localizedMessage)) } } @@ -262,11 +257,11 @@ class TapWalletManager { TapError.NoInternetConnection -> { store.dispatch(WalletAction.LoadData.Failure(error)) } - is TapError.NoAccount -> { + is TapError.WalletManagerUpdate.NoAccountError -> { store.dispatch(WalletAction.LoadWallet - .NoAccount(walletManager.wallet, error.amountToCreateAccount)) + .NoAccount(walletManager.wallet, error.customMessage)) } - is TapError.CustomError -> { + is TapError.WalletManagerUpdate.InternalError -> { store.dispatch(WalletAction.LoadWallet .Failure(walletManager.wallet, error.customMessage)) } diff --git a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt index ffc138b170..9003c9f4fc 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt @@ -60,6 +60,8 @@ object TapWorkarounds { ) } +val DELAY_SDK_DIALOG_CLOSE = 1400L + val Card.isMultiwalletAllowed: Boolean get() { return !isTangemTwin() && !isStart2Coin && !isTangemNote() diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt index 510d2785d8..39f5d63af0 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt @@ -67,7 +67,7 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable= FirmwareVersion.MultiWalletAvailable) { + } else if (!card.isTangemNote() && card.firmwareVersion >= FirmwareVersion.MultiWalletAvailable) { createMissingWalletsIfNeeded(card, session, callback) } else { callback(CompletionResult.Success( diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletAndRescanTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletAndRescanTask.kt new file mode 100644 index 0000000000..6134e5de55 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletAndRescanTask.kt @@ -0,0 +1,139 @@ +import com.tangem.common.CompletionResult +import com.tangem.common.card.Card +import com.tangem.common.card.EllipticCurve +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.operations.PreflightReadMode +import com.tangem.operations.PreflightReadTask +import com.tangem.operations.wallet.CreateWalletCommand +import com.tangem.operations.wallet.CreateWalletResponse +import com.tangem.tap.domain.TapWorkarounds.getTangemNoteBlockchain +import com.tangem.tap.domain.TapWorkarounds.isTangemNote +import com.tangem.tap.domain.TapWorkarounds.isTangemWallet +import com.tangem.tap.domain.tasks.product.ProductCommandProcessor +import com.tangem.tap.domain.twins.isTangemTwin + +/** +[REDACTED_AUTHOR] + */ +class CreateProductWalletAndRescanTask : CardSessionRunnable { + + override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { + CreateProductWalletTask().run(session) { result -> + when (result) { + is CompletionResult.Success -> PreflightReadTask(PreflightReadMode.FullCardRead).run(session, callback) + is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) + } + } + } +} + +private class CreateProductWalletTask : CardSessionRunnable { + + override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { + val card = session.environment.card.guard { + callback(CompletionResult.Failure(TangemSdkError.CardError())) + return + } + + val commandProcessor = when { + card.isTangemNote() -> CreateWalletTangemNote() + card.isTangemTwin() -> CreateWalletTangemTwin() + card.isTangemWallet() -> CreateWalletTangemWallet() + else -> CreateWalletOtherCards() + } + commandProcessor.proceed(card, session, callback) + } +} + +private class CreateWalletTangemNote : ProductCommandProcessor { + override fun proceed( + card: Card, + session: CardSession, + callback: (result: CompletionResult) -> Unit + ) { + if (card.supportedCurves.isEmpty()) { + callback(CompletionResult.Failure(TangemSdkError.CardError())) + return + } + + val curvesSupportedByBlockchain = card.getTangemNoteBlockchain()?.getSupportedCurves() + if (curvesSupportedByBlockchain == null || curvesSupportedByBlockchain.isEmpty()) { + callback(CompletionResult.Failure(TangemSdkError.CardError())) + return + } + + val intersectCurves = card.supportedCurves.intersect(curvesSupportedByBlockchain).toList() + if (intersectCurves.isEmpty()) { + callback(CompletionResult.Failure(TangemSdkError.CardError())) + } else { + val curve = if (intersectCurves.contains(EllipticCurve.Secp256k1)) { + EllipticCurve.Secp256k1 + } else { + intersectCurves[0] + } + CreateWalletCommand(curve).run(session, callback) + + } + } +} + +private class CreateWalletTangemWallet : ProductCommandProcessor { + override fun proceed( + card: Card, + session: CardSession, + callback: (result: CompletionResult) -> Unit + ) { + throw UnsupportedOperationException() + } +} + +private class CreateWalletTangemTwin : ProductCommandProcessor { + override fun proceed( + card: Card, + session: CardSession, + callback: (result: CompletionResult) -> Unit + ) { + throw UnsupportedOperationException() + } +} + +private class CreateWalletOtherCards : ProductCommandProcessor { + + private var index = 0 + + private val curves = EllipticCurve.values().toList() + + override fun proceed( + card: Card, + session: CardSession, + callback: (result: CompletionResult) -> Unit + ) { + val curve = curves[index] + createWallet(curve, session, callback) + } + + private fun createWallet( + curve: EllipticCurve, + session: CardSession, + callback: (result: CompletionResult) -> Unit + ) { + CreateWalletCommand(curve).run(session) { result -> + when (result) { + is CompletionResult.Success -> { + if (index == curves.lastIndex) { + callback(CompletionResult.Success(result.data)) + return@run + } + index += 1 + createWallet(curves[index], session, 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/ProductCommandProcessor.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/ProductCommandProcessor.kt new file mode 100644 index 0000000000..c0810ff887 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/ProductCommandProcessor.kt @@ -0,0 +1,16 @@ +package com.tangem.tap.domain.tasks.product + +import com.tangem.common.CompletionResult +import com.tangem.common.card.Card +import com.tangem.common.core.CardSession + +/** +[REDACTED_AUTHOR] + */ +interface ProductCommandProcessor { + fun proceed( + card: Card, + session: CardSession, + callback: (result: CompletionResult) -> Unit, + ) +} \ No newline at end of file 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 new file mode 100644 index 0000000000..710e6e99ff --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt @@ -0,0 +1,141 @@ +package com.tangem.tap.domain.tasks.product + +import com.tangem.* +import com.tangem.common.CompletionResult +import com.tangem.common.card.Card +import com.tangem.common.core.CardSession +import com.tangem.common.core.CardSessionRunnable +import com.tangem.common.core.TangemError +import com.tangem.common.extensions.toHexString +import com.tangem.operations.ScanTask +import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand +import com.tangem.tap.domain.TapSdkError +import com.tangem.tap.domain.TapWorkarounds.isExcluded +import com.tangem.tap.domain.TapWorkarounds.isTangemNote +import com.tangem.tap.domain.TapWorkarounds.isTangemWallet +import com.tangem.tap.domain.extensions.getSingleWallet +import com.tangem.tap.domain.tasks.CreateWalletsTask +import com.tangem.tap.domain.tasks.ScanNoteResponse +import com.tangem.tap.domain.twins.TwinCardsManager +import com.tangem.tap.domain.twins.isTangemTwin + +class ScanProductTask(val card: Card? = null) : CardSessionRunnable { + + override fun run( + session: CardSession, + callback: (result: CompletionResult) -> Unit, + ) { + ScanTask().run(session) { result -> + when (result) { + is CompletionResult.Success -> { + val card = this.card ?: result.data + + val error = getErrorIfExcludedCard(card) + if (error != null) { + callback(CompletionResult.Failure(error)) + return@run + } + + val commandProcessor = when { + card.isTangemNote() -> ScanNoteProcessor() + card.isTangemTwin() -> ScanTwinProcessor() + card.isTangemWallet() -> ScanWalletProcessor() + else -> ScanOtherCardsProcessor() + } + commandProcessor.proceed(card, session, callback) + } + is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) + } + } + } + + private fun getErrorIfExcludedCard(card: Card): TangemError? { + if (card.isExcluded()) return TapSdkError.CardForDifferentApp + + return null + } +} + +private class ScanNoteProcessor : ProductCommandProcessor { + override fun proceed( + card: Card, + session: CardSession, + callback: (result: CompletionResult) -> Unit + ) { + callback(CompletionResult.Success(ScanNoteResponse(card, session.environment.walletData))) + } +} + +private class ScanWalletProcessor : ProductCommandProcessor { + override fun proceed( + card: Card, + session: CardSession, + callback: (result: CompletionResult) -> Unit + ) { + callback(CompletionResult.Success(ScanNoteResponse(card, session.environment.walletData))) + } +} + +private class ScanTwinProcessor : ProductCommandProcessor { + override fun proceed( + card: Card, + session: CardSession, + callback: (result: CompletionResult) -> Unit, + ) { + ReadIssuerDataCommand().run(session) { readDataResult -> + when (readDataResult) { + is CompletionResult.Success -> { + val publicKey = card.getSingleWallet()?.publicKey + if (publicKey == null) { + callback(CompletionResult.Success(ScanNoteResponse(card, null))) + return@run + } + val verified = TwinCardsManager.verifyTwinPublicKey( + readDataResult.data.issuerData, publicKey + ) + if (verified) { + val twinPublicKey = readDataResult.data.issuerData.sliceArray(0 until 65) + callback(CompletionResult.Success( + ScanNoteResponse( + card = card, + walletData = session.environment.walletData, + secondTwinPublicKey = twinPublicKey.toHexString()) + )) + return@run + } else { + callback(CompletionResult.Success(ScanNoteResponse(card, null))) + } + } + is CompletionResult.Failure -> + callback(CompletionResult.Success(ScanNoteResponse(card, null))) + } + } + } + +} + +private class ScanOtherCardsProcessor : ProductCommandProcessor { + override fun proceed(card: Card, session: CardSession, callback: (result: CompletionResult) -> Unit) { + val walletData = session.environment.walletData + if (card.wallets.isEmpty()) { + callback(CompletionResult.Success(ScanNoteResponse(card, walletData))) + return + } + + val curvesPresent = card.wallets.map { it.curve } + val curvesToCreate = card.supportedCurves.subtract(curvesPresent) + + if (curvesToCreate.isEmpty()) { + callback(CompletionResult.Success(ScanNoteResponse(card, walletData))) + return + } + + CreateWalletsTask().run(session) { result -> + when (result) { + is CompletionResult.Success -> + callback(CompletionResult.Success(ScanNoteResponse(result.data, walletData))) + is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt index 7633a60893..71e0b7a83c 100644 --- a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt @@ -15,6 +15,7 @@ import com.tangem.tap.features.home.redux.HomeState import com.tangem.tap.store import com.tangem.wallet.R import kotlinx.android.synthetic.main.fragment_home.* +import kotlinx.android.synthetic.main.layout_onboarding_bottom_action_views.* import org.rekotlin.StoreSubscriber import java.lang.ref.WeakReference @@ -40,10 +41,15 @@ class HomeFragment : Fragment(R.layout.fragment_home), StoreSubscriber = { dispatch, state -> store.dispatch(GlobalAction.GetMoonPayUserStatus) store.dispatch(HomeAction.SetTermsOfUseState(preferencesStorage.wasDisclaimerAccepted())) } - is HomeAction.ReadCard -> { - if (!preferencesStorage.wasDisclaimerAccepted()) { - store.dispatch(NavigationAction.NavigateTo(AppScreen.Disclaimer)) - } else { - launchOnboardingService() - } - } + is HomeAction.ReadCard -> handleReadCard() is HomeAction.GoToShop -> store.dispatchOpenUrl(HomeMiddleware.CARD_SHOP_URI) } next(action) @@ -46,39 +47,59 @@ private val homeMiddleware: Middleware = { dispatch, state -> } } -private fun launchOnboardingService() { - fun changeButtonState(state: ButtonState) { - val btnState = IndeterminateProgressButton(state) - store.dispatch(HomeAction.ChangeScanCardButtonState(btnState)) +private fun handleReadCard() { + if (!preferencesStorage.wasDisclaimerAccepted()) { + store.dispatch(NavigationAction.NavigateTo(AppScreen.Disclaimer)) + } else { + fun changeButtonState(state: ButtonState) { + val btnState = IndeterminateProgressButton(state) + store.dispatch(HomeAction.ChangeScanCardButtonState(btnState)) + } + + fun proceedToWalletScreen(tapWalletManager: TapWalletManager, scanResponse: ScanNoteResponse) { + changeButtonState(ButtonState.ENABLED) + scope.launch { + tapWalletManager.onCardScanned(scanResponse) + delay(DELAY_SDK_DIALOG_CLOSE) + store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) + } + } + changeButtonState(ButtonState.PROGRESS) + + store.dispatch(GlobalAction.ReadCard({ scanResponse -> + val tapWalletManager = store.state.globalState.tapWalletManager + if (!OnboardingHelper.isOnboardingCase(scanResponse)) { + proceedToWalletScreen(tapWalletManager, scanResponse) + return@ReadCard + } + + val service = OnboardingHelper.createService( + AppScreen.Home, + scanResponse, + tapWalletManager.walletManagerFactory + ) + if (service == null) { + proceedToWalletScreen(tapWalletManager, scanResponse) + return@ReadCard + } + + service.onInitializationProgress = { + when (it) { + ProgressState.Loading -> changeButtonState(ButtonState.PROGRESS) + ProgressState.Done -> changeButtonState(ButtonState.ENABLED) + ProgressState.Error -> changeButtonState(ButtonState.ENABLED) + } + } + service.onInitialized = { + store.dispatch(GlobalAction.Onboarding.Activate(service)) + store.dispatch(NavigationAction.NavigateTo(it, store.state.homeState.shareTransition)) + } + service.onError = { + store.dispatchErrorNotification(it) + } + post(DELAY_SDK_DIALOG_CLOSE) { service.initializeOnboarding() } + }, { + changeButtonState(ButtonState.ENABLED) + })) } - changeButtonState(ButtonState.PROGRESS) - - val walletManager = store.state.globalState.tapWalletManager - val service = OnboardingService( - walletManager, - preferencesStorage.usedCardsPrefStorage - ) - - service.onReadyToProceed = { - store.dispatch(OnboardingAction.SetData(it)) - changeButtonState(ButtonState.ENABLED) - val sharedTransition = store.state.homeState.shareTransition - store.dispatch(NavigationAction.NavigateTo(it.onboardingScreen, fragmentShareTransition = sharedTransition)) - } - - service.onFailedToProceedToOnboardingCase = { - changeButtonState(ButtonState.ENABLED) - walletManager.onCardScanned(it) - store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) - } - - service.onError = { - changeButtonState(ButtonState.ENABLED) - store.dispatch(GlobalAction.DebugShowError(TapError.CustomError(it))) - } - - store.dispatch(GlobalAction.ReadCard(service::onSuccess, { - changeButtonState(ButtonState.ENABLED) - store.dispatch(GlobalAction.DebugShowError(TapError.ScanCardError)) - })) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/AddressInfoBottomSheetDialog.kt b/app/src/main/java/com/tangem/tap/features/onboarding/AddressInfoBottomSheetDialog.kt new file mode 100644 index 0000000000..0b5b229bc4 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/AddressInfoBottomSheetDialog.kt @@ -0,0 +1,44 @@ +package com.tangem.tap.features.onboarding + +import android.content.Context +import com.google.android.material.bottomsheet.BottomSheetDialog +import com.tangem.tap.common.extensions.copyToClipboard +import com.tangem.tap.common.extensions.dispatchDialogHide +import com.tangem.tap.common.redux.AppDialog +import com.tangem.tap.store +import com.tangem.wallet.R +import kotlinx.android.synthetic.main.dialog_onboarding_address_info.* +import kotlinx.android.synthetic.main.layout_pseudo_toolbar.* + +/** +[REDACTED_AUTHOR] + */ +class AddressInfoBottomSheetDialog( + private val stateDialog: AppDialog.AddressInfoDialog, + context: Context +) : BottomSheetDialog(context) { + + init { + this.setContentView(R.layout.dialog_onboarding_address_info) + setOnCancelListener { store.dispatchDialogHide() } + } + + override fun show() { + super.show() + val data = stateDialog.addressData + + imv_close.setOnClickListener { + dismissWithAnimation = true + cancel() + } + imv_qr_code.setImageBitmap(data.qrCode) + tv_address.text = data.address + btn_fl_copy_address.setOnClickListener { + context.copyToClipboard(data.address) + stateDialog.onCopyAddress() + } + btn_fl_explore.setOnClickListener { + stateDialog.onExploreAddress() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingService.kt b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingService.kt deleted file mode 100644 index 2fc0925f2e..0000000000 --- a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingService.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.tangem.tap.features.onboarding - -import com.tangem.blockchain.common.AmountType -import com.tangem.blockchain.common.WalletManager -import com.tangem.blockchain.common.WalletManagerFactory -import com.tangem.common.card.Card -import com.tangem.common.extensions.guard -import com.tangem.common.services.Result -import com.tangem.operations.attestation.OnlineCardVerifier -import com.tangem.tap.common.redux.navigation.AppScreen -import com.tangem.tap.domain.TapWalletManager -import com.tangem.tap.domain.TapWorkarounds.isTangemNote -import com.tangem.tap.domain.TapWorkarounds.isTangemWallet -import com.tangem.tap.domain.UrlBitmapLoader -import com.tangem.tap.domain.extensions.getArtworkUrl -import com.tangem.tap.domain.extensions.hasWallets -import com.tangem.tap.domain.extensions.makePrimaryWalletManager -import com.tangem.tap.domain.tasks.ScanNoteResponse -import com.tangem.tap.domain.twins.isTangemTwin -import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteAction -import com.tangem.tap.features.onboarding.redux.OnboardingData -import com.tangem.tap.features.wallet.redux.Artwork -import com.tangem.tap.persistence.UsedCardsPrefStorage -import com.tangem.tap.store -import java.math.BigDecimal - -/** -[REDACTED_AUTHOR] - */ -class OnboardingService( - private val tapWalletManager: TapWalletManager, - private val cardInfoStorage: UsedCardsPrefStorage -) { - - var onReadyToProceed: ((OnboardingData) -> Unit)? = null - set(value) { - field = value - onboardingDataLinker.onReady = { value?.invoke(it) } - } - var onFailedToProceedToOnboardingCase: (suspend (ScanNoteResponse) -> Unit)? = null - var onError: ((String) -> Unit)? = null - - private val onboardingDataLinker = OnboardingDataLinker() - - suspend fun onSuccess(response: ScanNoteResponse) { - onboardingDataLinker.scanNoteResponse = response - val card = response.card - val hasWallets = card.hasWallets() - - if (!card.isTangemNote() && !card.isTangemWallet() && !card.isTangemTwin()) { - if (hasWallets) { - cardInfoStorage.activate(card.cardId) - onFailedToProceedToOnboardingCase?.invoke(response) - return - } else { - cardInfoStorage.activate(card.cardId) // вызвать только после того, как был создан кошелек - onboardingDataLinker.navigateTo = AppScreen.OnboardingOther - } - } - if (cardInfoStorage.wasActivated(card.cardId)) { - onFailedToProceedToOnboardingCase?.invoke(response) - return - } - - loadCardImage(card) { onboardingDataLinker.cardArtwork = it } - - if (hasWallets) { - val walletManagerFactory = tapWalletManager.walletManagerFactory - when { - card.isTangemNote() -> loadBalanceNote(walletManagerFactory, response) - card.isTangemWallet() -> loadBalanceWallet(walletManagerFactory, response) - card.isTangemTwin() -> loadBalanceTwins(walletManagerFactory, response) - } - } else { - onboardingDataLinker.valueOfWalletAmount = BigDecimal.ZERO - } - - onboardingDataLinker.navigateTo = when { - card.isTangemNote() -> AppScreen.OnboardingNote - card.isTangemWallet() -> AppScreen.OnboardingWallet - card.isTangemTwin() -> AppScreen.OnboardingTwins - else -> AppScreen.OnboardingOther - } - } - - private suspend fun loadBalanceNote(walletManagerFactory: WalletManagerFactory, response: ScanNoteResponse) { - val walletManager: WalletManager = walletManagerFactory.makePrimaryWalletManager(response).guard { - onError?.invoke("Can't proceed: primary wallet manager didn't create") - return - } - walletManager.update() - store.dispatch(OnboardingNoteAction.SetWalletManager(walletManager)) - - val wallet = walletManager.wallet - val valueOfAmount = wallet.amounts[AmountType.Coin]?.value - if (valueOfAmount == null) { - onError?.invoke("Amount can't be NULL") - return - } - - onboardingDataLinker.valueOfWalletAmount = valueOfAmount - } - - private suspend fun loadBalanceWallet(walletManagerFactory: WalletManagerFactory, response: ScanNoteResponse) { - - } - - private suspend fun loadBalanceTwins(walletManagerFactory: WalletManagerFactory, response: ScanNoteResponse) { - - } - - private suspend fun loadCardImage(card: Card, callback: (Artwork?) -> Unit) { - fun loadBitmap(url: String, callback: (Artwork?) -> Unit) { - UrlBitmapLoader().loadBitmap(url) { - when (it) { - is Result.Success -> callback(Artwork(url, it.data)) - is Result.Failure -> callback(null) - } - } - } - - when (val cardInfoResult = OnlineCardVerifier().getCardInfo(card.cardId, card.cardPublicKey)) { - is Result.Success -> { - val artworkUrl = card.getArtworkUrl(cardInfoResult.data.artwork?.id) ?: Artwork.DEFAULT_IMG_URL - loadBitmap(artworkUrl, callback) - } - is Result.Failure -> loadBitmap(Artwork.DEFAULT_IMG_URL, callback) - } - } - -} - -class OnboardingDataLinker { - var onReady: ((OnboardingData) -> Unit)? = null - - internal var scanNoteResponse: ScanNoteResponse? = null - set(value) { - field = value - tryToProceed() - } - internal var cardArtwork: Artwork? = null - set(value) { - field = value - tryToProceed() - } - internal var valueOfWalletAmount: BigDecimal? = null - set(value) { - field = value - tryToProceed() - } - internal var navigateTo: AppScreen? = null - set(value) { - field = value - tryToProceed() - } - - private fun tryToProceed() { - if (!isReady()) return - - val data = OnboardingData(scanNoteResponse!!, cardArtwork!!, valueOfWalletAmount, navigateTo!!) - onReady?.invoke(data) - } - - private fun isReady(): Boolean { - val objList = mutableListOf(scanNoteResponse, cardArtwork, valueOfWalletAmount, navigateTo) - return !objList.any { it == null } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/BaseOnboardingFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/BaseOnboardingFragment.kt new file mode 100644 index 0000000000..b27d17e06a --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/BaseOnboardingFragment.kt @@ -0,0 +1,80 @@ +package com.tangem.tap.features.onboarding.products + +import android.os.Bundle +import android.view.View +import androidx.transition.TransitionInflater +import com.tangem.tap.common.extensions.inflate +import com.tangem.tap.common.extensions.show +import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.common.redux.navigation.NavigationAction +import com.tangem.tap.common.transitions.HomeToOnboardingTransition +import com.tangem.tap.features.send.BaseStoreFragment +import com.tangem.tap.store +import com.tangem.wallet.R +import kotlinx.android.synthetic.main.fragment_onboarding_main.* +import kotlinx.android.synthetic.main.layout_pseudo_toolbar.* +import kotlinx.android.synthetic.main.view_confetti.* +import org.rekotlin.StoreSubscriber + +/** +[REDACTED_AUTHOR] + */ +abstract class BaseOnboardingFragment : BaseStoreFragment(R.layout.fragment_onboarding_main), StoreSubscriber { + + protected var fromScreen: AppScreen? = null + + override fun onCreate(savedInstanceState: Bundle?) { + fromScreen = store.state.globalState.onboardingService?.fromScreen + + super.onCreate(savedInstanceState) + } + + override fun configureTransitions() { + val inflater = TransitionInflater.from(requireContext()) + when (fromScreen) { + AppScreen.Home -> { + enterTransition = HomeToOnboardingTransition() + exitTransition = HomeToOnboardingTransition() + } + AppScreen.Wallet -> { + enterTransition = inflater.inflateTransition(android.R.transition.slide_bottom) + exitTransition = inflater.inflateTransition(android.R.transition.slide_top) + } + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + onboarding_top_container.inflate(getOnboardingTopContainerId(), true) + configureToolbar() + } + + private fun configureToolbar() { + when (fromScreen) { + AppScreen.Home -> { + toolbar.show(true) + pseudo_toolbar.visibility = View.GONE + } + AppScreen.Wallet -> { + toolbar.visibility = View.GONE + pseudo_toolbar.show(true) + imv_close.setOnClickListener { + store.dispatch(NavigationAction.PopBackTo()) + } + } + } + } + + abstract fun getOnboardingTopContainerId(): Int + + protected fun showConfetti(show: Boolean) { + lav_confetti.show(show) + + if (show) { + lav_confetti.playAnimation() + } else { + lav_confetti.cancelAnimation() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt index 730c8760fc..182f63d7bb 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt @@ -2,46 +2,46 @@ package com.tangem.tap.features.onboarding.products.note import android.os.Bundle import android.view.View +import android.view.animation.OvershootInterpolator +import androidx.annotation.LayoutRes +import androidx.constraintlayout.widget.ConstraintSet import androidx.core.view.isVisible -import androidx.transition.TransitionInflater -import com.tangem.tap.common.extensions.addOnBackPressedDispatcher -import com.tangem.tap.common.extensions.beginDelayedTransition -import com.tangem.tap.common.redux.navigation.AppScreen -import com.tangem.tap.common.redux.navigation.NavigationAction -import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteAction -import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteState -import com.tangem.tap.features.onboarding.redux.OnboardingAction -import com.tangem.tap.features.onboarding.redux.OnboardingNoteStep -import com.tangem.tap.features.send.BaseStoreFragment +import androidx.transition.TransitionManager +import com.tangem.blockchain.common.Blockchain +import com.tangem.tap.common.extensions.getDrawableCompat +import com.tangem.tap.common.extensions.stripZeroPlainString +import com.tangem.tap.common.postUi +import com.tangem.tap.common.toggleWidget.RefreshBalanceWidget +import com.tangem.tap.common.transitions.InternalNoteLayoutTransition +import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment +import com.tangem.tap.features.onboarding.products.note.redux.* import com.tangem.tap.store import com.tangem.wallet.R -import kotlinx.android.synthetic.main.fragment_onboarding.* -import org.rekotlin.StoreSubscriber +import kotlinx.android.synthetic.main.layout_onboarding_bottom_action_views.* +import kotlinx.android.synthetic.main.layout_onboarding_note.* +import kotlinx.android.synthetic.main.view_onboarding_tv_balance.* /** [REDACTED_AUTHOR] */ -class OnboardingNoteFragment : BaseStoreFragment(R.layout.fragment_onboarding), StoreSubscriber { +class OnboardingNoteFragment : BaseOnboardingFragment() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) + private lateinit var btnRefreshBalanceWidget: RefreshBalanceWidget - activity?.addOnBackPressedDispatcher { - store.dispatch(NavigationAction.PopBackTo()) - store.dispatch(OnboardingAction.SetInitialStepOfScreen(OnboardingNoteStep.None)) - } - - val inflater = TransitionInflater.from(requireContext()) - enterTransition = inflater.inflateTransition(R.transition.fade) - exitTransition = inflater.inflateTransition(R.transition.fade) - } + override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_note override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - store.dispatch(OnboardingNoteAction.Init) + btnRefreshBalanceWidget = RefreshBalanceWidget(onboarding_main_container) + val resources = AndroidResources( + OnboardingStringResources( + R.string.copy_toast_msg + ) + ) - imv_front_card.setImageBitmap(store.state.onboardingState.onboardingData?.cardArtwork?.artwork) + store.dispatch(OnboardingNoteAction.SetResources(resources)) + store.dispatch(OnboardingNoteAction.DetermineStepOfScreen) } override fun subscribeToStore() { @@ -55,79 +55,107 @@ class OnboardingNoteFragment : BaseStoreFragment(R.layout.fragment_onboarding), override fun newState(state: OnboardingNoteState) { if (activity == null) return + state.artworkBitmap?.let { imv_front_card.setImageBitmap(it) } pb_state.max = state.steps.size - 1 + pb_state.progress = state.progress - onboarding_main_container.beginDelayedTransition() when (state.currentStep) { OnboardingNoteStep.None -> setupNoneState(state) OnboardingNoteStep.CreateWallet -> setupCreateWalletState(state) OnboardingNoteStep.TopUpWallet -> setupTopUpWalletState(state) - OnboardingNoteStep.Done -> setupSuccessState(state) + OnboardingNoteStep.Done -> setupDoneState(state) } + setBalance(state) + showConfetti(state.showConfetti) + } + + private fun setBalance(state: OnboardingNoteState) { + if (state.balanceCurrency.blockchain == Blockchain.Unknown) return + + val balanceValue = state.balanceValue.stripZeroPlainString() + val currency = state.balanceCurrency.currencySymbol + tv_balance_value.text = "$balanceValue $currency" } private fun setupNoneState(state: OnboardingNoteState) { - pb_state.progress = state.progress + btn_main_action.isVisible = false + btn_alternative_action.isVisible = false + btnRefreshBalanceWidget.show(false) + tv_header.isVisible = false + tv_body.isVisible = false - btn_main_action.isVisible = true - btn_alternative_action.isVisible = true - btn_check_balance.isVisible = true - tv_header.isVisible = true - tv_body.isVisible = true - - btn_main_action.text = "NULL" - btn_alternative_action.text = "NULL" - btn_check_balance.text = "NULL balance" - tv_header.text = "NULL" - tv_body.text = "NULL" + btn_main_action.text = "" + btn_alternative_action.text = "" + tv_header.text = "" + tv_body.text = "" } private fun setupCreateWalletState(state: OnboardingNoteState) { - pb_state.progress = state.progress - btn_main_action.setText(R.string.onboarding_create_wallet_button_create_wallet) btn_main_action.setOnClickListener { store.dispatch(OnboardingNoteAction.CreateWallet) } - btn_alternative_action.setText(R.string.onboarding_button_what_does_it_mean) btn_alternative_action.setOnClickListener { } - btn_check_balance.isVisible = false - btn_check_balance.setOnClickListener { } + btnRefreshBalanceWidget.mainView.setOnClickListener(null) tv_header.setText(R.string.onboarding_create_wallet_header) tv_body.setText(R.string.onboarding_create_wallet_body) + + imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_circle)) + updateConstraints(R.layout.lp_onboarding_create_wallet) } private fun setupTopUpWalletState(state: OnboardingNoteState) { - pb_state.progress = state.progress - btn_main_action.setText(R.string.onboarding_top_up_button_but_crypto) - btn_main_action.setOnClickListener { store.dispatch(OnboardingNoteAction.TopUp) } + btn_main_action.setOnClickListener { + store.dispatch(OnboardingNoteAction.TopUp) + } btn_alternative_action.setText(R.string.onboarding_top_up_button_show_wallet_address) - btn_alternative_action.setOnClickListener { } - - btn_check_balance.isVisible = false - btn_check_balance.setOnClickListener { } + btn_alternative_action.setOnClickListener { + store.dispatch(OnboardingNoteAction.ShowAddressInfoDialog) + } tv_header.setText(R.string.onboarding_top_up_header) tv_body.setText(R.string.onboarding_top_up_body) + + btnRefreshBalanceWidget.changeState(state.balanceState) + if (btnRefreshBalanceWidget.isShowing != true) { + postUi(300) { + btnRefreshBalanceWidget.mainView.setOnClickListener { + store.dispatch(OnboardingNoteAction.Balance.Update) + } + } + } + imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_rectangle_rounded_8)) + updateConstraints(R.layout.lp_onboarding_topup_wallet) } - private fun setupSuccessState(state: OnboardingNoteState) { - pb_state.progress = state.progress - + private fun setupDoneState(state: OnboardingNoteState) { btn_main_action.setText(R.string.onboarding_done_button_continue) - btn_main_action.setOnClickListener { store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) } + btn_main_action.setOnClickListener { + showConfetti(false) + store.dispatch(OnboardingNoteAction.Done) + } btn_alternative_action.isVisible = false btn_alternative_action.setText("") btn_alternative_action.setOnClickListener { } - - btn_check_balance.isVisible = false - btn_check_balance.setOnClickListener { } + btnRefreshBalanceWidget.mainView.setOnClickListener(null) tv_header.setText(R.string.onboarding_done_header) tv_body.setText(R.string.onboarding_done_body) + + imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_rectangle_rounded_8)) + updateConstraints(R.layout.lp_onboarding_done) + } + + private fun updateConstraints(@LayoutRes layoutId: Int) { + val constraintSet = ConstraintSet() + constraintSet.clone(requireContext(), layoutId) + constraintSet.applyTo(onboarding_main_container) + val transition = InternalNoteLayoutTransition() + transition.interpolator = OvershootInterpolator() + TransitionManager.beginDelayedTransition(onboarding_main_container, transition) } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt index bab556d68b..9c1644dda1 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt @@ -1,16 +1,31 @@ package com.tangem.tap.features.onboarding.products.note.redux -import com.tangem.blockchain.common.WalletManager +import com.tangem.tap.features.onboarding.service.OnboardingWalletBalance import org.rekotlin.Action /** [REDACTED_AUTHOR] */ sealed class OnboardingNoteAction : Action { - object Init : OnboardingNoteAction() + // from user, ui + data class SetResources(val resources: AndroidResources) : OnboardingNoteAction() object CreateWallet : OnboardingNoteAction() object TopUp : OnboardingNoteAction() + object ShowAddressInfoDialog : OnboardingNoteAction() + + // from redux + object DetermineStepOfScreen : OnboardingNoteAction() object Done : OnboardingNoteAction() - data class SetWalletManager(val walletManager: WalletManager) : OnboardingNoteAction() + data class SetStepOfScreen(val step: OnboardingNoteStep) : OnboardingNoteAction() + + sealed class Balance { + object Update : OnboardingNoteAction() + data class Set(val balance: OnboardingWalletBalance) : OnboardingNoteAction() + } + + sealed class Confetti { + object Show : OnboardingNoteAction() + object Hide : OnboardingNoteAction() + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt index 7f5ee16c3f..2eab43bc1b 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt @@ -1,9 +1,15 @@ package com.tangem.tap.features.onboarding.products.note.redux import com.tangem.common.CompletionResult +import com.tangem.tap.common.extensions.* +import com.tangem.tap.common.postUi +import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.common.redux.AppState -import com.tangem.tap.features.onboarding.redux.OnboardingAction -import com.tangem.tap.features.onboarding.redux.OnboardingNoteStep +import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.common.redux.navigation.NavigationAction +import com.tangem.tap.features.onboarding.service.balanceIsToppedUp +import com.tangem.tap.features.wallet.redux.ProgressState import com.tangem.tap.scope import com.tangem.tap.store import com.tangem.tap.tangemSdkManager @@ -13,8 +19,6 @@ import org.rekotlin.Middleware class OnboardingNoteMiddleware { companion object { - private const val CARD_SHOP_URI = - "https://shop.tangem.com/?afmc=1i&utm_campaign=1i&utm_source=leaddyno&utm_medium=affiliate" val handler = onboardingNoteMiddleware } } @@ -22,51 +26,93 @@ class OnboardingNoteMiddleware { private val onboardingNoteMiddleware: Middleware = { dispatch, state -> { next -> { action -> - handleMainOnboardingAction(action, ::handleNoteAction) + handleNoteAction(action) next(action) } } } -private fun handleMainOnboardingAction(action: Action, isNotMainAction: (Action) -> Unit) { - val action = action as? OnboardingAction ?: isNotMainAction(action) - - when (action) { - } -} - private fun handleNoteAction(action: Action) { - val onboardingState = store.state.onboardingState + if (action !is OnboardingNoteAction) return + val service = store.state.onboardingNoteState.onboardingService ?: return val noteState = store.state.onboardingNoteState when (action) { - is OnboardingNoteAction.SetWalletManager -> { - } - is OnboardingNoteAction.Init -> { - val step = if (noteState.walletManager == null) { - OnboardingNoteStep.CreateWallet - } else { - OnboardingNoteStep.TopUpWallet + is OnboardingNoteAction.DetermineStepOfScreen -> { + val step = when { + service.walletManager == null -> OnboardingNoteStep.CreateWallet + service.balanceIsToppedUp() -> OnboardingNoteStep.Done + else -> OnboardingNoteStep.TopUpWallet + } + store.dispatch((OnboardingNoteAction.SetStepOfScreen(step))) + } + is OnboardingNoteAction.SetStepOfScreen -> { + if (action.step == OnboardingNoteStep.Done) { + service.activationFinished() + postUi(500) { store.dispatch(OnboardingNoteAction.Confetti.Show) } } - store.dispatch(OnboardingAction.SetInitialStepOfScreen(step)) } is OnboardingNoteAction.CreateWallet -> { scope.launch { - val scanNoteResponse = onboardingState.onboardingData?.scanNoteResponse - val result = tangemSdkManager.createWallet(scanNoteResponse?.card?.cardId) - when (result) { - is CompletionResult.Success -> { -// val scanNoteResponse = scanNoteResponse!!.copy(card = result.data) -// store.dispatch(OnboardingAction.SetScanNoteResponse(scanNoteResponse)) - } - is CompletionResult.Failure -> { - store.dispatch(OnboardingAction.Error("Failed to create wallet")) + val result = tangemSdkManager.createProductWallet(service.scanResponse.card.cardId) + withMainContext { + when (result) { + is CompletionResult.Success -> { + val updatedResponse = service.scanResponse.copy(card = result.data) + service.scanResponse = updatedResponse + service.activationStarted() + + store.dispatch(OnboardingNoteAction.SetStepOfScreen(OnboardingNoteStep.TopUpWallet)) + store.dispatch(OnboardingNoteAction.Balance.Update) + } + is CompletionResult.Failure -> { +// do nothing + } } } } } - is OnboardingNoteAction.TopUp -> { + is OnboardingNoteAction.Balance.Update -> { + val loadingBalance = service.getBalance().copy( + state = ProgressState.Loading, + error = null, + criticalError = null + ) + store.dispatch(OnboardingNoteAction.Balance.Set(loadingBalance)) + scope.launch { + val loadedBalance = service.updateBalance() + if (loadedBalance.criticalError != null) { + store.dispatchErrorNotification(loadedBalance.criticalError) + } + withMainContext { store.dispatch(OnboardingNoteAction.Balance.Set(loadedBalance)) } + } + } + is OnboardingNoteAction.Balance.Set -> { + if (service.balanceIsToppedUp()) { + store.dispatch(OnboardingNoteAction.SetStepOfScreen(OnboardingNoteStep.Done)) + } + } + is OnboardingNoteAction.ShowAddressInfoDialog -> { + val addressData = noteState.onboardingService?.getAddressData() ?: return + val addressWasCopied = noteState.resources.strings.addressWasCopied + val appDialog = AppDialog.AddressInfoDialog( + addressData, + onCopyAddress = { store.dispatchToastNotification(addressWasCopied) }, + onExploreAddress = { store.dispatchOpenUrl(addressData.exploreUrl) } + ) + store.dispatchDialogShow(appDialog) + } + is OnboardingNoteAction.TopUp -> { + val topUpUrl = noteState.onboardingService?.getToUpUrl() ?: return + store.dispatchOpenUrl(topUpUrl) + } + OnboardingNoteAction.Done -> { + store.dispatch(GlobalAction.Onboarding.Deactivate) + scope.launch { + store.state.globalState.tapWalletManager.onCardScanned(service.scanResponse) + store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) + } } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt index b6a50be846..efe2540b66 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt @@ -1,8 +1,8 @@ package com.tangem.tap.features.onboarding.products.note.redux import com.tangem.tap.common.redux.AppState -import com.tangem.tap.features.onboarding.redux.OnboardingAction -import com.tangem.tap.features.onboarding.redux.OnboardingNoteStep +import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.features.onboarding.service.OnboardingNoteService import org.rekotlin.Action class OnboardingNoteReducer { @@ -15,23 +15,46 @@ private fun internalReduce(action: Action, appState: AppState): OnboardingNoteSt var state = appState.onboardingNoteState when (action) { - is OnboardingAction.SetInitialStepOfScreen -> { - (action.step as? OnboardingNoteStep)?.let { - if (it != state.currentStep && state.steps.contains(it)) { - state = state.copy(currentStep = it) - } + is GlobalAction.Onboarding.Activate -> { + if (action.onboardingService is OnboardingNoteService) { + val service = action.onboardingService + val balance = service.getBalance() + state = state.copy( + onboardingService = service, + artworkBitmap = service.getArtwork().value?.artwork, + balanceValue = balance.value, + balanceCurrency = balance.currency, + balanceState = balance.state, + amountToCreateAccount = balance.amountToCreateAccount, + showConfetti = false + ) } } - OnboardingAction.SwitchToNextStep -> { - val progress = state.progress - if (progress >= 0 && progress != state.steps.size - 1) { - state = state.copy(currentStep = state.steps[progress + 1]) + GlobalAction.Onboarding.Deactivate -> { + state = OnboardingNoteState() + } + is OnboardingNoteAction.SetResources -> { + state = state.copy(resources = action.resources) + } + is OnboardingNoteAction.Balance.Set -> { + state = state.copy( + balanceValue = action.balance.value, + balanceCurrency = action.balance.currency, + balanceState = action.balance.state, + amountToCreateAccount = action.balance.amountToCreateAccount, + ) + } + is OnboardingNoteAction.SetStepOfScreen -> { + if (action.step != state.currentStep && state.steps.contains(action.step)) { + state = state.copy(currentStep = action.step) } } - is OnboardingNoteAction.SetWalletManager -> { - state = state.copy(walletManager = action.walletManager) + is OnboardingNoteAction.Confetti.Show -> { + state = state.copy(showConfetti = true) + } + is OnboardingNoteAction.Confetti.Hide -> { + state = state.copy(showConfetti = false) } - } return state diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt index ae5677779f..b95c58510d 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt @@ -1,18 +1,42 @@ package com.tangem.tap.features.onboarding.products.note.redux -import com.tangem.blockchain.common.WalletManager -import com.tangem.tap.features.onboarding.redux.OnboardingNoteStep +import android.graphics.Bitmap +import com.tangem.blockchain.common.Blockchain +import com.tangem.tap.features.onboarding.service.OnboardingNoteService +import com.tangem.tap.features.wallet.redux.Currency +import com.tangem.tap.features.wallet.redux.ProgressState import org.rekotlin.StateType +import java.math.BigDecimal /** [REDACTED_AUTHOR] */ data class OnboardingNoteState( - val walletManager: WalletManager? = null, + val onboardingService: OnboardingNoteService? = null, + val resources: AndroidResources = AndroidResources(), + // UI + val showConfetti: Boolean = false, + val artworkBitmap: Bitmap? = null, + val balanceValue: BigDecimal = BigDecimal.ZERO, + val balanceCurrency: Currency = Currency.Blockchain(Blockchain.Unknown), + val balanceState: ProgressState = ProgressState.Done, + val amountToCreateAccount: String? = null, val currentStep: OnboardingNoteStep = OnboardingNoteStep.None, val steps: List = OnboardingNoteStep.values().toList(), ) : StateType { val progress: Int get() = steps.indexOf(currentStep) -} \ No newline at end of file +} + +enum class OnboardingNoteStep { + None, CreateWallet, TopUpWallet, Done +} + +data class AndroidResources( + val strings: OnboardingStringResources = OnboardingStringResources() +) + +data class OnboardingStringResources( + val addressWasCopied: Int = -1 +) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/old/OnboardingOtherCardsFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt similarity index 62% rename from app/src/main/java/com/tangem/tap/features/onboarding/products/old/OnboardingOtherCardsFragment.kt rename to app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt index cada557578..b0de1e9949 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/old/OnboardingOtherCardsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt @@ -1,31 +1,20 @@ -package com.tangem.tap.features.onboarding.products.old +package com.tangem.tap.features.onboarding.products.otherCards import android.os.Bundle import android.view.View import androidx.transition.TransitionInflater -import com.tangem.tap.common.extensions.addOnBackPressedDispatcher -import com.tangem.tap.common.redux.navigation.NavigationAction -import com.tangem.tap.features.onboarding.redux.OnboardingAction -import com.tangem.tap.features.onboarding.redux.OnboardingOtherCardsState -import com.tangem.tap.features.onboarding.redux.OnboardingOtherStep import com.tangem.tap.features.send.BaseStoreFragment -import com.tangem.tap.store import com.tangem.wallet.R import org.rekotlin.StoreSubscriber /** [REDACTED_AUTHOR] */ -class OnboardingOtherCardsFragment : BaseStoreFragment(R.layout.fragment_onboarding), StoreSubscriber { +class OnboardingOtherCardsFragment : BaseStoreFragment(R.layout.fragment_onboarding_main), StoreSubscriber { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - activity?.addOnBackPressedDispatcher { - store.dispatch(NavigationAction.PopBackTo()) - store.dispatch(OnboardingAction.SetInitialStepOfScreen(OnboardingOtherStep.Done)) - } - val inflater = TransitionInflater.from(requireContext()) enterTransition = inflater.inflateTransition(R.transition.fade) exitTransition = inflater.inflateTransition(R.transition.fade) @@ -47,4 +36,6 @@ class OnboardingOtherCardsFragment : BaseStoreFragment(R.layout.fragment_onboard if (activity == null) return } -} \ No newline at end of file +} + +class OnboardingOtherCardsState {} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/OnboardingWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/OnboardingWalletFragment.kt index 5b2eba2423..c947f32ab9 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/OnboardingWalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/OnboardingWalletFragment.kt @@ -3,11 +3,7 @@ package com.tangem.tap.features.onboarding.products.wallet import android.os.Bundle import android.view.View import androidx.transition.TransitionInflater -import com.tangem.tap.common.extensions.addOnBackPressedDispatcher -import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletState -import com.tangem.tap.features.onboarding.redux.OnboardingAction -import com.tangem.tap.features.onboarding.redux.OnboardingOtherStep import com.tangem.tap.features.send.BaseStoreFragment import com.tangem.tap.store import com.tangem.wallet.R @@ -21,11 +17,6 @@ class OnboardingWalletFragment : BaseStoreFragment(R.layout.fragment_onboarding_ override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - activity?.addOnBackPressedDispatcher { - store.dispatch(NavigationAction.PopBackTo()) - store.dispatch(OnboardingAction.SetInitialStepOfScreen(OnboardingOtherStep.Done)) - } - val inflater = TransitionInflater.from(requireContext()) enterTransition = inflater.inflateTransition(R.transition.fade) exitTransition = inflater.inflateTransition(R.transition.fade) @@ -33,8 +24,6 @@ class OnboardingWalletFragment : BaseStoreFragment(R.layout.fragment_onboarding_ override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - - store.dispatch(OnboardingAction.OnboardingOtherAction.Init) } override fun subscribeToStore() { 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 9b8579171a..5207a85b3d 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 @@ -1,6 +1,5 @@ package com.tangem.tap.features.onboarding.products.wallet.redux -import com.tangem.tap.features.onboarding.redux.OnboardingStep import org.rekotlin.StateType /** @@ -10,6 +9,6 @@ data class OnboardingWalletState( val any: String? = null ) : StateType -enum class OnboardingWalletStep : OnboardingStep { +enum class OnboardingWalletStep { None, CreateWallet, TopUpWallet, Backup, Done } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingAction.kt deleted file mode 100644 index 7a904b47ee..0000000000 --- a/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingAction.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.tangem.tap.features.onboarding.redux - -import org.rekotlin.Action - -/** -[REDACTED_AUTHOR] - */ -sealed class OnboardingAction : Action { - - data class SetInitialStepOfScreen(val step: OnboardingStep) : OnboardingAction() - object SwitchToNextStep : OnboardingAction() - - data class SetData(val data: OnboardingData) : OnboardingAction() - - sealed class OnboardingOtherAction : Action { - object Init : OnboardingOtherAction() - object CreateWallet : OnboardingOtherAction() - object Done : OnboardingOtherAction() - } - - class Error(val message: String) : OnboardingAction() -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingMiddleware.kt deleted file mode 100644 index ea373a5f41..0000000000 --- a/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingMiddleware.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.tangem.tap.features.onboarding.redux - -import com.tangem.tap.common.redux.AppState -import com.tangem.tap.store -import org.rekotlin.Middleware - -class OnboardingMiddleware { - companion object { - val handler = onboardingMiddleware - } -} - -private val onboardingMiddleware: Middleware = { dispatch, state -> - { next -> - { action -> - val onboardingState = store.state.onboardingState - when (action) { - } - next(action) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingReducer.kt deleted file mode 100644 index 5ee024922d..0000000000 --- a/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingReducer.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.tangem.tap.features.onboarding.redux - -import com.tangem.tap.common.redux.AppState -import org.rekotlin.Action - -class OnboardingReducer { - companion object { - fun reduce(action: Action, state: AppState): OnboardingState = internalReduce(action, state) - } -} - -private fun internalReduce(action: Action, appState: AppState): OnboardingState { - - if (action !is OnboardingAction) return appState.onboardingState - - var state = appState.onboardingState - when (action) { - is OnboardingAction.SetData -> { - state = state.copy(onboardingData = action.data) - } - } - - return state -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingState.kt deleted file mode 100644 index 2a4909de43..0000000000 --- a/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingState.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.tangem.tap.features.onboarding.redux - -import org.rekotlin.StateType - -data class OnboardingState( - val onboardingData: OnboardingData? = null, -) : StateType - -data class OnboardingOtherCardsState( - val any: String? = null -) : StateType \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/redux/StateSupportClasses.kt b/app/src/main/java/com/tangem/tap/features/onboarding/redux/StateSupportClasses.kt deleted file mode 100644 index cc422410a5..0000000000 --- a/app/src/main/java/com/tangem/tap/features/onboarding/redux/StateSupportClasses.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.tangem.tap.features.onboarding.redux - -import com.tangem.tap.common.redux.navigation.AppScreen -import com.tangem.tap.domain.tasks.ScanNoteResponse -import com.tangem.tap.features.wallet.redux.Artwork -import java.math.BigDecimal - -/** -[REDACTED_AUTHOR] - */ -data class OnboardingData( - val scanNoteResponse: ScanNoteResponse, - val cardArtwork: Artwork, - val balance: BigDecimal?, - val onboardingScreen: AppScreen -) - -interface OnboardingStep - -enum class OnboardingNoteStep : OnboardingStep { - None, CreateWallet, TopUpWallet, Done -} - -enum class OnboardingOtherStep : OnboardingStep { - CreateWallet, Done -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingHelper.kt b/app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingHelper.kt new file mode 100644 index 0000000000..7cf88da9c5 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingHelper.kt @@ -0,0 +1,33 @@ +package com.tangem.tap.features.onboarding.service + +import com.tangem.blockchain.common.WalletManagerFactory +import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.domain.TapWorkarounds.isTangemNote +import com.tangem.tap.domain.extensions.hasWallets +import com.tangem.tap.domain.tasks.ScanNoteResponse +import com.tangem.tap.preferencesStorage + +/** +[REDACTED_AUTHOR] + */ +class OnboardingHelper { + companion object { + + fun isOnboardingCase(response: ScanNoteResponse): Boolean = if (response.card.hasWallets()) + preferencesStorage.usedCardsPrefStorage.activationIsStarted(response.card.cardId) else true + + fun createService( + fromScreen: AppScreen, + scanResponse: ScanNoteResponse, + walletManagerFactory: WalletManagerFactory, + ): ProductOnboardingService? { + val card = scanResponse.card + val cardInfoStorage = preferencesStorage.usedCardsPrefStorage + return when { + card.isTangemNote() -> OnboardingNoteService(fromScreen, scanResponse, cardInfoStorage, + walletManagerFactory) + else -> null + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingNoteService.kt b/app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingNoteService.kt new file mode 100644 index 0000000000..4404a8e35b --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingNoteService.kt @@ -0,0 +1,21 @@ +package com.tangem.tap.features.onboarding.service + +import com.tangem.blockchain.common.WalletManagerFactory +import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.domain.tasks.ScanNoteResponse +import com.tangem.tap.persistence.UsedCardsPrefStorage + +/** +[REDACTED_AUTHOR] + */ +class OnboardingNoteService( + override val fromScreen: AppScreen, + override var scanResponse: ScanNoteResponse, + cardInfoStorage: UsedCardsPrefStorage, + walletManagerFactory: WalletManagerFactory, +) : ProductOnboardingService(cardInfoStorage, walletManagerFactory) { + + override fun navigateToScreen(): AppScreen { + return AppScreen.OnboardingNote + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/service/ProductOnboardingService.kt b/app/src/main/java/com/tangem/tap/features/onboarding/service/ProductOnboardingService.kt new file mode 100644 index 0000000000..44c4c99980 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/service/ProductOnboardingService.kt @@ -0,0 +1,295 @@ +package com.tangem.tap.features.onboarding.service + +import android.util.Log +import com.tangem.blockchain.common.AmountType +import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.common.WalletManager +import com.tangem.blockchain.common.WalletManagerFactory +import com.tangem.common.card.Card +import com.tangem.common.extensions.guard +import com.tangem.common.extensions.isZero +import com.tangem.common.services.Result +import com.tangem.operations.attestation.OnlineCardVerifier +import com.tangem.tap.common.extensions.dispatchDebugErrorNotification +import com.tangem.tap.common.extensions.isPositive +import com.tangem.tap.common.extensions.safeUpdate +import com.tangem.tap.common.extensions.withMainContext +import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.domain.TapError +import com.tangem.tap.domain.UrlBitmapLoader +import com.tangem.tap.domain.extensions.getArtworkUrl +import com.tangem.tap.domain.extensions.makePrimaryWalletManager +import com.tangem.tap.domain.tasks.ScanNoteResponse +import com.tangem.tap.domain.topup.TradeCryptoHelper +import com.tangem.tap.features.wallet.redux.AddressData +import com.tangem.tap.features.wallet.redux.Artwork +import com.tangem.tap.features.wallet.redux.Currency +import com.tangem.tap.features.wallet.redux.ProgressState +import com.tangem.tap.features.wallet.redux.reducers.createAddressesData +import com.tangem.tap.persistence.UsedCardsPrefStorage +import com.tangem.tap.store +import kotlinx.coroutines.* +import java.io.PrintWriter +import java.io.StringWriter +import java.math.BigDecimal +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine + +/** +[REDACTED_AUTHOR] + */ +interface OnboardingService { + val fromScreen: AppScreen + val scanResponse: ScanNoteResponse + + // val cardInfoStorage: UsedCardsPrefStorage + var walletManager: WalletManager? + + var onInitializationProgress: ((ProgressState) -> Unit)? + var onInitialized: ((AppScreen) -> Unit)? + var onError: ((TapError) -> Unit)? + + fun initializeOnboarding() + fun getArtwork(): OnboardingArtwork + fun getBalance(): OnboardingWalletBalance + suspend fun updateBalance(): OnboardingWalletBalance + fun getToUpUrl(): String? + fun getAddressData(): AddressData? + + fun activationStarted() + fun activationFinished() +} + +abstract class ProductOnboardingService( + private val cardInfoStorage: UsedCardsPrefStorage, + private val walletManagerFactory: WalletManagerFactory, +) : OnboardingService { + + override var walletManager: WalletManager? = null + + override var onInitializationProgress: ((ProgressState) -> Unit)? = null + override var onInitialized: ((AppScreen) -> Unit)? = null + override var onError: ((TapError) -> Unit)? = null + + protected var isProceeded = false + + protected var loadedCardArtwork: OnboardingArtwork = OnboardingArtwork.loading() + set(value) { + field = value + tryToProceed() + } + + protected var loadedBalance: OnboardingWalletBalance = OnboardingWalletBalance.loading() + set(value) { + field = value + tryToProceed() + } + + protected val internalScope = CoroutineScope(Job() + Dispatchers.IO + coroutineExceptionHandler()) + + override fun initializeOnboarding() { + val card = scanResponse.card + onInitializationProgress?.invoke(ProgressState.Loading) + + internalScope.launch { + val artwork = loadCardArtwork(card) + withMainContext { + loadedCardArtwork = if (artwork == null) OnboardingArtwork.error() else OnboardingArtwork.done(artwork) + } + } + internalScope.launch { updateBalance() } + } + + override fun getArtwork(): OnboardingArtwork = loadedCardArtwork + + override fun getBalance(): OnboardingWalletBalance = loadedBalance + + override fun getToUpUrl(): String? { + val wallet = walletManager?.wallet ?: return null + val config = store.state.globalState.configManager?.config ?: return null + val defaultAddress = wallet.address + + return TradeCryptoHelper.getUrl( + TradeCryptoHelper.Action.Buy, + wallet.blockchain, + wallet.blockchain.currency, + defaultAddress, + config.moonPayApiKey, + config.moonPayApiSecretKey + ) + } + + override fun getAddressData(): AddressData? { + val wallet = walletManager?.wallet ?: return null + + val addressDataList = wallet.createAddressesData() + return if (addressDataList.isEmpty()) null + else addressDataList[0] + } + + protected suspend fun loadCardArtwork(card: Card): Artwork? { + return when (val cardInfoResult = OnlineCardVerifier().getCardInfo(card.cardId, card.cardPublicKey)) { + is Result.Success -> { + val artworkUrl = card.getArtworkUrl(cardInfoResult.data.artwork?.id) ?: Artwork.DEFAULT_IMG_URL + loadBitmap(artworkUrl) + } + is Result.Failure -> { + loadBitmap(Artwork.DEFAULT_IMG_URL) + } + } + } + + override suspend fun updateBalance(): OnboardingWalletBalance { + withMainContext { loadedBalance = OnboardingWalletBalance.loading(loadedBalance.value) } + + val walletManager = walletManager ?: walletManagerFactory.makePrimaryWalletManager(scanResponse).guard { + val customError = TapError.CustomError("Loading cancelled. Cause: wallet manager didn't created") + loadedBalance = OnboardingWalletBalance.error(customError) + return loadedBalance + } + + this.walletManager = walletManager + val currency = Currency.Blockchain(walletManager.wallet.blockchain) + val updatedBalance = when (val result = walletManager.safeUpdate()) { + is Result.Success -> { + val wallet = walletManager.wallet + val valueOfAmount = wallet.amounts[AmountType.Coin]?.value + if (valueOfAmount == null) { + val customError = TapError.CustomError("Amount is NULL") + OnboardingWalletBalance.criticalError(customError) + } else { + val balance = if (valueOfAmount.isZero()) { + OnboardingWalletBalance.done(valueOfAmount, hasForIncomingTransactions(), currency) + } else { + OnboardingWalletBalance.done(valueOfAmount, false, currency) + } + balance + } + } + is Result.Failure -> { + val error = (result.error as? TapError) ?: TapError.UnknownError + when (error) { + is TapError.WalletManagerUpdate.NoAccountError -> OnboardingWalletBalance.error(error) +// NoInternetConnection, WalletManagerUpdate.InternalError + else -> OnboardingWalletBalance.criticalError(error) + } + } + } + withMainContext { loadedBalance = updatedBalance } + return loadedBalance + } + + override fun activationStarted() { + cardInfoStorage.activationStarted(scanResponse.card.cardId) + + } + + override fun activationFinished() { + cardInfoStorage.activationFinished(scanResponse.card.cardId) + } + + protected open suspend fun hasForIncomingTransactions(): Boolean { + return false + } + + protected open fun tryToProceed() { + if (isProceeded) return + + loadedBalance.criticalError?.let { + sendError(it) + return + } + if (!isReadyToProceed()) return + + isProceeded = true + onInitializationProgress?.invoke(ProgressState.Done) + onInitialized?.invoke(navigateToScreen()) + } + + protected open fun isReadyToProceed(): Boolean { + return loadedBalance.state != ProgressState.Loading && loadedCardArtwork.state != ProgressState.Loading + } + + protected fun sendError(error: TapError) { + onInitializationProgress?.invoke(ProgressState.Error) + onError?.invoke(error) + } + + protected abstract fun navigateToScreen(): AppScreen + + private suspend fun loadBitmap(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 -> null + } + continuation.resume(result) + } + } + } + } + + private fun coroutineExceptionHandler(): CoroutineExceptionHandler = CoroutineExceptionHandler { _, throwable -> + StringWriter().apply { + throwable.printStackTrace(PrintWriter(this)) + Log.e("Coroutine", this.toString()) + store.dispatchDebugErrorNotification(this.toString()) + } + } +} + +data class OnboardingArtwork( + val value: Artwork? = null, + val state: ProgressState +) { + companion object { + // balance was not loaded at all when any error occurred + fun error(): OnboardingArtwork = OnboardingArtwork(state = ProgressState.Error) + + // balance is loading + fun loading(): OnboardingArtwork = OnboardingArtwork(state = ProgressState.Loading) + + // balance was loaded + fun done(artwork: Artwork): OnboardingArtwork = OnboardingArtwork(artwork, ProgressState.Done) + } +} + +data class OnboardingWalletBalance( + val value: BigDecimal = BigDecimal.ZERO, + val currency: Currency.Blockchain = Currency.Blockchain(Blockchain.Unknown), + val hasIncomingTransaction: Boolean = false, + val state: ProgressState, + val error: TapError? = null, + val criticalError: TapError? = null, +) { + + val amountToCreateAccount: String? + get() = if (error is TapError.WalletManagerUpdate.NoAccountError) error.customMessage else null + + companion object { + fun error(error: TapError): OnboardingWalletBalance = OnboardingWalletBalance( + state = ProgressState.Error, + error = error + ) + + fun criticalError(error: TapError): OnboardingWalletBalance = OnboardingWalletBalance( + state = ProgressState.Error, + criticalError = error + ) + + fun loading(value: BigDecimal = BigDecimal.ZERO): OnboardingWalletBalance = OnboardingWalletBalance( + value, + state = ProgressState.Loading + ) + + fun done(value: BigDecimal, hasTransactions: Boolean, currency: Currency.Blockchain): OnboardingWalletBalance = + OnboardingWalletBalance(value, currency, hasTransactions, ProgressState.Done) + } +} + +fun ProductOnboardingService.balanceIsToppedUp(): Boolean { + val balance = getBalance() + return balance.value.isPositive() || balance.hasIncomingTransaction +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt b/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt index 9d19112f8a..75a2a5ad80 100644 --- a/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt @@ -24,7 +24,10 @@ abstract class BaseStoreFragment(layoutId: Int) : Fragment(layoutId) { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + configureTransitions() + } + protected open fun configureTransitions() { val inflater = TransitionInflater.from(requireContext()) enterTransition = inflater.inflateTransition(R.transition.slide_right) exitTransition = inflater.inflateTransition(R.transition.fade) From a4e7c2d6270f8ed20bc0562d494d6781774f12bf Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 7 Oct 2021 16:00:46 +0300 Subject: [PATCH 058/189] Updated on 2026-08-14 --- .../tap/common/leapfrogWidget/Animations.kt | 154 +++++++++++++ .../tap/common/leapfrogWidget/LeapView.kt | 59 +++++ .../common/leapfrogWidget/LeapfrogWidget.kt | 204 ++++++++++++++++++ app/src/main/res/layout/leapfrog_widget.xml | 26 +++ 4 files changed, 443 insertions(+) create mode 100644 app/src/main/java/com/tangem/tap/common/leapfrogWidget/Animations.kt create mode 100644 app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapView.kt create mode 100644 app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapfrogWidget.kt create mode 100644 app/src/main/res/layout/leapfrog_widget.xml 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 new file mode 100644 index 0000000000..b89618ee5f --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/Animations.kt @@ -0,0 +1,154 @@ +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 AnimatorSet.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.initAnimation(animDuration: Long, properties: Properties): AnimatorSet { + elevation = properties.elevationEnd + scaleX = properties.scaleEnd + scaleY = properties.scaleEnd + + val translate = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslationStart, properties.yTranslationEnd) + translate.duration = animDuration + return AnimatorSet().apply { playTogether(translate) } +} + +fun View.leapAnimation(animDuration: Long, properties: Properties, overLift: Float): AnimatorSet { + val halfDuration = animDuration / 2 + + val upTo = (height.toFloat() + properties.yTranslationStart + overLift) * -1 + val translateUp = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslationStart, upTo) + translateUp.duration = halfDuration + translateUp.interpolator = LinearInterpolator() + + val scaleDuration = halfDuration - halfDuration / 5 + val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X, properties.scaleStart, properties.scaleEnd) + val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scaleStart, properties.scaleEnd) + val scale = AnimatorSet().apply { + startDelay = scaleDuration + duration = scaleDuration + interpolator = LinearInterpolator() + playTogether(scaleX, scaleY) + } + + val elevation = ValueAnimator.ofFloat(properties.elevationStart, properties.elevationEnd) + elevation.addUpdateListener { this.elevation = (it.animatedValue as? Float) ?: properties.elevationStart } + elevation.startDelay = halfDuration + elevation.duration = 100 + + val translateDown = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, upTo, properties.yTranslationEnd) + translateDown.startDelay = halfDuration + translateDown.duration = halfDuration + translateDown.interpolator = LinearInterpolator() + + return AnimatorSet().apply { + playTogether( + translateUp, + scale, + elevation, + translateDown + ) + } +} + +fun View.leapBackAnimation(animDuration: Long, properties: Properties, calculator: PropertyCalculator): AnimatorSet { + val halfDuration = animDuration / 2 + + val upTo = ((height).toFloat() - calculator.yTranslationFactor) * -1 + val translateUp = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslationStart, upTo) + translateUp.duration = halfDuration + translateUp.interpolator = LinearInterpolator() + + val scaleDuration = halfDuration - halfDuration / 5 + val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X, properties.scaleStart, properties.scaleEnd) + val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scaleStart, properties.scaleEnd) + val scale = AnimatorSet().apply { + startDelay = scaleDuration + duration = scaleDuration + interpolator = LinearInterpolator() + playTogether(scaleX, scaleY) + } + + val elevation = ValueAnimator.ofFloat(properties.elevationStart, properties.elevationEnd) + elevation.addUpdateListener { this.elevation = (it.animatedValue as? Float) ?: properties.elevationStart } + elevation.startDelay = halfDuration + elevation.duration = 100 + + val translateDown = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, upTo, properties.yTranslationEnd) + translateDown.startDelay = halfDuration + translateDown.duration = halfDuration + + return AnimatorSet().apply { + playTogether( + translateUp, + scale, + elevation, + translateDown, + ) + } +} + +fun View.pullUpAnimation(leapDuration: Long, properties: Properties): AnimatorSet { + val pullDuration = leapDuration / 2 + val delayDuration = leapDuration / 2 + + val translateUp = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslationStart, properties.yTranslationEnd) + val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X, properties.scaleStart, properties.scaleEnd) + val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scaleStart, properties.scaleEnd) + val elevation = ValueAnimator.ofFloat(properties.elevationStart, properties.elevationEnd) + elevation.addUpdateListener { this.elevation = (it.animatedValue as? Float) ?: properties.elevationStart } + + return AnimatorSet().apply { + startDelay = delayDuration + duration = pullDuration + interpolator = DecelerateInterpolator() + playTogether(translateUp, scaleX, scaleY, elevation) + } +} + +fun View.pullDownAnimation(leapDuration: Long, properties: Properties): AnimatorSet { + val pullDuration = leapDuration / 2 + val delayDuration = leapDuration / 4 + + val translate = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslationStart, properties.yTranslationEnd) + val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X, properties.scaleStart, properties.scaleEnd) + val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scaleStart, properties.scaleEnd) + val elevation = ValueAnimator.ofFloat(properties.elevationStart, properties.elevationEnd) + elevation.addUpdateListener { this.elevation = (it.animatedValue as? Float) ?: properties.elevationStart } + + return AnimatorSet().apply { + startDelay = delayDuration + duration = pullDuration + interpolator = DecelerateInterpolator() + playTogether(translate, scaleX, scaleY, elevation) + } +} \ 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 new file mode 100644 index 0000000000..b3a115f964 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapView.kt @@ -0,0 +1,59 @@ +package com.tangem.tap.common.leapfrogWidget + +import android.view.View + +/** +[REDACTED_AUTHOR] + */ +class LeapView( + val view: View, + val index: Int, + position: Int, + val maximalPosition: Int, + val initialProperties: Properties +) { + var currentPosition: Int = position + private set + var previousPosition: Int = position + private set + + fun changePositionByLeap() { + previousPosition = currentPosition + currentPosition = when (previousPosition) { + 0 -> maximalPosition + else -> currentPosition - 1 + } + } + + fun changePositionByLeapBack() { + previousPosition = currentPosition + currentPosition = when (previousPosition) { + maximalPosition -> 0 + else -> currentPosition + 1 + } + } + + override fun toString(): String = "index: $index, position: $currentPosition, code: ${view.hashCode()}" +} + +data class Properties( + val startPosition: Int, + val endPosition: Int, + val scaleStart: Float, + val scaleEnd: Float, + val elevationStart: Float, + val elevationEnd: Float, + val yTranslationStart: Float, + val yTranslationEnd: Float, + val hasForegroundStart: Boolean, + val hasForegroundEnd: Boolean, +) { + override fun toString(): String { + return """ + scaleStart: $scaleStart, scaleEnd: $scaleEnd, + elevationStart: $elevationStart, elevationEnd: $elevationEnd, + yTranslationStart: $yTranslationStart, yTranslationEnd: $yTranslationEnd, + hasForegroundStart : $hasForegroundStart, hasForegroundEnd: $hasForegroundEnd, + """.trimIndent() + } +} \ 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 new file mode 100644 index 0000000000..e88e1cf49a --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapfrogWidget.kt @@ -0,0 +1,204 @@ +package com.tangem.tap.common.leapfrogWidget + +import android.animation.Animator +import android.animation.AnimatorSet +import android.view.View +import android.view.animation.AccelerateDecelerateInterpolator +import android.widget.FrameLayout +import androidx.core.view.children +import com.tangem.tangem_sdk_new.extensions.dpToPx + +/** +[REDACTED_AUTHOR] + */ +class LeapfrogWidget( + private val parentContainer: FrameLayout, + private val calculator: PropertyCalculator = PropertyCalculator(), +) { + private val initAnimationDuration = 500L + private val leapAnimationDuration = 800L + private val leapBackAnimationDuration = 800L + + private val leapViews = mutableListOf() + + private var leapProgress: Int = 100 + private var leapBackProgress: Int = 100 + + init { + calculator.setTranslationConverter { parentContainer.dpToPx(it) } + init() + } + + private fun init() { + if (!viewIsFullFledged()) return + + val maxPosition = parentContainer.childCount - 1 + parentContainer.children.forEachIndexed { index, view -> + val initialPosition = maxPosition - index + val initialProperties = createViewProperty(0, initialPosition) + leapViews.add(LeapView(view, index, initialPosition, maxPosition, initialProperties)) + } + resetToInitialState() + } + + private fun resetToInitialState(withAnimation: Boolean = true, duration: Long = initAnimationDuration) { + if (!viewIsFullFledged()) return + + if (withAnimation) { + val animatorsList = mutableListOf() + leapViews.forEach { animatorsList.add(it.view.initAnimation(duration, it.initialProperties)) } + val animator = AnimatorSet() + animator.interpolator = AccelerateDecelerateInterpolator() + animator.playTogether(animatorsList.toList()) + animator.start() + } else { + leapViews.forEach { + it.view.scaleX = it.initialProperties.scaleEnd + it.view.scaleY = it.initialProperties.scaleEnd + it.view.translationY = it.initialProperties.yTranslationEnd + it.view.elevation = it.initialProperties.elevationEnd + } + } + } + + fun leap(listener: ProgressListener? = null) { + if (!canLeap()) return + + // if not - then it's ready for pullUp + fun isReadyForLeap(leapView: LeapView): Boolean { + return leapView.previousPosition == 0 && leapView.currentPosition == leapView.maximalPosition + } + + val animatorsList = mutableListOf() + leapViews.forEach { + it.changePositionByLeap() + val properties = createViewProperty(it.previousPosition, it.currentPosition) + if (isReadyForLeap(it)) { + val overLift = calculator.overLift(leapViews.size) + animatorsList.add(it.view.leapAnimation(leapAnimationDuration, properties, overLift)) + } else { + animatorsList.add(it.view.pullUpAnimation(leapAnimationDuration, properties)) + } + } + val animator = AnimatorSet() + animatorsList.add(animator.createProgressListener(leapAnimationDuration) { leapProgress = it }) + animatorsList.add(animator.createProgressListener(leapAnimationDuration, listener)) + animator.playTogether(animatorsList.toList()) + animator.start() + } + + fun leapBack(listener: ProgressListener? = null) { + if (!canLeapBack()) return + + // if not - then it's ready for pullDown + fun isReadyForLeapBack(leapView: LeapView): Boolean { + return leapView.previousPosition == leapView.maximalPosition && leapView.currentPosition == 0 + } + + val animatorsList = mutableListOf() + leapViews.forEach { + it.changePositionByLeapBack() + val properties = createViewProperty(it.previousPosition, it.currentPosition) + if (isReadyForLeapBack(it)) { + val overLift = calculator.overLift(leapViews.size) + animatorsList.add(it.view.leapBackAnimation(leapBackAnimationDuration, properties, calculator)) + } else { + animatorsList.add(it.view.pullDownAnimation(leapBackAnimationDuration, properties)) + } + } + val animator = AnimatorSet() + animatorsList.add(animator.createProgressListener(leapBackAnimationDuration) { leapBackProgress = it }) + animatorsList.add(animator.createProgressListener(leapBackAnimationDuration, listener)) + animator.playTogether(animatorsList.toList()) + animator.start() + } + + fun getViewByPosition(position: Int): View? { + return leapViews.firstOrNull { it.currentPosition == position }?.view + } + + private fun viewIsFullFledged(): Boolean = parentContainer.childCount > 1 + + 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 leapInProgress(): Boolean = leapProgress != 100 + private fun leapBackInProgress(): Boolean = leapBackProgress != 100 + + private fun createViewProperty(startPosition: Int, endPosition: Int): Properties { + return Properties( + startPosition, + endPosition, + calculator.scale(startPosition), + calculator.scale(endPosition), + calculator.elevation(startPosition, parentContainer.childCount), + calculator.elevation(endPosition, parentContainer.childCount), + calculator.yTranslation(startPosition), + calculator.yTranslation(endPosition), + calculator.hasForeground(startPosition), + calculator.hasForeground(endPosition), + ) + } +} + +class PropertyCalculator( + val elevationFactor: Float = 1f, + val decreaseScaleFactor: Float = 0.1f, + val yTranslationFactor: Float = 20f, + val decreaseOverLiftingFactor: Float = 0.85f, +) { + // 1st 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/res/layout/leapfrog_widget.xml b/app/src/main/res/layout/leapfrog_widget.xml new file mode 100644 index 0000000000..13a8e61d88 --- /dev/null +++ b/app/src/main/res/layout/leapfrog_widget.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + \ No newline at end of file From 75a66a1b5b1dc2b036cdec673bed56a73d55112d Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 11 Oct 2021 18:25:19 +0300 Subject: [PATCH 059/189] Updated on 2026-08-14 --- .../onboarding/service/ProductOnboardingService.kt | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/service/ProductOnboardingService.kt b/app/src/main/java/com/tangem/tap/features/onboarding/service/ProductOnboardingService.kt index 44c4c99980..cf9276dd5a 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/service/ProductOnboardingService.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/service/ProductOnboardingService.kt @@ -1,10 +1,7 @@ package com.tangem.tap.features.onboarding.service import android.util.Log -import com.tangem.blockchain.common.AmountType -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.WalletManager -import com.tangem.blockchain.common.WalletManagerFactory +import com.tangem.blockchain.common.* import com.tangem.common.card.Card import com.tangem.common.extensions.guard import com.tangem.common.extensions.isZero @@ -159,7 +156,7 @@ abstract class ProductOnboardingService( OnboardingWalletBalance.criticalError(customError) } else { val balance = if (valueOfAmount.isZero()) { - OnboardingWalletBalance.done(valueOfAmount, hasForIncomingTransactions(), currency) + OnboardingWalletBalance.done(valueOfAmount, hasForIncomingTransactions(wallet), currency) } else { OnboardingWalletBalance.done(valueOfAmount, false, currency) } @@ -188,8 +185,8 @@ abstract class ProductOnboardingService( cardInfoStorage.activationFinished(scanResponse.card.cardId) } - protected open suspend fun hasForIncomingTransactions(): Boolean { - return false + protected open suspend fun hasForIncomingTransactions(wallet: Wallet): Boolean { + return wallet.recentTransactions.isNotEmpty() } protected open fun tryToProceed() { From 8ed876086db7f7242dfc690ec13367e786d21002 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 11 Oct 2021 23:44:29 +0300 Subject: [PATCH 060/189] Updated on 2026-08-14 --- .../tap/common/leapfrogWidget/Animations.kt | 25 ++-- .../tap/common/leapfrogWidget/LeapView.kt | 113 ++++++++++++++- .../common/leapfrogWidget/LeapfrogWidget.kt | 131 +++++++++--------- 3 files changed, 191 insertions(+), 78 deletions(-) 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 index b89618ee5f..77c9f1fcbb 100644 --- a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/Animations.kt +++ b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/Animations.kt @@ -25,24 +25,29 @@ import android.view.animation.LinearInterpolator typealias ProgressListener = (Int) -> Unit -fun AnimatorSet.createProgressListener(duration: Long, listener: ProgressListener?): Animator { +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.initAnimation(animDuration: Long, properties: Properties): AnimatorSet { - elevation = properties.elevationEnd - scaleX = properties.scaleEnd - scaleY = properties.scaleEnd - +fun View.unfoldAnimation(animDuration: Long, properties: AnimationProperties): AnimatorSet { val translate = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslationStart, properties.yTranslationEnd) translate.duration = animDuration return AnimatorSet().apply { playTogether(translate) } } -fun View.leapAnimation(animDuration: Long, properties: Properties, overLift: Float): AnimatorSet { +fun View.foldAnimation(animDuration: Long, properties: AnimationProperties): AnimatorSet { + val translate = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslationStart, properties.yTranslationEnd) + translate.duration = animDuration + + return AnimatorSet().apply { + playSequentially(translate) + } +} + +fun View.leapAnimation(animDuration: Long, properties: AnimationProperties, overLift: Float): AnimatorSet { val halfDuration = animDuration / 2 val upTo = (height.toFloat() + properties.yTranslationStart + overLift) * -1 @@ -80,7 +85,7 @@ fun View.leapAnimation(animDuration: Long, properties: Properties, overLift: Flo } } -fun View.leapBackAnimation(animDuration: Long, properties: Properties, calculator: PropertyCalculator): AnimatorSet { +fun View.leapBackAnimation(animDuration: Long, properties: AnimationProperties, calculator: PropertyCalculator): AnimatorSet { val halfDuration = animDuration / 2 val upTo = ((height).toFloat() - calculator.yTranslationFactor) * -1 @@ -117,7 +122,7 @@ fun View.leapBackAnimation(animDuration: Long, properties: Properties, calculato } } -fun View.pullUpAnimation(leapDuration: Long, properties: Properties): AnimatorSet { +fun View.pullUpAnimation(leapDuration: Long, properties: AnimationProperties): AnimatorSet { val pullDuration = leapDuration / 2 val delayDuration = leapDuration / 2 @@ -135,7 +140,7 @@ fun View.pullUpAnimation(leapDuration: Long, properties: Properties): AnimatorSe } } -fun View.pullDownAnimation(leapDuration: Long, properties: Properties): AnimatorSet { +fun View.pullDownAnimation(leapDuration: Long, properties: AnimationProperties): AnimatorSet { val pullDuration = leapDuration / 2 val delayDuration = leapDuration / 4 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 index b3a115f964..a9d5212f3e 100644 --- a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapView.kt +++ b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapView.kt @@ -9,36 +9,124 @@ class LeapView( val view: View, val index: Int, position: Int, - val maximalPosition: Int, - val initialProperties: Properties + private val maximalPosition: Int, + private val calculator: PropertyCalculator, ) { + var currentProperties: AnimationProperties + private set + var currentPosition: Int = position private set var previousPosition: Int = position private set - fun changePositionByLeap() { + init { + val initialProperties = createInitialProperty(position) + currentProperties = AnimationProperties.from(initialProperties, initialProperties) + + view.elevation = initialProperties.elevation + view.translationY = initialProperties.yTranslation + view.scaleX = initialProperties.scale + view.scaleY = initialProperties.scale +// initialProperties.hasForeground + } + + fun leap(): LeapFrogAnimation { previousPosition = currentPosition currentPosition = when (previousPosition) { 0 -> maximalPosition else -> currentPosition - 1 } + updateProperties(createLeapAnimationProperty(previousPosition, currentPosition)) + + return when { + previousPosition == 0 && currentPosition == maximalPosition -> LeapFrogAnimation.LEAP + else -> LeapFrogAnimation.PULL + } } - fun changePositionByLeapBack() { + fun leapBack(): LeapFrogAnimation { previousPosition = currentPosition currentPosition = when (previousPosition) { maximalPosition -> 0 else -> currentPosition + 1 } + updateProperties(createLeapAnimationProperty(previousPosition, currentPosition)) + + return when { + previousPosition == maximalPosition && currentPosition == 0 -> LeapFrogAnimation.LEAP + else -> LeapFrogAnimation.PULL + } + } + + fun fold() { + updateProperties(currentProperties.toFold()) + } + + fun unfold() { + updateProperties(currentProperties.toUnfold(calculator)) + } + + private fun updateProperties(properties: AnimationProperties) { + currentProperties = properties + } + + private fun createInitialProperty(endPosition: Int): Properties { + return Properties( + endPosition, + calculator.scale(endPosition), + calculator.elevation(endPosition, maximalPosition + 1), + calculator.yTranslation(0), + calculator.hasForeground(endPosition), + ) + } + + private fun createLeapAnimationProperty(startPosition: Int, endPosition: Int): AnimationProperties { + return AnimationProperties( + startPosition, + endPosition, + calculator.scale(startPosition), + calculator.scale(endPosition), + calculator.elevation(startPosition, maximalPosition + 1), + calculator.elevation(endPosition, maximalPosition + 1), + calculator.yTranslation(startPosition), + calculator.yTranslation(endPosition), + calculator.hasForeground(startPosition), + calculator.hasForeground(endPosition), + ) + } + + private fun AnimationProperties.toUnfold(calculator: PropertyCalculator): AnimationProperties { + val start = this.yTranslationEnd + val end = calculator.yTranslation(this.positionEnd) + return this.copy(yTranslationStart = start, yTranslationEnd = end) + } + + private fun AnimationProperties.toFold(): AnimationProperties { + val start = this.yTranslationEnd + val end = 0f + return this.copy(yTranslationStart = start, yTranslationEnd = end) } override fun toString(): String = "index: $index, position: $currentPosition, code: ${view.hashCode()}" + +} + +enum class LeapFrogAnimation { + LEAP, PULL } data class Properties( - val startPosition: Int, - val endPosition: Int, + val position: Int, + val scale: Float, + val elevation: Float, + val yTranslation: Float, + val hasForeground: Boolean, +) + +data class AnimationProperties( + val positionStart: Int, + val positionEnd: Int, val scaleStart: Float, val scaleEnd: Float, val elevationStart: Float, @@ -48,6 +136,7 @@ data class Properties( val hasForegroundStart: Boolean, val hasForegroundEnd: Boolean, ) { + override fun toString(): String { return """ scaleStart: $scaleStart, scaleEnd: $scaleEnd, @@ -56,4 +145,16 @@ data class Properties( hasForegroundStart : $hasForegroundStart, hasForegroundEnd: $hasForegroundEnd, """.trimIndent() } + + companion object { + fun from(start: Properties, end: Properties): AnimationProperties { + return AnimationProperties( + start.position, end.position, + start.scale, end.scale, + start.elevation, end.elevation, + start.yTranslation, end.yTranslation, + start.hasForeground, end.hasForeground + ) + } + } } \ 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 index e88e1cf49a..423be3b97b 100644 --- a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapfrogWidget.kt +++ b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapfrogWidget.kt @@ -15,15 +15,18 @@ class LeapfrogWidget( private val parentContainer: FrameLayout, private val calculator: PropertyCalculator = PropertyCalculator(), ) { - private val initAnimationDuration = 500L + private val foldAnimationDuration = 500L private val leapAnimationDuration = 800L private val leapBackAnimationDuration = 800L private val leapViews = mutableListOf() + private var foldUnfoldProgress: Int = 100 private var leapProgress: Int = 100 private var leapBackProgress: Int = 100 + private var isFolded: Boolean = true + init { calculator.setTranslationConverter { parentContainer.dpToPx(it) } init() @@ -35,54 +38,65 @@ class LeapfrogWidget( val maxPosition = parentContainer.childCount - 1 parentContainer.children.forEachIndexed { index, view -> val initialPosition = maxPosition - index - val initialProperties = createViewProperty(0, initialPosition) - leapViews.add(LeapView(view, index, initialPosition, maxPosition, initialProperties)) + leapViews.add(LeapView(view, index, initialPosition, maxPosition, calculator)) } - resetToInitialState() } - private fun resetToInitialState(withAnimation: Boolean = true, duration: Long = initAnimationDuration) { - if (!viewIsFullFledged()) return + fun fold(listener: ProgressListener? = null) { + if (!canFoldUnfold() || isFolded) return - if (withAnimation) { - val animatorsList = mutableListOf() - leapViews.forEach { animatorsList.add(it.view.initAnimation(duration, it.initialProperties)) } - val animator = AnimatorSet() - animator.interpolator = AccelerateDecelerateInterpolator() - animator.playTogether(animatorsList.toList()) - animator.start() - } else { - leapViews.forEach { - it.view.scaleX = it.initialProperties.scaleEnd - it.view.scaleY = it.initialProperties.scaleEnd - it.view.translationY = it.initialProperties.yTranslationEnd - it.view.elevation = it.initialProperties.elevationEnd + createFoldUnfoldAnimation(true, foldAnimationDuration, listener) + } + + fun unfold(listener: ProgressListener? = null) { + if (!canFoldUnfold() || !isFolded) return + + createFoldUnfoldAnimation(false, foldAnimationDuration, listener) + } + + private fun createFoldUnfoldAnimation(isFoldAnimation: Boolean, duration: Long, listener: ProgressListener?) { + val animatorsList = mutableListOf() + leapViews.forEach { + if (isFoldAnimation) { + it.fold() + animatorsList.add(it.view.foldAnimation(duration, it.currentProperties)) + } else { + it.unfold() + animatorsList.add(it.view.unfoldAnimation(duration, it.currentProperties)) } } + animatorsList.add(createProgressListener(duration) { + foldUnfoldProgress = it + if (it == 100) isFolded = isFoldAnimation + }) + animatorsList.add(createProgressListener(duration, listener)) + + val animator = AnimatorSet() + animator.interpolator = AccelerateDecelerateInterpolator() + animator.playTogether(animatorsList.toList()) + animator.start() } fun leap(listener: ProgressListener? = null) { if (!canLeap()) return - // if not - then it's ready for pullUp - fun isReadyForLeap(leapView: LeapView): Boolean { - return leapView.previousPosition == 0 && leapView.currentPosition == leapView.maximalPosition - } - + val duration = leapAnimationDuration val animatorsList = mutableListOf() leapViews.forEach { - it.changePositionByLeap() - val properties = createViewProperty(it.previousPosition, it.currentPosition) - if (isReadyForLeap(it)) { - val overLift = calculator.overLift(leapViews.size) - animatorsList.add(it.view.leapAnimation(leapAnimationDuration, properties, overLift)) - } else { - animatorsList.add(it.view.pullUpAnimation(leapAnimationDuration, properties)) + when (it.leap()) { + LeapFrogAnimation.LEAP -> { + val overLift = calculator.overLift(leapViews.size) + animatorsList.add(it.view.leapAnimation(duration, it.currentProperties, overLift)) + } + LeapFrogAnimation.PULL -> { + animatorsList.add(it.view.pullUpAnimation(duration, it.currentProperties)) + } } } + animatorsList.add(createProgressListener(duration) { leapProgress = it }) + animatorsList.add(createProgressListener(duration, listener)) + val animator = AnimatorSet() - animatorsList.add(animator.createProgressListener(leapAnimationDuration) { leapProgress = it }) - animatorsList.add(animator.createProgressListener(leapAnimationDuration, listener)) animator.playTogether(animatorsList.toList()) animator.start() } @@ -90,25 +104,21 @@ class LeapfrogWidget( fun leapBack(listener: ProgressListener? = null) { if (!canLeapBack()) return - // if not - then it's ready for pullDown - fun isReadyForLeapBack(leapView: LeapView): Boolean { - return leapView.previousPosition == leapView.maximalPosition && leapView.currentPosition == 0 - } - + val duration = leapBackAnimationDuration val animatorsList = mutableListOf() leapViews.forEach { - it.changePositionByLeapBack() - val properties = createViewProperty(it.previousPosition, it.currentPosition) - if (isReadyForLeapBack(it)) { - val overLift = calculator.overLift(leapViews.size) - animatorsList.add(it.view.leapBackAnimation(leapBackAnimationDuration, properties, calculator)) - } else { - animatorsList.add(it.view.pullDownAnimation(leapBackAnimationDuration, properties)) + when (it.leapBack()) { + LeapFrogAnimation.LEAP -> { + animatorsList.add(it.view.leapBackAnimation(duration, it.currentProperties, calculator)) + } + LeapFrogAnimation.PULL -> { + animatorsList.add(it.view.pullDownAnimation(duration, it.currentProperties)) + } } } + animatorsList.add(createProgressListener(duration) { leapBackProgress = it }) + animatorsList.add(createProgressListener(duration, listener)) val animator = AnimatorSet() - animatorsList.add(animator.createProgressListener(leapBackAnimationDuration) { leapBackProgress = it }) - animatorsList.add(animator.createProgressListener(leapBackAnimationDuration, listener)) animator.playTogether(animatorsList.toList()) animator.start() } @@ -119,9 +129,19 @@ class LeapfrogWidget( 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 + isFolded -> false leapBackInProgress() -> false leapProgress < 70 -> false else -> true @@ -131,29 +151,16 @@ class LeapfrogWidget( private fun canLeapBack(): Boolean { return when { !viewIsFullFledged() -> false + isFolded -> 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 - - private fun createViewProperty(startPosition: Int, endPosition: Int): Properties { - return Properties( - startPosition, - endPosition, - calculator.scale(startPosition), - calculator.scale(endPosition), - calculator.elevation(startPosition, parentContainer.childCount), - calculator.elevation(endPosition, parentContainer.childCount), - calculator.yTranslation(startPosition), - calculator.yTranslation(endPosition), - calculator.hasForeground(startPosition), - calculator.hasForeground(endPosition), - ) - } } class PropertyCalculator( From 033028510647d1db2c5590e44cd7ae4f1819dc0d Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 12 Oct 2021 00:11:31 +0300 Subject: [PATCH 061/189] Updated on 2026-08-14 --- .../tap/common/leapfrogWidget/LeapView.kt | 39 +++++++++++++++++-- .../common/leapfrogWidget/LeapfrogWidget.kt | 14 +++++++ 2 files changed, 49 insertions(+), 4 deletions(-) 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 index a9d5212f3e..0c4d37a066 100644 --- a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapView.kt +++ b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapView.kt @@ -23,11 +23,14 @@ class LeapView( init { val initialProperties = createInitialProperty(position) currentProperties = AnimationProperties.from(initialProperties, initialProperties) + initView(initialProperties) + } - view.elevation = initialProperties.elevation - view.translationY = initialProperties.yTranslation - view.scaleX = initialProperties.scale - view.scaleY = initialProperties.scale + private fun initView(properties: Properties) { + view.elevation = properties.elevation + view.translationY = properties.yTranslation + view.scaleX = properties.scale + view.scaleY = properties.scale // initialProperties.hasForeground } @@ -67,6 +70,17 @@ class LeapView( updateProperties(currentProperties.toUnfold(calculator)) } + fun getState(): LeapViewState { + return LeapViewState(index, currentPosition, previousPosition, currentProperties) + } + + fun applyState(state: LeapViewState) { + previousPosition = state.previousPosition + currentPosition = state.currentPosition + updateProperties(state.properties) + initView(state.properties.endProperties()) + } + private fun updateProperties(properties: AnimationProperties) { currentProperties = properties } @@ -112,6 +126,13 @@ class LeapView( } +data class LeapViewState( + val index: Int, + val currentPosition: Int, + val previousPosition: Int, + val properties: AnimationProperties +) + enum class LeapFrogAnimation { LEAP, PULL } @@ -137,6 +158,16 @@ data class AnimationProperties( val hasForegroundEnd: Boolean, ) { + fun endProperties(): Properties { + return Properties( + positionEnd, + scaleEnd, + elevationEnd, + yTranslationEnd, + hasForegroundEnd, + ) + } + override fun toString(): String { return """ scaleStart: $scaleStart, scaleEnd: $scaleEnd, 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 index 423be3b97b..37b04d4f2a 100644 --- a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapfrogWidget.kt +++ b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapfrogWidget.kt @@ -127,6 +127,15 @@ class LeapfrogWidget( return leapViews.firstOrNull { it.currentPosition == position }?.view } + fun getState(): LeapfrogWidgetState = LeapfrogWidgetState(isFolded, leapViews.map { it.getState() }) + + fun applyState(state: LeapfrogWidgetState) { + isFolded = state.isFolded + state.leapViewStates.forEach { state -> + leapViews.firstOrNull { it.index == state.index }?.applyState(state) + } + } + private fun viewIsFullFledged(): Boolean = parentContainer.childCount > 1 private fun canFoldUnfold(): Boolean { @@ -163,6 +172,11 @@ class LeapfrogWidget( private fun leapBackInProgress(): Boolean = leapBackProgress != 100 } +data class LeapfrogWidgetState( + val isFolded: Boolean, + val leapViewStates: List +) + class PropertyCalculator( val elevationFactor: Float = 1f, val decreaseScaleFactor: Float = 0.1f, From fd214bffd818a7d1b88a1dbfd666ed99134fa717 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 13 Oct 2021 18:54:49 +0300 Subject: [PATCH 062/189] Updated on 2026-08-14 --- .../main/java/com/tangem/tap/MainActivity.kt | 2 +- .../com/tangem/tap/common/redux/AppReducer.kt | 2 + .../com/tangem/tap/common/redux/AppState.kt | 4 + .../tap/common/redux/global/GlobalAction.kt | 8 +- .../tap/common/redux/global/GlobalReducer.kt | 10 +- .../tap/common/redux/global/GlobalState.kt | 28 ++--- .../java/com/tangem/tap/domain/ProductType.kt | 8 ++ .../com/tangem/tap/domain/TangemSdkManager.kt | 28 ++--- .../com/tangem/tap/domain/TapWalletManager.kt | 10 +- .../domain/extensions/WalletManagerFactory.kt | 4 +- .../domain/tasks/CreateWalletAndRescanTask.kt | 1 + .../tap/domain/tasks/CreateWalletsTask.kt | 13 +-- .../tangem/tap/domain/tasks/ScanNoteTask.kt | 64 +++------- .../CreateProductWalletAndRescanTask.kt | 80 +++++-------- .../tasks/product/CreateProductWalletsTask.kt | 49 ++++++++ .../domain/tasks/product/ScanProductTask.kt | 109 +++++++++++++----- .../tap/domain/twins/FinalizeTwinTask.kt | 10 +- .../tap/domain/twins/TwinCardsManager.kt | 16 +-- .../features/details/redux/DetailsAction.kt | 18 +-- .../features/details/redux/DetailsReducer.kt | 2 +- .../redux/twins/CreateTwinWalletMiddleware.kt | 4 +- .../redux/twins/CreateTwinWalletState.kt | 14 +-- .../walletconnect/WalletConnectMiddleware.kt | 4 +- .../tap/features/home/redux/HomeMiddleware.kt | 7 +- .../products/note/OnboardingNoteFragment.kt | 4 +- .../note/redux/OnboardingNoteMiddleware.kt | 2 +- .../wallet/OnboardingWalletFragment.kt | 1 + .../onboarding/service/OnboardingHelper.kt | 18 +-- .../service/OnboardingNoteService.kt | 4 +- .../service/ProductOnboardingService.kt | 7 +- .../send/redux/middlewares/SendMiddleware.kt | 4 +- .../features/tokens/redux/TokensMiddleware.kt | 2 +- .../middlewares/MultiWalletMiddleware.kt | 17 ++- .../redux/middlewares/WalletMiddleware.kt | 12 +- .../redux/middlewares/WarningsMiddleware.kt | 6 +- .../tap/features/wallet/ui/WalletFragment.kt | 2 +- 36 files changed, 313 insertions(+), 261 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/domain/ProductType.kt create mode 100644 app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletsTask.kt diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index 49856db42b..dbef280f6f 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -74,7 +74,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler { notificationsHandler = NotificationsHandler(fragment_container) val backStackIsEmpty = supportFragmentManager.backStackEntryCount == 0 - val isScannedBefore = store.state.globalState.scanNoteResponse != null + val isScannedBefore = store.state.globalState.scanResponse != null val isOnboardingServiceActive = store.state.globalState.onboardingService != null if (backStackIsEmpty || (!isOnboardingServiceActive && !isScannedBefore)) { store.dispatch(NavigationAction.NavigateTo(AppScreen.Home)) diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt index 4de2f24938..38c76819b3 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt @@ -7,6 +7,7 @@ import com.tangem.tap.features.details.redux.walletconnect.WalletConnectReducer import com.tangem.tap.features.disclaimer.redux.DisclaimerReducer import com.tangem.tap.features.home.redux.HomeReducer import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteReducer +import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsReducer import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletReducer import com.tangem.tap.features.send.redux.reducers.SendScreenReducer import com.tangem.tap.features.tokens.redux.TokensReducer @@ -23,6 +24,7 @@ fun appReducer(action: Action, state: AppState?): AppState { homeState = HomeReducer.reduce(action, state), onboardingNoteState = OnboardingNoteReducer.reduce(action, state), onboardingWalletState = OnboardingWalletReducer.reduce(action, state), + onboardingOtherCardsState = OnboardingOtherCardsReducer.reduce(action, state), walletState = WalletReducer.reduce(action, state), sendState = SendScreenReducer.reduce(action, state.sendState), detailsState = DetailsReducer.reduce(action, state), diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt index ba9aa021d1..40e0c80077 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt @@ -14,6 +14,8 @@ import com.tangem.tap.features.home.redux.HomeMiddleware import com.tangem.tap.features.home.redux.HomeState import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteMiddleware import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteState +import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsMiddleware +import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsState import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletMiddleware import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletState import com.tangem.tap.features.send.redux.middlewares.SendMiddleware @@ -31,6 +33,7 @@ data class AppState( val homeState: HomeState = HomeState(), val onboardingNoteState: OnboardingNoteState = OnboardingNoteState(), val onboardingWalletState: OnboardingWalletState = OnboardingWalletState(), + val onboardingOtherCardsState: OnboardingOtherCardsState = OnboardingOtherCardsState(), val walletState: WalletState = WalletState(), val sendState: SendState = SendState(), val detailsState: DetailsState = DetailsState(), @@ -47,6 +50,7 @@ data class AppState( HomeMiddleware.handler, OnboardingNoteMiddleware.handler, OnboardingWalletMiddleware.handler, + OnboardingOtherCardsMiddleware.handler, WalletMiddleware().walletMiddleware, SendMiddleware().sendMiddleware, DetailsMiddleware().detailsMiddleware, 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 4021557fac..50efb26994 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 @@ -8,7 +8,7 @@ import com.tangem.tap.domain.TapError import com.tangem.tap.domain.configurable.config.ConfigManager import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager -import com.tangem.tap.domain.tasks.ScanNoteResponse +import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.features.details.redux.SecurityOption import com.tangem.tap.features.feedback.EmailData import com.tangem.tap.features.feedback.FeedbackManager @@ -34,18 +34,18 @@ sealed class GlobalAction : Action { } data class ReadCard( - val onSuccess: ((ScanNoteResponse) -> Unit)? = null, + val onSuccess: ((ScanResponse) -> Unit)? = null, val onFailure: ((TangemError) -> Unit)? = null, val messageResId: Int? = null, ) : GlobalAction() object ScanFailsCounter { - data class ChooseBehavior(val result: CompletionResult) : GlobalAction() + data class ChooseBehavior(val result: CompletionResult) : GlobalAction() object Reset : GlobalAction() object Increment : GlobalAction() } - data class SaveScanNoteResponse(val scanNoteResponse: ScanNoteResponse) : GlobalAction() + data class SaveScanNoteResponse(val scanResponse: ScanResponse) : GlobalAction() data class SetIfCardVerifiedOnline(val verified: Boolean) : GlobalAction() 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 380fe6847f..30eabac6e4 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 @@ -23,7 +23,7 @@ fun globalReducer(action: Action, state: AppState): GlobalState { globalState.copy(scanCardFailsCounter = 0) } is GlobalAction.SaveScanNoteResponse -> - globalState.copy(scanNoteResponse = action.scanNoteResponse) + globalState.copy(scanResponse = action.scanResponse) is GlobalAction.ChangeAppCurrency -> { globalState.copy(appCurrency = action.appCurrency) } @@ -35,17 +35,17 @@ fun globalReducer(action: Action, state: AppState): GlobalState { } is GlobalAction.SetWarningManager -> globalState.copy(warningManager = action.warningManager) is GlobalAction.UpdateWalletSignedHashes -> { - val wallet = globalState.scanNoteResponse?.card + val wallet = globalState.scanResponse?.card ?.wallet(action.walletPublicKey) ?.copy( totalSignedHashes = action.walletSignedHashes, remainingSignatures = action.remainingSignatures ) - val card = globalState.scanNoteResponse?.card - wallet?.let { globalState.scanNoteResponse.card.updateWallet(wallet) } + val card = globalState.scanResponse?.card + wallet?.let { globalState.scanResponse.card.updateWallet(wallet) } if (card != null) { - globalState.copy(scanNoteResponse = globalState.scanNoteResponse.copy(card = card)) + globalState.copy(scanResponse = globalState.scanResponse.copy(card = card)) } else { 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 0753b6d472..94eb578fc4 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 @@ -6,7 +6,7 @@ import com.tangem.tap.domain.PayIdManager import com.tangem.tap.domain.TapWalletManager import com.tangem.tap.domain.configurable.config.ConfigManager import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager -import com.tangem.tap.domain.tasks.ScanNoteResponse +import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.features.feedback.FeedbackManager import com.tangem.tap.features.onboarding.service.ProductOnboardingService import com.tangem.tap.network.coinmarketcap.CoinMarketCapService @@ -14,19 +14,19 @@ import com.tangem.tap.network.moonpay.MoonPayUserStatus import org.rekotlin.StateType data class GlobalState( - val scanNoteResponse: ScanNoteResponse? = null, - val cardVerifiedOnline: Boolean = false, - val tapWalletManager: TapWalletManager = TapWalletManager(), - val payIdManager: PayIdManager = PayIdManager(), - val coinMarketCapService: CoinMarketCapService = CoinMarketCapService(), - val configManager: ConfigManager? = null, - val warningManager: WarningMessagesManager? = null, - val feedbackManager: FeedbackManager? = null, - val appCurrency: FiatCurrencyName = DEFAULT_FIAT_CURRENCY, - val scanCardFailsCounter: Int = 0, - val dialog: StateDialog? = null, - val moonPayUserStatus: MoonPayUserStatus? = null, - val onboardingService: ProductOnboardingService? = null, + val scanResponse: ScanResponse? = null, + val cardVerifiedOnline: Boolean = false, + val tapWalletManager: TapWalletManager = TapWalletManager(), + val payIdManager: PayIdManager = PayIdManager(), + val coinMarketCapService: CoinMarketCapService = CoinMarketCapService(), + val configManager: ConfigManager? = null, + val warningManager: WarningMessagesManager? = null, + val feedbackManager: FeedbackManager? = null, + val appCurrency: FiatCurrencyName = DEFAULT_FIAT_CURRENCY, + val scanCardFailsCounter: Int = 0, + val dialog: StateDialog? = null, + val moonPayUserStatus: MoonPayUserStatus? = null, + val onboardingService: ProductOnboardingService? = null, ) : StateType typealias CryptoCurrencyName = String diff --git a/app/src/main/java/com/tangem/tap/domain/ProductType.kt b/app/src/main/java/com/tangem/tap/domain/ProductType.kt new file mode 100644 index 0000000000..02f0246c82 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/ProductType.kt @@ -0,0 +1,8 @@ +package com.tangem.tap.domain + +/** +[REDACTED_AUTHOR] + */ +enum class ProductType { + Note, Wallet, Twin, Other +} \ No newline at end of file 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 4cc78b775b..5253a9f91c 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -21,9 +21,8 @@ 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.domain.tasks.CreateWalletAndRescanTask -import com.tangem.tap.domain.tasks.ScanNoteResponse -import com.tangem.tap.domain.tasks.ScanNoteTask import com.tangem.tap.domain.tasks.product.ScanProductTask +import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.domain.twins.isTangemTwin import com.tangem.wallet.R import kotlinx.coroutines.Dispatchers @@ -33,23 +32,10 @@ import kotlin.coroutines.suspendCoroutine class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Context) { - @Deprecated("Use can product") - suspend fun scanNote( - analyticsHandler: AnalyticsHandler, - messageRes: Int? = null, - ): CompletionResult { - analyticsHandler.triggerEvent(AnalyticsEvent.READY_TO_SCAN, null) - - return runTaskAsyncReturnOnMain( - ScanNoteTask(), - initialMessage = Message(context.getString(messageRes ?: R.string.initial_message_scan_header)) - ).also { sendScanFailuresToAnalytics(analyticsHandler, it) } - } - suspend fun scanProduct( analyticsHandler: AnalyticsHandler, messageRes: Int? = null, - ): CompletionResult { + ): CompletionResult { analyticsHandler.triggerEvent(AnalyticsEvent.READY_TO_SCAN, null) val message = Message(context.getString(messageRes ?: R.string.initial_message_scan_header)) @@ -57,13 +43,17 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co .also { sendScanFailuresToAnalytics(analyticsHandler, it) } } - suspend fun createProductWallet(cardId: String?): CompletionResult { - return runTaskAsync(CreateProductWalletAndRescanTask(), cardId, Message(context.getString(R.string.initial_message_create_wallet_body))) + suspend fun createProductWallet(scanResponse: ScanResponse): CompletionResult { + return runTaskAsync( + CreateProductWalletAndRescanTask(scanResponse.productType), + scanResponse.card.cardId, + Message(context.getString(R.string.initial_message_create_wallet_body)) + ) } private fun sendScanFailuresToAnalytics( analyticsHandler: AnalyticsHandler, - result: CompletionResult + result: CompletionResult ) { if (result is CompletionResult.Failure && result.error is TangemSdkError) { (result.error as? TangemSdkError)?.let { error -> 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 507db2ad21..8a592c14ad 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -14,7 +14,7 @@ import com.tangem.tap.domain.TapWorkarounds.isStart2Coin import com.tangem.tap.domain.TapWorkarounds.isTestCard import com.tangem.tap.domain.configurable.config.ConfigManager import com.tangem.tap.domain.extensions.* -import com.tangem.tap.domain.tasks.ScanNoteResponse +import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.domain.tokens.CardCurrencies import com.tangem.tap.domain.twins.TwinsHelper import com.tangem.tap.domain.twins.isTangemTwin @@ -77,7 +77,7 @@ class TapWalletManager { handleFiatRatesResult(results) } - suspend fun onCardScanned(data: ScanNoteResponse, addAnalyticsEvent: Boolean = false) { + suspend fun onCardScanned(data: ScanResponse, addAnalyticsEvent: Boolean = false) { if (addAnalyticsEvent) { FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.CARD_IS_SCANNED, data.card, data.walletData?.blockchain) } @@ -108,7 +108,7 @@ class TapWalletManager { } } - private fun updateConfigManager(data: ScanNoteResponse) { + private fun updateConfigManager(data: ScanResponse) { val configManager = store.state.globalState.configManager val blockchain = data.getBlockchain() if (data.card.isStart2Coin) { @@ -124,7 +124,7 @@ class TapWalletManager { } } - suspend fun loadData(data: ScanNoteResponse) { + suspend fun loadData(data: ScanResponse) { withContext(Dispatchers.Main) { store.dispatch(WalletAction.LoadCardInfo(data.card)) @@ -220,7 +220,7 @@ class TapWalletManager { } } - suspend fun reloadData(data: ScanNoteResponse) { + suspend fun reloadData(data: ScanResponse) { withContext(Dispatchers.Main) { when { data.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> { 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 0ab0ec0293..99ed8231c4 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 @@ -8,7 +8,7 @@ import com.tangem.common.card.CardWallet import com.tangem.common.card.EllipticCurve import com.tangem.common.extensions.hexToBytes import com.tangem.tap.domain.TapWorkarounds.isTestCard -import com.tangem.tap.domain.tasks.ScanNoteResponse +import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.domain.twins.isTangemTwin fun WalletManagerFactory.makeWalletManagerForApp( @@ -50,7 +50,7 @@ fun WalletManagerFactory.makeWalletManagersForApp( } fun WalletManagerFactory.makePrimaryWalletManager( - data: ScanNoteResponse, + data: ScanResponse, ): WalletManager? { val card = data.card val blockchain = if (card.isTestCard) { diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt index c75a37a709..1487f07e37 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt @@ -11,6 +11,7 @@ import com.tangem.operations.PreflightReadMode import com.tangem.operations.PreflightReadTask import com.tangem.operations.wallet.CreateWalletCommand +@Deprecated("Use CreateProductWalletAndRescanTask instead") class CreateWalletAndRescanTask : CardSessionRunnable { override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt index 34d203eac6..6de34f651a 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt @@ -9,6 +9,7 @@ import com.tangem.operations.PreflightReadMode import com.tangem.operations.PreflightReadTask import com.tangem.operations.wallet.CreateWalletCommand +@Deprecated("Use CreateProductWalletsResponse instead") class CreateWalletsTask(curves: List? = null) : CardSessionRunnable { private val curves = curves ?: listOf( @@ -25,8 +26,8 @@ class CreateWalletsTask(curves: List? = null) : CardSessionRunnab } private fun createWallet( - curve: EllipticCurve, session: CardSession, - callback: (result: CompletionResult) -> Unit + curve: EllipticCurve, session: CardSession, + callback: (result: CompletionResult) -> Unit ) { CreateWalletCommand(curve).run(session) { result -> @@ -45,12 +46,4 @@ class CreateWalletsTask(curves: List? = null) : CardSessionRunnab } } } -} - -private fun EllipticCurve.toWalletIndex(): Int { - return when (this) { - EllipticCurve.Secp256k1 -> 0 - EllipticCurve.Ed25519 -> 1 - EllipticCurve.Secp256r1 -> 2 - } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt index 39f5d63af0..b2b415dc15 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt @@ -1,56 +1,29 @@ package com.tangem.tap.domain.tasks import com.tangem.* -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.Token import com.tangem.common.CompletionResult import com.tangem.common.card.Card import com.tangem.common.card.FirmwareVersion -import com.tangem.common.card.WalletData import com.tangem.common.core.CardSession import com.tangem.common.core.CardSessionRunnable import com.tangem.common.core.TangemError import com.tangem.common.extensions.toHexString -import com.tangem.operations.CommandResponse import com.tangem.operations.ScanTask import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand +import com.tangem.tap.domain.ProductType import com.tangem.tap.domain.TapSdkError -import com.tangem.tap.domain.TapWorkarounds.getTangemNoteBlockchain import com.tangem.tap.domain.TapWorkarounds.isExcluded import com.tangem.tap.domain.TapWorkarounds.isTangemNote import com.tangem.tap.domain.extensions.getSingleWallet +import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.domain.twins.TwinCardsManager import com.tangem.tap.domain.twins.isTangemTwin -data class ScanNoteResponse( - val card: Card, - val walletData: WalletData?, - val secondTwinPublicKey: String? = null, -) : CommandResponse { - - fun getBlockchain(): Blockchain { - if (card.isTangemNote()) return card.getTangemNoteBlockchain() ?: return Blockchain.Unknown - val blockchainName: String = walletData?.blockchain ?: return Blockchain.Unknown - return Blockchain.fromId(blockchainName) - } - - fun getPrimaryToken(): Token? { - val cardToken = walletData?.token ?: return null - return Token( - cardToken.name, - cardToken.symbol, - cardToken.contractAddress, - cardToken.decimals, - Blockchain.fromId(walletData.blockchain) - ) - } -} - -class ScanNoteTask(val card: Card? = null) : CardSessionRunnable { +class ScanNoteTask(val card: Card? = null) : CardSessionRunnable { override fun run( session: CardSession, - callback: (result: CompletionResult) -> Unit, + callback: (result: CompletionResult) -> Unit, ) { ScanTask().run(session) { result -> when (result) { @@ -71,7 +44,7 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable) -> Unit, + callback: (result: CompletionResult) -> Unit, ) { val walletData = session.environment.walletData if (card.wallets.isEmpty()) { - callback(CompletionResult.Success(ScanNoteResponse(card, walletData))) + callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, walletData))) return } @@ -93,14 +66,14 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable when (result) { is CompletionResult.Success -> - callback(CompletionResult.Success(ScanNoteResponse(result.data, walletData))) + callback(CompletionResult.Success(ScanResponse(result.data, ProductType.Other, walletData))) is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) } } @@ -109,34 +82,35 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable) -> Unit, + callback: (result: CompletionResult) -> Unit, ) { ReadIssuerDataCommand().run(session) { readDataResult -> when (readDataResult) { is CompletionResult.Success -> { val publicKey = card.getSingleWallet()?.publicKey if (publicKey == null) { - callback(CompletionResult.Success(ScanNoteResponse(card, null))) + callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, null))) return@run } val verified = TwinCardsManager.verifyTwinPublicKey( - readDataResult.data.issuerData, publicKey + readDataResult.data.issuerData, publicKey ) if (verified) { val twinPublicKey = readDataResult.data.issuerData.sliceArray(0 until 65) callback(CompletionResult.Success( - ScanNoteResponse( - card = card, - walletData = session.environment.walletData, - secondTwinPublicKey = twinPublicKey.toHexString()) + ScanResponse( + card = card, + ProductType.Other, + walletData = session.environment.walletData, + secondTwinPublicKey = twinPublicKey.toHexString()) )) return@run } else { - callback(CompletionResult.Success(ScanNoteResponse(card, null))) + callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, null))) } } is CompletionResult.Failure -> - callback(CompletionResult.Success(ScanNoteResponse(card, null))) + callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, null))) } } } diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletAndRescanTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletAndRescanTask.kt index 6134e5de55..3b3c6ea2ae 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletAndRescanTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletAndRescanTask.kt @@ -1,6 +1,7 @@ import com.tangem.common.CompletionResult import com.tangem.common.card.Card import com.tangem.common.card.EllipticCurve +import com.tangem.common.card.FirmwareVersion import com.tangem.common.core.CardSession import com.tangem.common.core.CardSessionRunnable import com.tangem.common.core.TangemSdkError @@ -9,19 +10,25 @@ import com.tangem.operations.PreflightReadMode import com.tangem.operations.PreflightReadTask import com.tangem.operations.wallet.CreateWalletCommand import com.tangem.operations.wallet.CreateWalletResponse +import com.tangem.tap.domain.ProductType import com.tangem.tap.domain.TapWorkarounds.getTangemNoteBlockchain -import com.tangem.tap.domain.TapWorkarounds.isTangemNote -import com.tangem.tap.domain.TapWorkarounds.isTangemWallet +import com.tangem.tap.domain.tasks.product.CreateProductWalletsTask import com.tangem.tap.domain.tasks.product.ProductCommandProcessor -import com.tangem.tap.domain.twins.isTangemTwin +import com.tangem.tap.domain.tasks.product.getCurvesForNonCreatedWallets /** [REDACTED_AUTHOR] */ -class CreateProductWalletAndRescanTask : CardSessionRunnable { +class CreateProductWalletAndRescanTask( + private val type: ProductType, +) : CardSessionRunnable { override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { - CreateProductWalletTask().run(session) { result -> + val card = session.environment.card.guard { + callback(CompletionResult.Failure(TangemSdkError.CardError())) + return + } + CreateProductWalletTask(card, type).run(session) { result -> when (result) { is CompletionResult.Success -> PreflightReadTask(PreflightReadMode.FullCardRead).run(session, callback) is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) @@ -30,18 +37,16 @@ class CreateProductWalletAndRescanTask : CardSessionRunnable { } } -private class CreateProductWalletTask : CardSessionRunnable { +private class CreateProductWalletTask( + private val card: Card, + private val type: ProductType, +) : CardSessionRunnable { override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { - val card = session.environment.card.guard { - callback(CompletionResult.Failure(TangemSdkError.CardError())) - return - } - - val commandProcessor = when { - card.isTangemNote() -> CreateWalletTangemNote() - card.isTangemTwin() -> CreateWalletTangemTwin() - card.isTangemWallet() -> CreateWalletTangemWallet() + val commandProcessor = when (type) { + ProductType.Note -> CreateWalletTangemNote() + ProductType.Twin -> throw UnsupportedOperationException("Use the TwinCardsManager to create a wallet") + ProductType.Wallet -> CreateWalletTangemWallet() else -> CreateWalletOtherCards() } commandProcessor.proceed(card, session, callback) @@ -86,53 +91,28 @@ private class CreateWalletTangemWallet : ProductCommandProcessor) -> Unit ) { - throw UnsupportedOperationException() - } -} - -private class CreateWalletTangemTwin : ProductCommandProcessor { - override fun proceed( - card: Card, - session: CardSession, - callback: (result: CompletionResult) -> Unit - ) { - throw UnsupportedOperationException() + //TODO: } } private class CreateWalletOtherCards : ProductCommandProcessor { - private var index = 0 - - private val curves = EllipticCurve.values().toList() - override fun proceed( card: Card, session: CardSession, callback: (result: CompletionResult) -> Unit ) { - val curve = curves[index] - createWallet(curve, session, callback) - } + val firmwareVersion = card.firmwareVersion + val task = if (firmwareVersion < FirmwareVersion.MultiWalletAvailable) { + CreateProductWalletsTask(listOf(card.supportedCurves.first())) + } else { + CreateProductWalletsTask(card.getCurvesForNonCreatedWallets()) + } - private fun createWallet( - curve: EllipticCurve, - session: CardSession, - callback: (result: CompletionResult) -> Unit - ) { - CreateWalletCommand(curve).run(session) { result -> + task.run(session) { result -> when (result) { - is CompletionResult.Success -> { - if (index == curves.lastIndex) { - callback(CompletionResult.Success(result.data)) - return@run - } - index += 1 - createWallet(curves[index], session, callback) - } - is CompletionResult.Failure -> { - callback(CompletionResult.Failure(result.error)) - } + is CompletionResult.Success -> callback(CompletionResult.Success(result.data.createWalletResponses[0])) + is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) } } } diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletsTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletsTask.kt new file mode 100644 index 0000000000..fc00138a57 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletsTask.kt @@ -0,0 +1,49 @@ +package com.tangem.tap.domain.tasks.product + +import com.tangem.common.CompletionResult +import com.tangem.common.card.EllipticCurve +import com.tangem.common.core.CardSession +import com.tangem.common.core.CardSessionRunnable +import com.tangem.operations.CommandResponse +import com.tangem.operations.wallet.CreateWalletCommand +import com.tangem.operations.wallet.CreateWalletResponse + +/** +[REDACTED_AUTHOR] + */ +class CreateProductWalletsResponse( + val createWalletResponses: List +) : CommandResponse + +class CreateProductWalletsTask( + private val curves: List, +) : CardSessionRunnable { + + private val createdWalletsResponses = mutableListOf() + + override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { + val curve = curves[createdWalletsResponses.size] + createWallet(curve, session, callback) + } + + private fun createWallet( + curve: EllipticCurve, + session: CardSession, + callback: (result: CompletionResult) -> Unit + ) { + + CreateWalletCommand(curve).run(session) { result -> + when (result) { + is CompletionResult.Success -> { + createdWalletsResponses.add(result.data) + if (createdWalletsResponses.size == curves.size) { + callback(CompletionResult.Success(CreateProductWalletsResponse(createdWalletsResponses))) + return@run + } + createWallet(curves[createdWalletsResponses.size], session, 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/ScanProductTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt index 710e6e99ff..3493d1c322 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 @@ -1,29 +1,62 @@ package com.tangem.tap.domain.tasks.product import com.tangem.* +import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.common.Token import com.tangem.common.CompletionResult import com.tangem.common.card.Card +import com.tangem.common.card.EllipticCurve +import com.tangem.common.card.FirmwareVersion +import com.tangem.common.card.WalletData import com.tangem.common.core.CardSession import com.tangem.common.core.CardSessionRunnable import com.tangem.common.core.TangemError import com.tangem.common.extensions.toHexString +import com.tangem.operations.CommandResponse +import com.tangem.operations.PreflightReadMode +import com.tangem.operations.PreflightReadTask import com.tangem.operations.ScanTask import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand +import com.tangem.tap.domain.ProductType import com.tangem.tap.domain.TapSdkError +import com.tangem.tap.domain.TapWorkarounds.getTangemNoteBlockchain import com.tangem.tap.domain.TapWorkarounds.isExcluded import com.tangem.tap.domain.TapWorkarounds.isTangemNote import com.tangem.tap.domain.TapWorkarounds.isTangemWallet import com.tangem.tap.domain.extensions.getSingleWallet -import com.tangem.tap.domain.tasks.CreateWalletsTask -import com.tangem.tap.domain.tasks.ScanNoteResponse import com.tangem.tap.domain.twins.TwinCardsManager import com.tangem.tap.domain.twins.isTangemTwin -class ScanProductTask(val card: Card? = null) : CardSessionRunnable { +data class ScanResponse( + val card: Card, + val productType: ProductType, + val walletData: WalletData?, + val secondTwinPublicKey: String? = null, +) : CommandResponse { + + fun getBlockchain(): Blockchain { + if (productType == ProductType.Note) return card.getTangemNoteBlockchain() ?: return Blockchain.Unknown + val blockchainName: String = walletData?.blockchain ?: return Blockchain.Unknown + return Blockchain.fromId(blockchainName) + } + + fun getPrimaryToken(): Token? { + val cardToken = walletData?.token ?: return null + return Token( + cardToken.name, + cardToken.symbol, + cardToken.contractAddress, + cardToken.decimals, + Blockchain.fromId(walletData.blockchain) + ) + } +} + +class ScanProductTask(val card: Card? = null) : CardSessionRunnable { override fun run( session: CardSession, - callback: (result: CompletionResult) -> Unit, + callback: (result: CompletionResult) -> Unit, ) { ScanTask().run(session) { result -> when (result) { @@ -56,38 +89,38 @@ class ScanProductTask(val card: Card? = null) : CardSessionRunnable { +private class ScanNoteProcessor : ProductCommandProcessor { override fun proceed( card: Card, session: CardSession, - callback: (result: CompletionResult) -> Unit + callback: (result: CompletionResult) -> Unit ) { - callback(CompletionResult.Success(ScanNoteResponse(card, session.environment.walletData))) + callback(CompletionResult.Success(ScanResponse(card, ProductType.Note, session.environment.walletData))) } } -private class ScanWalletProcessor : ProductCommandProcessor { +private class ScanWalletProcessor : ProductCommandProcessor { override fun proceed( card: Card, session: CardSession, - callback: (result: CompletionResult) -> Unit + callback: (result: CompletionResult) -> Unit ) { - callback(CompletionResult.Success(ScanNoteResponse(card, session.environment.walletData))) + callback(CompletionResult.Success(ScanResponse(card, ProductType.Wallet, session.environment.walletData))) } } -private class ScanTwinProcessor : ProductCommandProcessor { +private class ScanTwinProcessor : ProductCommandProcessor { override fun proceed( card: Card, session: CardSession, - callback: (result: CompletionResult) -> Unit, + callback: (result: CompletionResult) -> Unit, ) { ReadIssuerDataCommand().run(session) { readDataResult -> when (readDataResult) { is CompletionResult.Success -> { val publicKey = card.getSingleWallet()?.publicKey if (publicKey == null) { - callback(CompletionResult.Success(ScanNoteResponse(card, null))) + callback(CompletionResult.Success(ScanResponse(card, ProductType.Twin, null))) return@run } val verified = TwinCardsManager.verifyTwinPublicKey( @@ -96,46 +129,62 @@ private class ScanTwinProcessor : ProductCommandProcessor { if (verified) { val twinPublicKey = readDataResult.data.issuerData.sliceArray(0 until 65) callback(CompletionResult.Success( - ScanNoteResponse( - card = card, - walletData = session.environment.walletData, - secondTwinPublicKey = twinPublicKey.toHexString()) + ScanResponse( + card, + ProductType.Twin, + session.environment.walletData, + twinPublicKey.toHexString()) )) return@run } else { - callback(CompletionResult.Success(ScanNoteResponse(card, null))) + callback(CompletionResult.Success(ScanResponse(card, ProductType.Twin, null))) } } is CompletionResult.Failure -> - callback(CompletionResult.Success(ScanNoteResponse(card, null))) + callback(CompletionResult.Success(ScanResponse(card, ProductType.Twin, null))) } } } } -private class ScanOtherCardsProcessor : ProductCommandProcessor { - override fun proceed(card: Card, session: CardSession, callback: (result: CompletionResult) -> Unit) { +private class ScanOtherCardsProcessor : ProductCommandProcessor { + + override fun proceed(card: Card, session: CardSession, callback: (result: CompletionResult) -> Unit) { val walletData = session.environment.walletData - if (card.wallets.isEmpty()) { - callback(CompletionResult.Success(ScanNoteResponse(card, walletData))) + + if (card.wallets.isEmpty() || card.firmwareVersion < FirmwareVersion.MultiWalletAvailable) { + callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, walletData))) return } - val curvesPresent = card.wallets.map { it.curve } - val curvesToCreate = card.supportedCurves.subtract(curvesPresent) - + val curvesToCreate = card.getCurvesForNonCreatedWallets() if (curvesToCreate.isEmpty()) { - callback(CompletionResult.Success(ScanNoteResponse(card, walletData))) + callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, walletData))) return } - CreateWalletsTask().run(session) { result -> + CreateProductWalletsTask(curvesToCreate).run(session) { result -> when (result) { - is CompletionResult.Success -> - callback(CompletionResult.Success(ScanNoteResponse(result.data, walletData))) + is CompletionResult.Success -> { + PreflightReadTask(PreflightReadMode.FullCardRead, card.cardId).run(session) { readResult -> + when (readResult) { + is CompletionResult.Success -> { + val response = ScanResponse(readResult.data, ProductType.Other, walletData) + callback(CompletionResult.Success(response)) + } + is CompletionResult.Failure -> callback(CompletionResult.Failure(readResult.error)) + } + } + } is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) } } } +} + +fun Card.getCurvesForNonCreatedWallets(): List { + val curvesPresent = wallets.map { it.curve } + val curvesForNonCreatedWallets = supportedCurves.subtract(curvesPresent) + return curvesForNonCreatedWallets.toList() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt index aeef1b9481..c035973ac7 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt @@ -6,16 +6,16 @@ import com.tangem.common.core.CardSession import com.tangem.common.core.CardSessionRunnable import com.tangem.operations.PreflightReadMode import com.tangem.operations.PreflightReadTask -import com.tangem.tap.domain.tasks.ScanNoteResponse -import com.tangem.tap.domain.tasks.ScanNoteTask +import com.tangem.tap.domain.tasks.product.ScanProductTask +import com.tangem.tap.domain.tasks.product.ScanResponse class FinalizeTwinTask( private val twinPublicKey: ByteArray, private val issuerKeys: KeyPair, -) : CardSessionRunnable { +) : CardSessionRunnable { override fun run( session: CardSession, - callback: (result: CompletionResult) -> Unit, + callback: (result: CompletionResult) -> Unit, ) { WriteProtectedIssuerDataTask(twinPublicKey, issuerKeys).run(session) { result -> when (result) { @@ -23,7 +23,7 @@ class FinalizeTwinTask( PreflightReadTask(PreflightReadMode.FullCardRead).run(session) { readResult -> when (readResult) { is CompletionResult.Success -> - ScanNoteTask(readResult.data).run(session, callback) + ScanProductTask(readResult.data).run(session, callback) is CompletionResult.Failure -> callback(CompletionResult.Failure(readResult.error)) } 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 ad753690fe..d879826f12 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 @@ -14,20 +14,20 @@ import com.tangem.common.extensions.toHexString import com.tangem.crypto.CryptoUtils import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler import com.tangem.tap.common.extensions.readAssetAsString -import com.tangem.tap.domain.tasks.ScanNoteResponse +import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.network.createMoshi import com.tangem.tap.tangemSdkManager -class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse, context: Context) { +class TwinCardsManager(private val scanResponse: ScanResponse, context: Context) { - private val currentCardId: String = scanNoteResponse.card.cardId + private val currentCardId: String = scanResponse.card.cardId private val secondCardId: String? = TwinsHelper.getTwinsCardId(currentCardId) private var currentCardPublicKey: String? = null private var secondCardPublicKey: String? = null private val issuerKeyPair: KeyPair = getIssuerKeys( - context, scanNoteResponse.card.issuer.publicKey.toHexString() + context, scanResponse.card.issuer.publicKey.toHexString() ) suspend fun createFirstWallet(message: Message): SimpleResult { @@ -44,7 +44,7 @@ class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse, context: FirebaseAnalyticsHandler.logCardSdkError( error, FirebaseAnalyticsHandler.ActionToLog.CreateWallet, - card = scanNoteResponse.card + card = scanResponse.card ) } return SimpleResult.failure(response.error) @@ -77,7 +77,7 @@ class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse, context: FirebaseAnalyticsHandler.logCardSdkError( error, FirebaseAnalyticsHandler.ActionToLog.CreateWallet, - card = scanNoteResponse.card + card = scanResponse.card ) } return SimpleResult.failure(response.error) @@ -86,7 +86,7 @@ class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse, context: } - suspend fun complete(message: Message): Result { + suspend fun complete(message: Message): Result { val response = tangemSdkManager.runTaskAsync( FinalizeTwinTask(secondCardPublicKey!!.hexToBytes(), issuerKeyPair), currentCardId, message @@ -98,7 +98,7 @@ class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse, context: FirebaseAnalyticsHandler.logCardSdkError( error, FirebaseAnalyticsHandler.ActionToLog.WriteIssuerData, - card = scanNoteResponse.card + card = scanResponse.card ) } Result.failure(response.error) diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt index 4d3882a2a7..74b37f90f4 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt @@ -7,7 +7,7 @@ import com.tangem.common.card.Card import com.tangem.operations.pins.CheckUserCodesResponse import com.tangem.tap.common.redux.NotificationAction import com.tangem.tap.common.redux.global.FiatCurrencyName -import com.tangem.tap.domain.tasks.ScanNoteResponse +import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.domain.termsOfUse.CardTou import com.tangem.tap.domain.twins.TwinCardNumber import com.tangem.tap.features.details.redux.twins.CreateTwinWallet @@ -18,13 +18,13 @@ import org.rekotlin.Action sealed class DetailsAction : Action { data class PrepareScreen( - val card: Card, - val scanNoteResponse: ScanNoteResponse, - val wallets: List, - val isCreatingTwinWalletAllowed: Boolean?, - val cardTou: CardTou, - val fiatCurrencyName: FiatCurrencyName, - val fiatCurrencies: List? = null, + val card: Card, + val scanResponse: ScanResponse, + val wallets: List, + val isCreatingTwinWalletAllowed: Boolean?, + val cardTou: CardTou, + val fiatCurrencyName: FiatCurrencyName, + val fiatCurrencies: List? = null, ) : DetailsAction() object ShowDisclaimer : DetailsAction() @@ -81,7 +81,7 @@ sealed class DetailsAction : Action { } data class LaunchThirdStep(val message: Message) : CreateTwinWalletAction() { - data class Success(val scanNoteResponse: ScanNoteResponse) : CreateTwinWalletAction() + data class Success(val scanResponse: ScanResponse) : CreateTwinWalletAction() object Failure : CreateTwinWalletAction() } } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt index 61b94447a1..04bcd51f32 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt @@ -48,7 +48,7 @@ private fun internalReduce(action: Action, state: AppState): DetailsState { private fun handlePrepareScreen(action: DetailsAction.PrepareScreen, state: DetailsState): DetailsState { val twinsState = if (action.card.isTangemTwin()) { CreateTwinWalletState( - scanResponse = action.scanNoteResponse, + scanResponse = action.scanResponse, twinCardNumber = TwinsHelper.getTwinCardNumber(action.card.cardId), createTwinWallet = null, showAlert = false, diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt index fa81ea9a18..1a6c05c2b1 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt @@ -48,7 +48,7 @@ class CreateTwinWalletMiddleware { store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) } is DetailsAction.CreateTwinWalletAction.LaunchFirstStep -> { - store.state.globalState.scanNoteResponse?.let { + store.state.globalState.scanResponse?.let { twinsManager = TwinCardsManager(it, action.context) } scope.launch { @@ -106,7 +106,7 @@ class CreateTwinWalletMiddleware { } is DetailsAction.CreateTwinWalletAction.LaunchThirdStep.Success -> { scope.launch { - store.state.globalState.tapWalletManager.onCardScanned(action.scanNoteResponse) + store.state.globalState.tapWalletManager.onCardScanned(action.scanResponse) } store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletState.kt index 488ac431bc..22c96dd0bf 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletState.kt @@ -1,15 +1,15 @@ package com.tangem.tap.features.details.redux.twins -import com.tangem.tap.domain.tasks.ScanNoteResponse +import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.domain.twins.TwinCardNumber data class CreateTwinWalletState( - val scanResponse: ScanNoteResponse?, - val step: CreateTwinWalletStep = CreateTwinWalletStep.FirstStep, - val twinCardNumber: TwinCardNumber?, - val createTwinWallet: CreateTwinWallet?, - val showAlert: Boolean, - val allowRecreatingWallet: Boolean? = null + val scanResponse: ScanResponse?, + val step: CreateTwinWalletStep = CreateTwinWalletStep.FirstStep, + val twinCardNumber: TwinCardNumber?, + val createTwinWallet: CreateTwinWallet?, + val showAlert: Boolean, + val allowRecreatingWallet: Boolean? = null ) enum class CreateTwinWalletStep { FirstStep, SecondStep, ThirdStep } 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 813dba5494..d316ca59ec 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 @@ -133,7 +133,7 @@ class WalletConnectMiddleware { private fun handleScanCard(wcUri: String) { scope.launch { - val result = tangemSdkManager.scanNote( + val result = tangemSdkManager.scanProduct( FirebaseAnalyticsHandler, R.string.wallet_connect_scan_card_message ) @@ -174,7 +174,7 @@ class WalletConnectMiddleware { private fun getWalletManager(card: Card): WalletManager? { val factory = store.state.globalState.tapWalletManager.walletManagerFactory - return if (store.state.globalState.scanNoteResponse?.card?.cardId == card.cardId) { + return if (store.state.globalState.scanResponse?.card?.cardId == card.cardId) { store.state.walletState.getWalletManager(Blockchain.Ethereum) ?: factory.makeWalletManagerForApp(card, Blockchain.Ethereum) ?.also { walletManager -> 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 03cdd9c2c2..3e8ccb008b 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 @@ -3,6 +3,7 @@ package com.tangem.tap.features.home.redux import com.tangem.tap.common.entities.IndeterminateProgressButton import com.tangem.tap.common.extensions.dispatchErrorNotification import com.tangem.tap.common.extensions.dispatchOpenUrl +import com.tangem.tap.common.extensions.withMainContext import com.tangem.tap.common.post import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction @@ -10,7 +11,7 @@ import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.DELAY_SDK_DIALOG_CLOSE import com.tangem.tap.domain.TapWalletManager -import com.tangem.tap.domain.tasks.ScanNoteResponse +import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.features.onboarding.service.OnboardingHelper import com.tangem.tap.features.send.redux.states.ButtonState import com.tangem.tap.features.wallet.redux.ProgressState @@ -56,11 +57,11 @@ private fun handleReadCard() { store.dispatch(HomeAction.ChangeScanCardButtonState(btnState)) } - fun proceedToWalletScreen(tapWalletManager: TapWalletManager, scanResponse: ScanNoteResponse) { - changeButtonState(ButtonState.ENABLED) + fun proceedToWalletScreen(tapWalletManager: TapWalletManager, scanResponse: ScanResponse) { scope.launch { tapWalletManager.onCardScanned(scanResponse) delay(DELAY_SDK_DIALOG_CLOSE) + withMainContext { changeButtonState(ButtonState.ENABLED) } store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt index 182f63d7bb..7712bbc3da 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt @@ -19,6 +19,7 @@ import com.tangem.tap.store import com.tangem.wallet.R import kotlinx.android.synthetic.main.layout_onboarding_bottom_action_views.* import kotlinx.android.synthetic.main.layout_onboarding_note.* +import kotlinx.android.synthetic.main.view_onboarding_progress.* import kotlinx.android.synthetic.main.view_onboarding_tv_balance.* /** @@ -50,6 +51,7 @@ class OnboardingNoteFragment : BaseOnboardingFragment() { oldState.onboardingNoteState == newState.onboardingNoteState }.select { it.onboardingNoteState } } + storeSubscribersList.add(this) } override fun newState(state: OnboardingNoteState) { @@ -147,7 +149,7 @@ class OnboardingNoteFragment : BaseOnboardingFragment() { tv_body.setText(R.string.onboarding_done_body) imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_rectangle_rounded_8)) - updateConstraints(R.layout.lp_onboarding_done) + updateConstraints(R.layout.lp_onboarding_done_activation) } private fun updateConstraints(@LayoutRes layoutId: Int) { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt index 2eab43bc1b..83ea98d0d0 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt @@ -54,7 +54,7 @@ private fun handleNoteAction(action: Action) { } is OnboardingNoteAction.CreateWallet -> { scope.launch { - val result = tangemSdkManager.createProductWallet(service.scanResponse.card.cardId) + val result = tangemSdkManager.createProductWallet(service.scanResponse) withMainContext { when (result) { is CompletionResult.Success -> { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/OnboardingWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/OnboardingWalletFragment.kt index c947f32ab9..d0463040bd 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/OnboardingWalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/OnboardingWalletFragment.kt @@ -32,6 +32,7 @@ class OnboardingWalletFragment : BaseStoreFragment(R.layout.fragment_onboarding_ oldState.onboardingWalletState == newState.onboardingWalletState }.select { it.onboardingWalletState } } + storeSubscribersList.add(this) } override fun newState(state: OnboardingWalletState) { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingHelper.kt b/app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingHelper.kt index 7cf88da9c5..8f7eb6f726 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingHelper.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingHelper.kt @@ -2,9 +2,9 @@ package com.tangem.tap.features.onboarding.service import com.tangem.blockchain.common.WalletManagerFactory import com.tangem.tap.common.redux.navigation.AppScreen -import com.tangem.tap.domain.TapWorkarounds.isTangemNote +import com.tangem.tap.domain.ProductType import com.tangem.tap.domain.extensions.hasWallets -import com.tangem.tap.domain.tasks.ScanNoteResponse +import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.preferencesStorage /** @@ -13,20 +13,20 @@ import com.tangem.tap.preferencesStorage class OnboardingHelper { companion object { - fun isOnboardingCase(response: ScanNoteResponse): Boolean = if (response.card.hasWallets()) + fun isOnboardingCase(response: ScanResponse): Boolean = if (response.card.hasWallets()) preferencesStorage.usedCardsPrefStorage.activationIsStarted(response.card.cardId) else true fun createService( fromScreen: AppScreen, - scanResponse: ScanNoteResponse, + scanResponse: ScanResponse, walletManagerFactory: WalletManagerFactory, ): ProductOnboardingService? { - val card = scanResponse.card val cardInfoStorage = preferencesStorage.usedCardsPrefStorage - return when { - card.isTangemNote() -> OnboardingNoteService(fromScreen, scanResponse, cardInfoStorage, - walletManagerFactory) - else -> null + return when(scanResponse.productType) { + ProductType.Note -> OnboardingNoteService(fromScreen, scanResponse, cardInfoStorage, walletManagerFactory) + ProductType.Wallet -> null + ProductType.Twin -> null + ProductType.Other -> OnboardingOtherCardsService(fromScreen, scanResponse, cardInfoStorage, walletManagerFactory) } } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingNoteService.kt b/app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingNoteService.kt index 4404a8e35b..feaa76eb5c 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingNoteService.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingNoteService.kt @@ -2,7 +2,7 @@ package com.tangem.tap.features.onboarding.service import com.tangem.blockchain.common.WalletManagerFactory import com.tangem.tap.common.redux.navigation.AppScreen -import com.tangem.tap.domain.tasks.ScanNoteResponse +import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.persistence.UsedCardsPrefStorage /** @@ -10,7 +10,7 @@ import com.tangem.tap.persistence.UsedCardsPrefStorage */ class OnboardingNoteService( override val fromScreen: AppScreen, - override var scanResponse: ScanNoteResponse, + override var scanResponse: ScanResponse, cardInfoStorage: UsedCardsPrefStorage, walletManagerFactory: WalletManagerFactory, ) : ProductOnboardingService(cardInfoStorage, walletManagerFactory) { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/service/ProductOnboardingService.kt b/app/src/main/java/com/tangem/tap/features/onboarding/service/ProductOnboardingService.kt index cf9276dd5a..6faff4073e 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/service/ProductOnboardingService.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/service/ProductOnboardingService.kt @@ -16,7 +16,7 @@ import com.tangem.tap.domain.TapError import com.tangem.tap.domain.UrlBitmapLoader import com.tangem.tap.domain.extensions.getArtworkUrl import com.tangem.tap.domain.extensions.makePrimaryWalletManager -import com.tangem.tap.domain.tasks.ScanNoteResponse +import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.domain.topup.TradeCryptoHelper import com.tangem.tap.features.wallet.redux.AddressData import com.tangem.tap.features.wallet.redux.Artwork @@ -37,7 +37,7 @@ import kotlin.coroutines.suspendCoroutine */ interface OnboardingService { val fromScreen: AppScreen - val scanResponse: ScanNoteResponse + val scanResponse: ScanResponse // val cardInfoStorage: UsedCardsPrefStorage var walletManager: WalletManager? @@ -141,7 +141,7 @@ abstract class ProductOnboardingService( val walletManager = walletManager ?: walletManagerFactory.makePrimaryWalletManager(scanResponse).guard { val customError = TapError.CustomError("Loading cancelled. Cause: wallet manager didn't created") - loadedBalance = OnboardingWalletBalance.error(customError) + withMainContext { loadedBalance = OnboardingWalletBalance.error(customError) } return loadedBalance } @@ -178,7 +178,6 @@ abstract class ProductOnboardingService( override fun activationStarted() { cardInfoStorage.activationStarted(scanResponse.card.cardId) - } override fun activationFinished() { 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 c6fb811fba..26d2f8dd27 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 @@ -21,9 +21,9 @@ import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.extensions.minimalAmount import com.tangem.tap.features.send.redux.* import com.tangem.tap.features.send.redux.FeeAction.RequestFee +import com.tangem.tap.features.send.redux.states.ButtonState import com.tangem.tap.features.send.redux.states.ExternalTransactionData import com.tangem.tap.features.send.redux.states.MainCurrencyType -import com.tangem.tap.features.send.redux.states.ButtonState import com.tangem.tap.features.send.redux.states.TransactionExtrasState import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.scope @@ -77,7 +77,7 @@ private fun verifyAndSendTransaction( ) { val sendState = appState?.sendState ?: return val walletManager = sendState.walletManager ?: return - val card = appState.globalState.scanNoteResponse?.card ?: return + val card = appState.globalState.scanResponse?.card ?: return val destinationAddress = sendState.addressPayIdState.destinationWalletAddress ?: return val typedAmount = sendState.amountState.amountToExtract ?: return val feeAmount = sendState.feeState.currentFee ?: return 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 f1bcb72e51..e8afbe2d62 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 @@ -14,7 +14,7 @@ class TokensMiddleware { { action -> when (action) { is TokensAction.LoadCurrencies -> { - val card = state()?.globalState?.scanNoteResponse?.card + val card = state()?.globalState?.scanResponse?.card val isTestcard = card?.isTestCard ?: false val tokens = currenciesRepository.getPopularTokens(isTestcard) val blockchains = currenciesRepository.getBlockchains( 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 b5c15c945c..026cc26e4d 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 @@ -8,7 +8,6 @@ import com.tangem.tap.common.redux.global.GlobalState import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.currenciesRepository -import com.tangem.tap.domain.TapWorkarounds.isTestCard import com.tangem.tap.domain.extensions.makeWalletManagerForApp import com.tangem.tap.domain.extensions.makeWalletManagersForApp import com.tangem.tap.features.wallet.redux.Currency @@ -36,7 +35,7 @@ class MultiWalletMiddleware { } } is WalletAction.MultiWallet.AddToken -> { - globalState?.scanNoteResponse?.card?.cardId?.let { + globalState?.scanResponse?.card?.cardId?.let { currenciesRepository.saveAddedToken(it, action.token) } addToken(action.token, walletState, globalState) @@ -45,7 +44,7 @@ class MultiWalletMiddleware { addTokens(action.tokens, walletState, globalState) } is WalletAction.MultiWallet.AddBlockchain -> { - globalState?.scanNoteResponse?.card?.let { card -> + globalState?.scanResponse?.card?.let { card -> currenciesRepository.saveAddedBlockchain(card.cardId, action.blockchain) if (walletState?.blockchains?.contains(action.blockchain) != true) { globalState.tapWalletManager.walletManagerFactory @@ -58,11 +57,11 @@ class MultiWalletMiddleware { store.dispatch(WalletAction.LoadWallet(blockchain = action.blockchain)) } is WalletAction.MultiWallet.SaveCurrencies -> { - val cardId = globalState?.scanNoteResponse?.card?.cardId + val cardId = globalState?.scanResponse?.card?.cardId cardId?.let { currenciesRepository.saveCardCurrencies(it, action.cardCurrencies) } } is WalletAction.MultiWallet.RemoveWallet -> { - val cardId = globalState?.scanNoteResponse?.card?.cardId + val cardId = globalState?.scanResponse?.card?.cardId when (val currency = action.walletData.currency) { is Currency.Blockchain -> { cardId?.let { @@ -80,7 +79,7 @@ class MultiWalletMiddleware { } } is WalletAction.MultiWallet.FindBlockchainsInUse -> { - val cardFirmware = globalState?.scanNoteResponse?.card?.firmwareVersion + val cardFirmware = globalState?.scanResponse?.card?.firmwareVersion val blockchains = currenciesRepository.getBlockchains(cardFirmware) .filterNot { walletState?.blockchains?.contains(it) == true } val walletManagers = @@ -117,7 +116,7 @@ class MultiWalletMiddleware { } } is WalletAction.MultiWallet.FindTokensInUse -> { - val card = globalState?.scanNoteResponse?.card ?: return + val card = globalState?.scanResponse?.card ?: return val walletManager = walletState?.getWalletManager(Blockchain.Ethereum) ?: globalState.tapWalletManager.walletManagerFactory.makeWalletManagerForApp( card = card, @@ -156,7 +155,7 @@ class MultiWalletMiddleware { } private fun addToken(token: Token, walletState: WalletState?, globalState: GlobalState?) { - val card = globalState?.scanNoteResponse?.card ?: return + val card = globalState?.scanResponse?.card ?: return val walletManager = walletState?.getWalletManager(token) ?: globalState.tapWalletManager.walletManagerFactory.makeWalletManagerForApp( card = card, @@ -186,7 +185,7 @@ class MultiWalletMiddleware { walletState: WalletState?, globalState: GlobalState?, ) { - val card = globalState?.scanNoteResponse?.card ?: return + val card = globalState?.scanResponse?.card ?: return val tokensWithManagers = tokens.map { token -> val walletManager = walletState?.getWalletManager(token) ?: globalState.tapWalletManager.walletManagerFactory.makeWalletManagerForApp( 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 8c0b638587..ea654a6777 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 @@ -96,7 +96,7 @@ class WalletMiddleware { is WalletAction.CreateWallet -> { if (walletState?.twinCardsState != null) { store.dispatch(DetailsAction.CreateTwinWalletAction.ShowWarning( - globalState?.scanNoteResponse?.card?.cardId?.let { + globalState?.scanResponse?.card?.cardId?.let { TwinsHelper.getTwinCardNumber(it) }, CreateTwinWallet.CreateWallet @@ -104,12 +104,12 @@ class WalletMiddleware { } else { scope.launch { val result = tangemSdkManager.createWallet( - globalState?.scanNoteResponse?.card?.cardId + globalState?.scanResponse?.card?.cardId ) when (result) { is CompletionResult.Success -> { val scanNoteResponse = - globalState?.scanNoteResponse?.copy(card = result.data) + globalState?.scanResponse?.copy(card = result.data) scanNoteResponse?.let { globalState.tapWalletManager.onCardScanned(scanNoteResponse) } @@ -146,7 +146,7 @@ class WalletMiddleware { } is WalletAction.Scan -> { scope.launch { - val result = tangemSdkManager.scanNote(FirebaseAnalyticsHandler) + val result = tangemSdkManager.scanProduct(FirebaseAnalyticsHandler) scope.launch(Dispatchers.Main) { store.dispatch(GlobalAction.ScanFailsCounter.ChooseBehavior(result)) when (result) { @@ -186,7 +186,7 @@ class WalletMiddleware { } is WalletAction.LoadData -> { scope.launch { - val scanNoteResponse = globalState?.scanNoteResponse + val scanNoteResponse = globalState?.scanResponse ?: return@launch if (!walletState?.wallets.isNullOrEmpty()) { globalState.tapWalletManager.reloadData(scanNoteResponse) @@ -196,7 +196,7 @@ class WalletMiddleware { } } is NetworkStateChanged -> { - globalState?.scanNoteResponse?.let { scanNoteResponse -> + globalState?.scanResponse?.let { scanNoteResponse -> store.dispatch(WalletAction.Warnings.CheckHashesCount.CheckHashesCountOnline) scope.launch { globalState.tapWalletManager.loadData(scanNoteResponse) 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 d69b213ece..39f84c1b09 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 @@ -40,7 +40,7 @@ class WarningsMiddleware { } is WalletAction.Warnings.CheckHashesCount.CheckHashesCountOnline -> checkHashesCountOnline() is WalletAction.Warnings.CheckHashesCount.SaveCardId -> { - val cardId = globalState?.scanNoteResponse?.card?.cardId + val cardId = globalState?.scanResponse?.card?.cardId cardId?.let { preferencesStorage.usedCardsPrefStorage.scanned(it) } } is WalletAction.Warnings.AppRating.RemindLater -> { @@ -81,7 +81,7 @@ class WarningsMiddleware { } private fun showCardWarningsIfNeeded(globalState: GlobalState?) { - globalState?.scanNoteResponse?.card?.let { card -> + globalState?.scanResponse?.card?.let { card -> globalState.warningManager?.removeWarnings(WarningMessage.Origin.Local) if (card.isTestCard) { addWarningMessage(WarningMessagesManager.testCardWarning(), autoUpdate = true) @@ -149,7 +149,7 @@ class WarningsMiddleware { if (store.state.walletState.hashesCountVerified != false) return if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) return - val card = store.state.globalState.scanNoteResponse?.card + val card = store.state.globalState.scanResponse?.card if (card == null || preferencesStorage.usedCardsPrefStorage.wasScanned(card.cardId)) return if (card.isTangemTwin() || card.isMultiwalletAllowed) return diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt index f5f61139b1..e8f2a250fd 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt @@ -156,7 +156,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber { store.dispatch(GlobalAction.UpdateFeedbackInfo(store.state.walletState.walletManagers)) - store.state.globalState.scanNoteResponse?.let { scanNoteResponse -> + store.state.globalState.scanResponse?.let { scanNoteResponse -> store.dispatch(DetailsAction.PrepareScreen( scanNoteResponse.card, scanNoteResponse, store.state.walletState.walletManagers.map { it.wallet }, From 4de7334b8926a940ec091a599f5b36072d74e343 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 13 Oct 2021 18:55:38 +0300 Subject: [PATCH 063/189] Updated on 2026-08-14 --- .../main/java/com/tangem/tap/features/send/BaseStoreFragment.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt b/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt index 75a2a5ad80..bb3046db5e 100644 --- a/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt @@ -51,6 +51,7 @@ abstract class BaseStoreFragment(layoutId: Int) : Fragment(layoutId) { override fun onStop() { storeSubscribersList.forEach { store.unsubscribe(it) } + storeSubscribersList.clear() super.onStop() } From ccb8afe104fea8e5a3b647797b840be5cb98d625 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 13 Oct 2021 18:57:11 +0300 Subject: [PATCH 064/189] Updated on 2026-08-14 --- .../res/layout/layout_onboarding_note.xml | 5 +- .../layout/lp_onboarding_create_wallet.xml | 5 +- .../main/res/layout/lp_onboarding_done.xml | 23 +++--- .../layout/lp_onboarding_done_activation.xml | 82 +++++++++++++++++++ .../res/layout/lp_onboarding_topup_wallet.xml | 5 +- .../res/layout/view_onboarding_progress.xml | 10 +++ 6 files changed, 110 insertions(+), 20 deletions(-) create mode 100644 app/src/main/res/layout/lp_onboarding_done_activation.xml create mode 100644 app/src/main/res/layout/view_onboarding_progress.xml diff --git a/app/src/main/res/layout/layout_onboarding_note.xml b/app/src/main/res/layout/layout_onboarding_note.xml index 9804063722..e2cb262a46 100644 --- a/app/src/main/res/layout/layout_onboarding_note.xml +++ b/app/src/main/res/layout/layout_onboarding_note.xml @@ -6,9 +6,8 @@ android:layout_height="match_parent" android:visibility="visible"> - - - + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/pb_state" /> \ No newline at end of file diff --git a/app/src/main/res/layout/lp_onboarding_done_activation.xml b/app/src/main/res/layout/lp_onboarding_done_activation.xml new file mode 100644 index 0000000000..a3e9722587 --- /dev/null +++ b/app/src/main/res/layout/lp_onboarding_done_activation.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/lp_onboarding_topup_wallet.xml b/app/src/main/res/layout/lp_onboarding_topup_wallet.xml index ab70711896..2783b41196 100644 --- a/app/src/main/res/layout/lp_onboarding_topup_wallet.xml +++ b/app/src/main/res/layout/lp_onboarding_topup_wallet.xml @@ -6,9 +6,8 @@ android:layout_height="match_parent" android:visibility="visible"> - + From fe42b821db1999558a8c858399c9352634219011 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 13 Oct 2021 18:57:29 +0300 Subject: [PATCH 065/189] Updated on 2026-08-14 --- .../OnboardingOtherCardsFragment.kt | 93 +++++++++++++++---- .../redux/OnboardingOtherCardsAction.kt | 22 +++++ .../redux/OnboardingOtherCardsMiddleware.kt | 81 ++++++++++++++++ .../redux/OnboardingOtherCardsReducer.kt | 45 +++++++++ .../redux/OnboardingOtherCardsState.kt | 25 +++++ .../service/OnboardingOtherCardsService.kt | 39 ++++++++ 6 files changed, 287 insertions(+), 18 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsAction.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsReducer.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsState.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingOtherCardsService.kt diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt index b0de1e9949..99ce6f4280 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt @@ -2,40 +2,97 @@ package com.tangem.tap.features.onboarding.products.otherCards import android.os.Bundle import android.view.View -import androidx.transition.TransitionInflater -import com.tangem.tap.features.send.BaseStoreFragment +import android.view.animation.OvershootInterpolator +import androidx.annotation.LayoutRes +import androidx.constraintlayout.widget.ConstraintSet +import androidx.core.view.isVisible +import androidx.transition.TransitionManager +import com.tangem.tap.common.extensions.getDrawableCompat +import com.tangem.tap.common.transitions.InternalNoteLayoutTransition +import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment +import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsAction +import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsState +import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsStep +import com.tangem.tap.store import com.tangem.wallet.R -import org.rekotlin.StoreSubscriber +import kotlinx.android.synthetic.main.layout_onboarding_bottom_action_views.* +import kotlinx.android.synthetic.main.layout_onboarding_note.* +import kotlinx.android.synthetic.main.view_onboarding_progress.* /** [REDACTED_AUTHOR] */ -class OnboardingOtherCardsFragment : BaseStoreFragment(R.layout.fragment_onboarding_main), StoreSubscriber { +class OnboardingOtherCardsFragment : BaseOnboardingFragment() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - val inflater = TransitionInflater.from(requireContext()) - enterTransition = inflater.inflateTransition(R.transition.fade) - exitTransition = inflater.inflateTransition(R.transition.fade) - } + override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_note override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + + store.dispatch(OnboardingOtherCardsAction.DetermineStepOfScreen) } override fun subscribeToStore() { -// store.subscribe(this) { state -> -// state.skipRepeats { oldState, newState -> -// oldState.onboardingOtherState == newState.onboardingOtherState -// }.select { it.onboardingOtherState } -// } + store.subscribe(this) { state -> + state.skipRepeats { oldState, newState -> + oldState.onboardingOtherCardsState == newState.onboardingOtherCardsState + }.select { it.onboardingOtherCardsState } + } + storeSubscribersList.add(this) } override fun newState(state: OnboardingOtherCardsState) { if (activity == null) return + if (state.currentStep == OnboardingOtherCardsStep.None) return + state.artworkBitmap?.let { imv_front_card.setImageBitmap(it) } + pb_state.max = state.steps.size - 1 + pb_state.progress = state.progress + + when (state.currentStep) { + OnboardingOtherCardsStep.CreateWallet -> setupCreateWalletState(state) + OnboardingOtherCardsStep.Done -> setupDoneState(state) + } + showConfetti(state.showConfetti) } -} -class OnboardingOtherCardsState {} \ No newline at end of file + private fun setupCreateWalletState(state: OnboardingOtherCardsState) { + btn_main_action.setText(R.string.onboarding_create_wallet_button_create_wallet) + btn_main_action.setOnClickListener { store.dispatch(OnboardingOtherCardsAction.CreateWallet) } + btn_alternative_action.setText(R.string.onboarding_button_what_does_it_mean) + btn_alternative_action.setOnClickListener { } + + tv_header.setText(R.string.onboarding_create_wallet_header) + tv_body.setText(R.string.onboarding_create_wallet_body) + + imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_circle)) + updateConstraints(R.layout.lp_onboarding_create_wallet) + } + + private fun setupDoneState(state: OnboardingOtherCardsState) { + btn_main_action.setText(R.string.onboarding_done_button_continue) + btn_main_action.setOnClickListener { + showConfetti(false) + store.dispatch(OnboardingOtherCardsAction.Done) + } + + btn_alternative_action.isVisible = false + btn_alternative_action.setText("") + btn_alternative_action.setOnClickListener { } + + tv_header.setText(R.string.onboarding_done_header) + tv_body.setText(R.string.onboarding_done_body) + + imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_rectangle_rounded_8)) + updateConstraints(R.layout.lp_onboarding_done) + } + + private fun updateConstraints(@LayoutRes layoutId: Int) { + val constraintSet = ConstraintSet() + constraintSet.clone(requireContext(), layoutId) + constraintSet.applyTo(onboarding_main_container) + val transition = InternalNoteLayoutTransition() + transition.interpolator = OvershootInterpolator() + TransitionManager.beginDelayedTransition(onboarding_main_container, transition) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsAction.kt new file mode 100644 index 0000000000..effdecff73 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsAction.kt @@ -0,0 +1,22 @@ +package com.tangem.tap.features.onboarding.products.otherCards.redux + +import org.rekotlin.Action + +/** +[REDACTED_AUTHOR] + */ +sealed class OnboardingOtherCardsAction : Action { + // from user, ui + object CreateWallet : OnboardingOtherCardsAction() + + // from redux + object DetermineStepOfScreen : OnboardingOtherCardsAction() + object Done : OnboardingOtherCardsAction() + + data class SetStepOfScreen(val step: OnboardingOtherCardsStep) : OnboardingOtherCardsAction() + + sealed class Confetti { + object Show : OnboardingOtherCardsAction() + object Hide : OnboardingOtherCardsAction() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt new file mode 100644 index 0000000000..d781c5b1cb --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt @@ -0,0 +1,81 @@ +package com.tangem.tap.features.onboarding.products.otherCards.redux + +import com.tangem.common.CompletionResult +import com.tangem.tap.common.extensions.withMainContext +import com.tangem.tap.common.postUi +import com.tangem.tap.common.redux.AppState +import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.common.redux.navigation.NavigationAction +import com.tangem.tap.domain.DELAY_SDK_DIALOG_CLOSE +import com.tangem.tap.scope +import com.tangem.tap.store +import com.tangem.tap.tangemSdkManager +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import org.rekotlin.Action +import org.rekotlin.Middleware + +class OnboardingOtherCardsMiddleware { + companion object { + val handler = onboardingOtherCardsMiddleware + } +} + +private val onboardingOtherCardsMiddleware: Middleware = { dispatch, state -> + { next -> + { action -> + handleOtherCardsAction(action) + next(action) + } + } +} + +private fun handleOtherCardsAction(action: Action) { + if (action !is OnboardingOtherCardsAction) return + val service = store.state.onboardingOtherCardsState.onboardingService ?: return + val noteState = store.state.onboardingOtherCardsState + + when (action) { + is OnboardingOtherCardsAction.DetermineStepOfScreen -> { + val step = when { + service.walletManager == null -> OnboardingOtherCardsStep.CreateWallet + else -> OnboardingOtherCardsStep.Done + } + store.dispatch((OnboardingOtherCardsAction.SetStepOfScreen(step))) + } + is OnboardingOtherCardsAction.SetStepOfScreen -> { + if (action.step == OnboardingOtherCardsStep.Done) { + service.activationFinished() + postUi(500) { store.dispatch(OnboardingOtherCardsAction.Confetti.Show) } + } + } + is OnboardingOtherCardsAction.CreateWallet -> { + scope.launch { + val result = tangemSdkManager.createProductWallet(service.scanResponse) + withMainContext { + when (result) { + is CompletionResult.Success -> { + val updatedResponse = service.scanResponse.copy(card = result.data) + service.scanResponse = updatedResponse + service.activationStarted() + + delay(DELAY_SDK_DIALOG_CLOSE) + store.dispatch(OnboardingOtherCardsAction.SetStepOfScreen(OnboardingOtherCardsStep.Done)) + } + is CompletionResult.Failure -> { +// do nothing + } + } + } + } + } + OnboardingOtherCardsAction.Done -> { + store.dispatch(GlobalAction.Onboarding.Deactivate) + scope.launch { + store.state.globalState.tapWalletManager.onCardScanned(service.scanResponse) + store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsReducer.kt new file mode 100644 index 0000000000..316f97f636 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsReducer.kt @@ -0,0 +1,45 @@ +package com.tangem.tap.features.onboarding.products.otherCards.redux + +import com.tangem.tap.common.redux.AppState +import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.features.onboarding.service.OnboardingOtherCardsService +import org.rekotlin.Action + +class OnboardingOtherCardsReducer { + companion object { + fun reduce(action: Action, state: AppState): OnboardingOtherCardsState = internalReduce(action, state) + } +} + +private fun internalReduce(action: Action, appState: AppState): OnboardingOtherCardsState { + var state = appState.onboardingOtherCardsState + + when (action) { + is GlobalAction.Onboarding.Activate -> { + if (action.onboardingService is OnboardingOtherCardsService) { + val service = action.onboardingService + state = state.copy( + onboardingService = service, + artworkBitmap = service.getArtwork().value?.artwork, + showConfetti = false + ) + } + } + GlobalAction.Onboarding.Deactivate -> { + state = OnboardingOtherCardsState() + } + is OnboardingOtherCardsAction.SetStepOfScreen -> { + if (action.step != state.currentStep && state.steps.contains(action.step)) { + state = state.copy(currentStep = action.step) + } + } + is OnboardingOtherCardsAction.Confetti.Show -> { + state = state.copy(showConfetti = true) + } + is OnboardingOtherCardsAction.Confetti.Hide -> { + state = state.copy(showConfetti = false) + } + } + + return state +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsState.kt new file mode 100644 index 0000000000..bcf232bd85 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsState.kt @@ -0,0 +1,25 @@ +package com.tangem.tap.features.onboarding.products.otherCards.redux + +import android.graphics.Bitmap +import com.tangem.tap.features.onboarding.service.OnboardingOtherCardsService +import org.rekotlin.StateType + +/** +[REDACTED_AUTHOR] + */ +data class OnboardingOtherCardsState( + val onboardingService: OnboardingOtherCardsService? = null, + // UI + val showConfetti: Boolean = false, + val artworkBitmap: Bitmap? = null, + val currentStep: OnboardingOtherCardsStep = OnboardingOtherCardsStep.None, + val steps: List = OnboardingOtherCardsStep.values().toList(), +) : StateType { + + val progress: Int + get() = steps.indexOf(currentStep) +} + +enum class OnboardingOtherCardsStep { + None, CreateWallet, Done +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingOtherCardsService.kt b/app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingOtherCardsService.kt new file mode 100644 index 0000000000..5e81842312 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingOtherCardsService.kt @@ -0,0 +1,39 @@ +package com.tangem.tap.features.onboarding.service + +import com.tangem.blockchain.common.WalletManagerFactory +import com.tangem.tap.common.extensions.withMainContext +import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.domain.TapError +import com.tangem.tap.domain.tasks.product.ScanResponse +import com.tangem.tap.features.wallet.redux.AddressData +import com.tangem.tap.persistence.UsedCardsPrefStorage + +/** +[REDACTED_AUTHOR] + */ +class OnboardingOtherCardsService( + override val fromScreen: AppScreen, + override var scanResponse: ScanResponse, + cardInfoStorage: UsedCardsPrefStorage, + walletManagerFactory: WalletManagerFactory, +) : ProductOnboardingService(cardInfoStorage, walletManagerFactory) { + + override fun navigateToScreen(): AppScreen { + return AppScreen.OnboardingOther + } + + // Return any value and any state. The balance of the card is not important in this service + override suspend fun updateBalance(): OnboardingWalletBalance { + val customError = TapError.CustomError("Loading cancelled. Cause: wallet manager didn't created") + withMainContext { loadedBalance = OnboardingWalletBalance.error(customError) } + return loadedBalance + } + + override fun getToUpUrl(): String? { + throw UnsupportedOperationException() + } + + override fun getAddressData(): AddressData? { + throw UnsupportedOperationException() + } +} \ No newline at end of file From 013f80ef6390c5273e33a4d9c58f127d64d9ad77 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 13 Oct 2021 22:57:44 +0300 Subject: [PATCH 066/189] Updated on 2026-08-14 --- .../com/tangem/tap/domain/TapWalletManager.kt | 7 +++-- .../domain/tasks/product/ScanProductTask.kt | 15 +++------ .../tangem/tap/domain/twins/TwinsHelper.kt | 31 ++++++++++++------- .../features/details/redux/DetailsReducer.kt | 4 +-- 4 files changed, 29 insertions(+), 28 deletions(-) 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 8a592c14ad..76a9bc46f1 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -16,7 +16,8 @@ import com.tangem.tap.domain.configurable.config.ConfigManager import com.tangem.tap.domain.extensions.* import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.domain.tokens.CardCurrencies -import com.tangem.tap.domain.twins.TwinsHelper +import com.tangem.tap.domain.twins.getTwinCardNumber +import com.tangem.tap.domain.twins.getTwinsCardId import com.tangem.tap.domain.twins.isTangemTwin import com.tangem.tap.features.tokens.redux.TokensAction import com.tangem.tap.features.wallet.redux.Currency @@ -90,8 +91,8 @@ class TapWalletManager { store.dispatch(WalletAction.SetIfTestnetCard(data.card.isTestCard)) store.dispatch(WalletAction.MultiWallet.SetIsMultiwalletAllowed(data.card.isMultiwalletAllowed)) if (data.card.isTangemTwin()) { - val secondCardId = TwinsHelper.getTwinsCardId(data.card.cardId) - val cardNumber = TwinsHelper.getTwinCardNumber(data.card.cardId) + val secondCardId = data.card.getTwinsCardId() + val cardNumber = data.card.getTwinCardNumber() if (secondCardId != null && cardNumber != null) { store.dispatch(WalletAction.TwinsAction.SetTwinCard( secondCardId, cardNumber, isCreatingTwinCardsAllowed = true 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 3493d1c322..cdb63d456b 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 @@ -123,19 +123,12 @@ private class ScanTwinProcessor : ProductCommandProcessor { callback(CompletionResult.Success(ScanResponse(card, ProductType.Twin, null))) return@run } - val verified = TwinCardsManager.verifyTwinPublicKey( - readDataResult.data.issuerData, publicKey - ) + val verified = TwinCardsManager.verifyTwinPublicKey(readDataResult.data.issuerData, publicKey) if (verified) { val twinPublicKey = readDataResult.data.issuerData.sliceArray(0 until 65) - callback(CompletionResult.Success( - ScanResponse( - card, - ProductType.Twin, - session.environment.walletData, - twinPublicKey.toHexString()) - )) - return@run + val walletData = session.environment.walletData + val response = ScanResponse(card, ProductType.Twin, walletData, twinPublicKey.toHexString()) + callback(CompletionResult.Success(response)) } else { callback(CompletionResult.Success(ScanResponse(card, ProductType.Twin, null))) } diff --git a/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt b/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt index f8e5de93fe..8c66c05bfa 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt @@ -2,7 +2,6 @@ package com.tangem.tap.domain.twins import com.tangem.common.card.Card import com.tangem.tap.common.extensions.isEven -import com.tangem.tap.domain.twins.TwinsHelper.Companion.getTwinCardNumber class TwinsHelper { companion object { @@ -53,16 +52,16 @@ class TwinsHelper { private fun String.calculateLuhn(): Int { val checksum = this.reversed() - .mapIndexed { index, c -> - val digit = if (c in '0'..'9') c - '0' else c - 'A' - if (!index.isEven()) { - digit - } else { - val newDigit = digit * 2 - if (newDigit >= 10) newDigit - 9 else newDigit - } - }.sum() - .rem(10) + .mapIndexed { index, c -> + val digit = if (c in '0'..'9') c - '0' else c - 'A' + if (!index.isEven()) { + digit + } else { + val newDigit = digit * 2 + if (newDigit >= 10) newDigit - 9 else newDigit + } + }.sum() + .rem(10) return (10 - checksum) % 10 } @@ -76,7 +75,15 @@ enum class TwinCardNumber(val number: Int) { } fun Card.isTangemTwin(): Boolean { - return getTwinCardNumber(cardId) != null + return TwinsHelper.getTwinCardNumber(cardId) != null +} + +fun Card.getTwinCardNumber(): TwinCardNumber? { + return TwinsHelper.getTwinCardNumber(this.cardId) +} + +fun Card.getTwinsCardId(): String? { + return TwinsHelper.getTwinsCardId(this.cardId) } fun Card.getTwinCardIdForUser(): String { diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt index 04bcd51f32..4e45d765d7 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt @@ -7,7 +7,7 @@ import com.tangem.tap.domain.TapWorkarounds.isStart2Coin import com.tangem.tap.domain.extensions.isWalletDataSupported import com.tangem.tap.domain.extensions.signedHashesCount import com.tangem.tap.domain.extensions.toSendableAmounts -import com.tangem.tap.domain.twins.TwinsHelper +import com.tangem.tap.domain.twins.getTwinCardNumber import com.tangem.tap.domain.twins.isTangemTwin import com.tangem.tap.features.details.redux.twins.CreateTwinWalletReducer import com.tangem.tap.features.details.redux.twins.CreateTwinWalletState @@ -49,7 +49,7 @@ private fun handlePrepareScreen(action: DetailsAction.PrepareScreen, state: Deta val twinsState = if (action.card.isTangemTwin()) { CreateTwinWalletState( scanResponse = action.scanResponse, - twinCardNumber = TwinsHelper.getTwinCardNumber(action.card.cardId), + twinCardNumber = action.card.getTwinCardNumber(), createTwinWallet = null, showAlert = false, allowRecreatingWallet = action.isCreatingTwinWalletAllowed From 72e282956c5db89b1c64cc01372c838e698f16e6 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 13 Oct 2021 22:58:02 +0300 Subject: [PATCH 067/189] Updated on 2026-08-14 --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 69493ced41..2d9e53e7e6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,8 +77,8 @@ dependencies { coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' implementation 'com.tangem:blockchain:develop-42' - implementation 'com.tangem.tangem-sdk-kotlin:core:develop-80' - implementation 'com.tangem.tangem-sdk-kotlin:android:develop-80' + implementation 'com.tangem.tangem-sdk-kotlin:core:develop-88' + implementation 'com.tangem.tangem-sdk-kotlin:android:develop-88' // WebView implementation "androidx.browser:browser:1.3.0" From b327109ca8613a624412d35191d60db6649b40b1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 14 Oct 2021 22:11:44 +0300 Subject: [PATCH 068/189] Updated on 2026-08-14 --- .../tap/common/leapfrogWidget/Animations.kt | 86 ++++----- .../tap/common/leapfrogWidget/LeapView.kt | 173 +++++++----------- .../common/leapfrogWidget/LeapfrogWidget.kt | 117 +++++++----- 3 files changed, 175 insertions(+), 201 deletions(-) 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 index 77c9f1fcbb..a95488d44f 100644 --- a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/Animations.kt +++ b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/Animations.kt @@ -32,14 +32,14 @@ fun createProgressListener(duration: Long, listener: ProgressListener?): Animato return valueAnimator } -fun View.unfoldAnimation(animDuration: Long, properties: AnimationProperties): AnimatorSet { - val translate = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslationStart, properties.yTranslationEnd) +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: AnimationProperties): AnimatorSet { - val translate = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslationStart, properties.yTranslationEnd) +fun View.foldAnimation(animDuration: Long, properties: LeapViewProperties): AnimatorSet { + val translate = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation) translate.duration = animDuration return AnimatorSet().apply { @@ -47,17 +47,17 @@ fun View.foldAnimation(animDuration: Long, properties: AnimationProperties): Ani } } -fun View.leapAnimation(animDuration: Long, properties: AnimationProperties, overLift: Float): AnimatorSet { +fun View.leapAnimation(animDuration: Long, properties: LeapViewProperties, overLift: Float): AnimatorSet { val halfDuration = animDuration / 2 - val upTo = (height.toFloat() + properties.yTranslationStart + overLift) * -1 - val translateUp = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslationStart, upTo) + 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.scaleStart, properties.scaleEnd) - val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scaleStart, properties.scaleEnd) + 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 @@ -65,37 +65,28 @@ fun View.leapAnimation(animDuration: Long, properties: AnimationProperties, over playTogether(scaleX, scaleY) } - val elevation = ValueAnimator.ofFloat(properties.elevationStart, properties.elevationEnd) - elevation.addUpdateListener { this.elevation = (it.animatedValue as? Float) ?: properties.elevationStart } - elevation.startDelay = halfDuration - elevation.duration = 100 - - val translateDown = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, upTo, properties.yTranslationEnd) + 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 - ) + playTogether(translateUp, scale, elevation, translateDown) } } -fun View.leapBackAnimation(animDuration: Long, properties: AnimationProperties, calculator: PropertyCalculator): AnimatorSet { +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, properties.yTranslationStart, upTo) + 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.scaleStart, properties.scaleEnd) - val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scaleStart, properties.scaleEnd) + 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 @@ -103,34 +94,24 @@ fun View.leapBackAnimation(animDuration: Long, properties: AnimationProperties, playTogether(scaleX, scaleY) } - val elevation = ValueAnimator.ofFloat(properties.elevationStart, properties.elevationEnd) - elevation.addUpdateListener { this.elevation = (it.animatedValue as? Float) ?: properties.elevationStart } - elevation.startDelay = halfDuration - elevation.duration = 100 - - val translateDown = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, upTo, properties.yTranslationEnd) + 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, - ) + playTogether(translateUp, scale, elevation, translateDown) } } -fun View.pullUpAnimation(leapDuration: Long, properties: AnimationProperties): AnimatorSet { +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.yTranslationStart, properties.yTranslationEnd) - val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X, properties.scaleStart, properties.scaleEnd) - val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scaleStart, properties.scaleEnd) - val elevation = ValueAnimator.ofFloat(properties.elevationStart, properties.elevationEnd) - elevation.addUpdateListener { this.elevation = (it.animatedValue as? Float) ?: properties.elevationStart } + 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 @@ -140,15 +121,14 @@ fun View.pullUpAnimation(leapDuration: Long, properties: AnimationProperties): A } } -fun View.pullDownAnimation(leapDuration: Long, properties: AnimationProperties): AnimatorSet { +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.yTranslationStart, properties.yTranslationEnd) - val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X, properties.scaleStart, properties.scaleEnd) - val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scaleStart, properties.scaleEnd) - val elevation = ValueAnimator.ofFloat(properties.elevationStart, properties.elevationEnd) - elevation.addUpdateListener { this.elevation = (it.animatedValue as? Float) ?: properties.elevationStart } + 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 @@ -156,4 +136,12 @@ fun View.pullDownAnimation(leapDuration: Long, properties: AnimationProperties): 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 index 0c4d37a066..be2d63b664 100644 --- a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapView.kt +++ b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapView.kt @@ -12,35 +12,33 @@ class LeapView( private val maximalPosition: Int, private val calculator: PropertyCalculator, ) { - var currentProperties: AnimationProperties + var state: LeapViewState private set - var currentPosition: Int = position - private set - var previousPosition: Int = position - private set + val initialState: LeapViewState init { val initialProperties = createInitialProperty(position) - currentProperties = AnimationProperties.from(initialProperties, initialProperties) - initView(initialProperties) + state = LeapViewState(index, position, position, initialProperties) + initialState = state.copy() } - private fun initView(properties: Properties) { - view.elevation = properties.elevation - view.translationY = properties.yTranslation - view.scaleX = properties.scale - view.scaleY = properties.scale -// initialProperties.hasForeground + fun initView() { + applyState(initialState) } fun leap(): LeapFrogAnimation { - previousPosition = currentPosition - currentPosition = when (previousPosition) { + val previousPosition = state.currentPosition + val currentPosition = when (previousPosition) { 0 -> maximalPosition - else -> currentPosition - 1 + else -> state.currentPosition - 1 } - updateProperties(createLeapAnimationProperty(previousPosition, currentPosition)) + + state = state.copy( + currentPosition = currentPosition, + previousPosition = previousPosition, + properties = createLeapAnimationProperty(previousPosition, currentPosition) + ) return when { previousPosition == 0 && currentPosition == maximalPosition -> LeapFrogAnimation.LEAP @@ -49,12 +47,16 @@ class LeapView( } fun leapBack(): LeapFrogAnimation { - previousPosition = currentPosition - currentPosition = when (previousPosition) { + val previousPosition = state.currentPosition + val currentPosition = when (previousPosition) { maximalPosition -> 0 - else -> currentPosition + 1 + else -> state.currentPosition + 1 } - updateProperties(createLeapAnimationProperty(previousPosition, currentPosition)) + state = state.copy( + currentPosition = currentPosition, + previousPosition = previousPosition, + properties = createLeapAnimationProperty(previousPosition, currentPosition) + ) return when { previousPosition == maximalPosition && currentPosition == 0 -> LeapFrogAnimation.LEAP @@ -63,66 +65,60 @@ class LeapView( } fun fold() { - updateProperties(currentProperties.toFold()) + state = state.copy(properties = state.properties.toFold() + ) } fun unfold() { - updateProperties(currentProperties.toUnfold(calculator)) - } - - fun getState(): LeapViewState { - return LeapViewState(index, currentPosition, previousPosition, currentProperties) + state = state.copy(properties = state.properties.toUnfold(calculator)) } fun applyState(state: LeapViewState) { - previousPosition = state.previousPosition - currentPosition = state.currentPosition - updateProperties(state.properties) - initView(state.properties.endProperties()) + this.state = state + setProperties(this.state.properties) } - private fun updateProperties(properties: AnimationProperties) { - currentProperties = 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): Properties { - return Properties( - endPosition, - calculator.scale(endPosition), - calculator.elevation(endPosition, maximalPosition + 1), - calculator.yTranslation(0), - calculator.hasForeground(endPosition), + 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): AnimationProperties { - return AnimationProperties( - startPosition, - endPosition, - calculator.scale(startPosition), - calculator.scale(endPosition), - calculator.elevation(startPosition, maximalPosition + 1), - calculator.elevation(endPosition, maximalPosition + 1), - calculator.yTranslation(startPosition), - calculator.yTranslation(endPosition), - calculator.hasForeground(startPosition), - 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 AnimationProperties.toUnfold(calculator: PropertyCalculator): AnimationProperties { - val start = this.yTranslationEnd - val end = calculator.yTranslation(this.positionEnd) - return this.copy(yTranslationStart = start, yTranslationEnd = end) + private fun LeapViewProperties.toUnfold(calculator: PropertyCalculator): LeapViewProperties { + return this.copy(yTranslation = calculator.yTranslation(this.positionEnd)) } - private fun AnimationProperties.toFold(): AnimationProperties { - val start = this.yTranslationEnd - val end = 0f - return this.copy(yTranslationStart = start, yTranslationEnd = end) + private fun LeapViewProperties.toFold(): LeapViewProperties { + return this.copy(yTranslation = 0f) } - override fun toString(): String = "index: $index, position: $currentPosition, code: ${view.hashCode()}" + override fun toString(): String = "index: $index, position: ${state.currentPosition}, code: ${view.hashCode()}" } @@ -130,62 +126,19 @@ data class LeapViewState( val index: Int, val currentPosition: Int, val previousPosition: Int, - val properties: AnimationProperties + val properties: LeapViewProperties ) enum class LeapFrogAnimation { LEAP, PULL } -data class Properties( - val position: Int, - val scale: Float, - val elevation: Float, - val yTranslation: Float, - val hasForeground: Boolean, -) - -data class AnimationProperties( +data class LeapViewProperties( val positionStart: Int, val positionEnd: Int, - val scaleStart: Float, - val scaleEnd: Float, + val scale: Float, val elevationStart: Float, val elevationEnd: Float, - val yTranslationStart: Float, - val yTranslationEnd: Float, - val hasForegroundStart: Boolean, - val hasForegroundEnd: Boolean, -) { - - fun endProperties(): Properties { - return Properties( - positionEnd, - scaleEnd, - elevationEnd, - yTranslationEnd, - hasForegroundEnd, - ) - } - - override fun toString(): String { - return """ - scaleStart: $scaleStart, scaleEnd: $scaleEnd, - elevationStart: $elevationStart, elevationEnd: $elevationEnd, - yTranslationStart: $yTranslationStart, yTranslationEnd: $yTranslationEnd, - hasForegroundStart : $hasForegroundStart, hasForegroundEnd: $hasForegroundEnd, - """.trimIndent() - } - - companion object { - fun from(start: Properties, end: Properties): AnimationProperties { - return AnimationProperties( - start.position, end.position, - start.scale, end.scale, - start.elevation, end.elevation, - start.yTranslation, end.yTranslation, - start.hasForeground, end.hasForeground - ) - } - } -} \ No newline at end of file + 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 index 37b04d4f2a..11c0b11e63 100644 --- a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapfrogWidget.kt +++ b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapfrogWidget.kt @@ -2,9 +2,9 @@ package com.tangem.tap.common.leapfrogWidget import android.animation.Animator import android.animation.AnimatorSet -import android.view.View import android.view.animation.AccelerateDecelerateInterpolator import android.widget.FrameLayout +import androidx.core.animation.doOnEnd import androidx.core.view.children import com.tangem.tangem_sdk_new.extensions.dpToPx @@ -25,60 +25,87 @@ class LeapfrogWidget( private var leapProgress: Int = 100 private var leapBackProgress: Int = 100 - private var isFolded: Boolean = true + var isFolded: Boolean = true + private set init { calculator.setTranslationConverter { parentContainer.dpToPx(it) } - init() - } - - private fun init() { - if (!viewIsFullFledged()) return - - val maxPosition = parentContainer.childCount - 1 - parentContainer.children.forEachIndexed { index, view -> - val initialPosition = maxPosition - index - leapViews.add(LeapView(view, index, initialPosition, maxPosition, calculator)) + if (viewIsFullFledged()) { + val maxPosition = parentContainer.childCount - 1 + parentContainer.children.forEachIndexed { index, view -> + val initialPosition = maxPosition - index + leapViews.add(LeapView(view, index, initialPosition, maxPosition, calculator)) + } } } - fun fold(listener: ProgressListener? = null) { - if (!canFoldUnfold() || isFolded) return - - createFoldUnfoldAnimation(true, foldAnimationDuration, listener) + /** + * Apply initial properties to the views + */ + fun initViews() { + isFolded = true + leapViews.forEach { it.initView() } } - fun unfold(listener: ProgressListener? = null) { - if (!canFoldUnfold() || !isFolded) return - - createFoldUnfoldAnimation(false, foldAnimationDuration, listener) + fun fold(onEnd: () -> Unit = {}) { + val animator = foldAnimator() + if (animator == null) { + onEnd() + } else { + animator.doOnEnd { onEnd() } + animator.start() + } } - private fun createFoldUnfoldAnimation(isFoldAnimation: Boolean, duration: Long, listener: ProgressListener?) { + fun unfold(onEnd: () -> Unit = {}) { + val animator = unfoldAnimator() + if (animator == null) { + onEnd() + } else { + animator.doOnEnd { onEnd() } + animator.start() + } + } + + fun foldAnimator(): Animator? { + if (!canFoldUnfold() || isFolded) return null + + return createFoldUnfoldAnimator(true, foldAnimationDuration) + } + + fun unfoldAnimator(): Animator? { + if (!canFoldUnfold() || !isFolded) return null + + return createFoldUnfoldAnimator(false, foldAnimationDuration) + } + + private fun createFoldUnfoldAnimator(isFoldAnimation: Boolean, duration: Long): Animator { val animatorsList = mutableListOf() leapViews.forEach { if (isFoldAnimation) { it.fold() - animatorsList.add(it.view.foldAnimation(duration, it.currentProperties)) + animatorsList.add(it.view.foldAnimation(duration, it.state.properties)) } else { it.unfold() - animatorsList.add(it.view.unfoldAnimation(duration, it.currentProperties)) + animatorsList.add(it.view.unfoldAnimation(duration, it.state.properties)) } } animatorsList.add(createProgressListener(duration) { foldUnfoldProgress = it if (it == 100) isFolded = isFoldAnimation }) - animatorsList.add(createProgressListener(duration, listener)) val animator = AnimatorSet() animator.interpolator = AccelerateDecelerateInterpolator() animator.playTogether(animatorsList.toList()) - animator.start() + return animator } - fun leap(listener: ProgressListener? = null) { - if (!canLeap()) return + fun leap(onEnd: () -> Unit = {}) { + if (!canLeap()) { + onEnd() + return + } val duration = leapAnimationDuration val animatorsList = mutableListOf() @@ -86,48 +113,54 @@ class LeapfrogWidget( when (it.leap()) { LeapFrogAnimation.LEAP -> { val overLift = calculator.overLift(leapViews.size) - animatorsList.add(it.view.leapAnimation(duration, it.currentProperties, overLift)) + animatorsList.add(it.view.leapAnimation(duration, it.state.properties, overLift)) } LeapFrogAnimation.PULL -> { - animatorsList.add(it.view.pullUpAnimation(duration, it.currentProperties)) + animatorsList.add(it.view.pullUpAnimation(duration, it.state.properties)) } } } animatorsList.add(createProgressListener(duration) { leapProgress = it }) - animatorsList.add(createProgressListener(duration, listener)) val animator = AnimatorSet() animator.playTogether(animatorsList.toList()) + animator.doOnEnd { onEnd() } animator.start() } - fun leapBack(listener: ProgressListener? = null) { - if (!canLeapBack()) return + fun leapBack(onEnd: () -> Unit = {}) { + if (!canLeapBack()) { + onEnd() + return + } val duration = leapBackAnimationDuration val animatorsList = mutableListOf() leapViews.forEach { when (it.leapBack()) { LeapFrogAnimation.LEAP -> { - animatorsList.add(it.view.leapBackAnimation(duration, it.currentProperties, calculator)) + animatorsList.add(it.view.leapBackAnimation(duration, it.state.properties, calculator)) } LeapFrogAnimation.PULL -> { - animatorsList.add(it.view.pullDownAnimation(duration, it.currentProperties)) + animatorsList.add(it.view.pullDownAnimation(duration, it.state.properties)) } } } animatorsList.add(createProgressListener(duration) { leapBackProgress = it }) - animatorsList.add(createProgressListener(duration, listener)) + val animator = AnimatorSet() animator.playTogether(animatorsList.toList()) + animator.doOnEnd { onEnd() } animator.start() } - fun getViewByPosition(position: Int): View? { - return leapViews.firstOrNull { it.currentPosition == position }?.view - } + fun getViewsCount(): Int = parentContainer.childCount - fun getState(): LeapfrogWidgetState = LeapfrogWidgetState(isFolded, leapViews.map { it.getState() }) + 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(isFolded, leapViews.map { it.state }) fun applyState(state: LeapfrogWidgetState) { isFolded = state.isFolded @@ -179,11 +212,11 @@ data class LeapfrogWidgetState( class PropertyCalculator( val elevationFactor: Float = 1f, - val decreaseScaleFactor: Float = 0.1f, - val yTranslationFactor: Float = 20f, + val decreaseScaleFactor: Float = 0.2f, + val yTranslationFactor: Float = -40f, val decreaseOverLiftingFactor: Float = 0.85f, ) { - // 1st view it is view on the top of stack, but it is last in parent.children + // 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) { From ea82db240b8d6d0d63b53b1a7e5155057c34dff7 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 14 Oct 2021 22:12:02 +0300 Subject: [PATCH 069/189] Updated on 2026-08-14 --- .../tap/domain/twins/TwinsCardWidget.kt | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 app/src/main/java/com/tangem/tap/domain/twins/TwinsCardWidget.kt diff --git a/app/src/main/java/com/tangem/tap/domain/twins/TwinsCardWidget.kt b/app/src/main/java/com/tangem/tap/domain/twins/TwinsCardWidget.kt new file mode 100644 index 0000000000..63f891eb4d --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/twins/TwinsCardWidget.kt @@ -0,0 +1,152 @@ +package com.tangem.tap.domain.twins + +import android.animation.AnimatorSet +import android.animation.ObjectAnimator +import android.animation.PropertyValuesHolder +import android.view.View +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 + +/** +[REDACTED_AUTHOR] + */ +class TwinsCardWidget( + val leapfrogWidget: LeapfrogWidget, + val getTopOfAnchorViewForActivateState: () -> Float +) { + + init { + if (leapfrogWidget.getViewsCount() != 2) throw UnsupportedOperationException() + } + + fun toWelcome(animate: Boolean = true, onEnd: () -> Unit = {}) { + val animator = createAnimator(animate, onEnd) + animator.playTogether( + createAnimator(TwinCardNumber.First, createWelcomeProperties(TwinCardNumber.First)), + createAnimator(TwinCardNumber.Second, createWelcomeProperties(TwinCardNumber.Second)) + ) + leapfrogWidget.fold { animator.start() } + } + + fun toLeapfrog(animate: Boolean = true, onEnd: () -> Unit = {}) { + val animator = createAnimator(animate, onEnd) + animator.playTogether( + createAnimator(TwinCardNumber.First, createLeapfrogProperties(TwinCardNumber.First)), + createAnimator(TwinCardNumber.Second, createLeapfrogProperties(TwinCardNumber.Second)) + ) + leapfrogWidget.fold { + animator.doOnEnd { + leapfrogWidget.initViews() + leapfrogWidget.unfold() + } + animator.start() + } + } + + fun toActivate(animate: Boolean = true, onEnd: () -> Unit = {}) { + val animator = createAnimator(animate, onEnd) + animator.playTogether( + createAnimator(TwinCardNumber.First, createActivateProperties(TwinCardNumber.First)), + createAnimator(TwinCardNumber.Second, createActivateProperties(TwinCardNumber.Second)) + ) + animator.start() + } + + private fun createAnimator(animate: Boolean, onEnd: () -> Unit): AnimatorSet { + return AnimatorSet().apply { + duration = if (animate) 400 else 0 + doOnEnd { onEnd() } + } + } + + private fun createAnimator(cardNumber: TwinCardNumber, properties: TwinsCardProperties): ObjectAnimator { + val view = getLeapViewByCardNumber(cardNumber).view + val animator = ObjectAnimator.ofPropertyValuesHolder(view, *properties.createValuesHolders().toTypedArray()) + view.elevation = properties.elevation + return animator + } + + private fun createWelcomeProperties(cardNumber: TwinCardNumber): TwinsCardProperties { + return when (cardNumber) { + TwinCardNumber.First -> { + TwinsCardProperties( + xTranslation = -180f, + yTranslation = -350f, + rotation = -3f, + elevation = 1f, + scale = 1.1f, + ) + } + TwinCardNumber.Second -> { + TwinsCardProperties( + xTranslation = 180f, + yTranslation = 10f, + rotation = -3f, + elevation = 0f, + scale = 1.1f, + ) + } + } + } + + private fun createLeapfrogProperties(cardNumber: TwinCardNumber): TwinsCardProperties { + val twinProperties = TwinsCardProperties.from(getLeapViewByCardNumber(cardNumber).state) + return twinProperties.copy( + xTranslation = 0f, + yTranslation = 0f, + rotation = 0f, + scale = 1f, + ) + } + + private fun createActivateProperties(cardNumber: TwinCardNumber): TwinsCardProperties { + val topOfAnchorView = getTopOfAnchorViewForActivateState() + val twinProperties = TwinsCardProperties.from(getLeapViewByCardNumber(cardNumber).state) + return twinProperties.copy( + xTranslation = twinProperties.xTranslation, + yTranslation = twinProperties.yTranslation - topOfAnchorView, + rotation = 0f, + scale = twinProperties.scale - 0.4f, + ) + } + + private fun getLeapViewByCardNumber(cardNumber: TwinCardNumber): LeapView { + return when (cardNumber) { + TwinCardNumber.First -> leapfrogWidget.getViewByPosition(0) + TwinCardNumber.Second -> leapfrogWidget.getViewByPosition(1) + } + } +} + +private data class TwinsCardProperties( + val xTranslation: Float = 0f, + val yTranslation: Float = 0f, + val rotation: Float = 0f, + val elevation: Float = 0f, + val scale: Float = 1.1f, +) { + + fun createValuesHolders(): List { + return listOf( + PropertyValuesHolder.ofFloat(View.TRANSLATION_X, xTranslation), + PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, yTranslation), + PropertyValuesHolder.ofFloat(View.ROTATION, rotation), + PropertyValuesHolder.ofFloat(View.SCALE_X, scale), + PropertyValuesHolder.ofFloat(View.SCALE_Y, scale), + ) + } + + companion object { + fun from(leapViewState: LeapViewState): TwinsCardProperties { + val leapViewProperties = leapViewState.properties + + return TwinsCardProperties( + yTranslation = leapViewProperties.yTranslation, + elevation = leapViewProperties.elevationEnd, + scale = leapViewProperties.scale, + ) + } + } +} \ No newline at end of file From b40dc0a04a7d367f8f1fa20aec55d21eaf92a973 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 14 Oct 2021 22:18:38 +0300 Subject: [PATCH 070/189] Updated on 2026-08-14 --- .../com/tangem/tap/common/leapfrogWidget/LeapfrogWidget.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 index 11c0b11e63..884cef3f94 100644 --- a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapfrogWidget.kt +++ b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapfrogWidget.kt @@ -212,8 +212,8 @@ data class LeapfrogWidgetState( class PropertyCalculator( val elevationFactor: Float = 1f, - val decreaseScaleFactor: Float = 0.2f, - val yTranslationFactor: Float = -40f, + val decreaseScaleFactor: Float = 0.1f, + val yTranslationFactor: Float = 20f, val decreaseOverLiftingFactor: Float = 0.85f, ) { // 0 view it is view on the top of stack, but it is last in parent.children From 11823643255408948be255196fe7774b1fff167e Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 14 Oct 2021 22:19:23 +0300 Subject: [PATCH 071/189] Updated on 2026-08-14 --- .../leapfrogWidget/TestLeapfrogFragment.kt | 68 +++++++++ .../res/layout/test_leapfrog_fragment.xml | 133 ++++++++++++++++++ ...og_widget.xml => test_leapfrog_widget.xml} | 11 +- 3 files changed, 205 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/common/leapfrogWidget/TestLeapfrogFragment.kt create mode 100644 app/src/main/res/layout/test_leapfrog_fragment.xml rename app/src/main/res/layout/{leapfrog_widget.xml => test_leapfrog_widget.xml} (69%) 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 new file mode 100644 index 0000000000..1a41ff35be --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/TestLeapfrogFragment.kt @@ -0,0 +1,68 @@ +package com.tangem.tap.common.leapfrogWidget + +import android.os.Bundle +import android.view.View +import android.widget.FrameLayout +import androidx.fragment.app.Fragment +import androidx.transition.TransitionInflater +import com.tangem.tap.domain.twins.TwinsCardWidget +import com.tangem.wallet.R +import kotlinx.android.synthetic.main.test_leapfrog_fragment.* + +class TestLeapfrogFragment : Fragment(R.layout.test_leapfrog_fragment) { + + private lateinit var twinsCardWidget: TwinsCardWidget + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val inflater = TransitionInflater.from(requireContext()) + exitTransition = inflater.inflateTransition(R.transition.fade) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val leapfrogContainer: FrameLayout = view.findViewById(R.id.leapfrog_views_container) + val leapfrog = LeapfrogWidget(leapfrogContainer) + twinsCardWidget = TwinsCardWidget(leapfrog) { 200f } + + btn_twin_welcome.setOnClickListener { + twinsCardWidget.toWelcome() + } + btn_twin_to_leapfrog.setOnClickListener { + twinsCardWidget.toLeapfrog() + } + btn_twin_activate.setOnClickListener { + twinsCardWidget.toActivate() + } + + + btn_lp_init.setOnClickListener { + leapfrog.initViews() + } + btn_lp_unfold.setOnClickListener { + leapfrog.unfold() + } + btn_lp_fold.setOnClickListener { + leapfrog.fold() + } + btn_lp_leap.setOnClickListener { + leapfrog.leap() + } + btn_lp_leap_back.setOnClickListener { + leapfrog.leapBack() + } + } + + override fun onStart() { + super.onStart() + leapfrogWidgetState?.let { twinsCardWidget.leapfrogWidget.applyState(it) } + } + + override fun onStop() { + super.onStop() + leapfrogWidgetState = twinsCardWidget.leapfrogWidget.getState() + } +} + +private var leapfrogWidgetState: LeapfrogWidgetState? = null \ No newline at end of file diff --git a/app/src/main/res/layout/test_leapfrog_fragment.xml b/app/src/main/res/layout/test_leapfrog_fragment.xml new file mode 100644 index 0000000000..52b720ad27 --- /dev/null +++ b/app/src/main/res/layout/test_leapfrog_fragment.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/leapfrog_widget.xml b/app/src/main/res/layout/test_leapfrog_widget.xml similarity index 69% rename from app/src/main/res/layout/leapfrog_widget.xml rename to app/src/main/res/layout/test_leapfrog_widget.xml index 13a8e61d88..3fbf992180 100644 --- a/app/src/main/res/layout/leapfrog_widget.xml +++ b/app/src/main/res/layout/test_leapfrog_widget.xml @@ -11,16 +11,13 @@ + android:tint="@color/colorPrimary" + android:src="@drawable/card_placeholder" /> - - + android:tint="@color/accent" + android:src="@drawable/card_placeholder" /> \ No newline at end of file From 7e821a934c42d4e46ce91ac83888b39583a62db1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sun, 17 Oct 2021 22:40:55 +0300 Subject: [PATCH 072/189] Updated on 2026-08-14 --- .../main/java/com/tangem/tap/MainActivity.kt | 2 +- .../tap/common/extensions/Transition.kt | 24 ++ .../tap/common/extensions/WalletManager.kt | 27 ++ .../tap/common/redux/global/GlobalAction.kt | 8 +- .../common/redux/global/GlobalMidlleware.kt | 2 +- .../tap/common/redux/global/GlobalReducer.kt | 22 +- .../tap/common/redux/global/GlobalState.kt | 4 +- .../com/tangem/tap/domain/TapWalletManager.kt | 9 +- .../com/tangem/tap/domain/extensions/Card.kt | 33 ++ .../tap/domain/twins/TwinCardsManager.kt | 3 +- .../tangem/tap/domain/twins/TwinsHelper.kt | 36 --- .../tap/features/home/redux/HomeMiddleware.kt | 74 ++--- .../features/onboarding/OnboardingHelper.kt | 27 ++ .../features/onboarding/OnboardingManager.kt | 129 ++++++++ .../products/BaseOnboardingFragment.kt | 2 +- .../products/note/OnboardingNoteFragment.kt | 43 ++- .../note/redux/OnboardingNoteAction.kt | 7 +- .../note/redux/OnboardingNoteMiddleware.kt | 94 ++++-- .../note/redux/OnboardingNoteReducer.kt | 31 +- .../note/redux/OnboardingNoteState.kt | 22 +- .../OnboardingOtherCardsFragment.kt | 4 +- .../redux/OnboardingOtherCardsAction.kt | 3 + .../redux/OnboardingOtherCardsMiddleware.kt | 34 +- .../redux/OnboardingOtherCardsReducer.kt | 16 +- .../redux/OnboardingOtherCardsState.kt | 8 +- .../onboarding/service/OnboardingHelper.kt | 33 -- .../service/OnboardingNoteService.kt | 21 -- .../service/OnboardingOtherCardsService.kt | 39 --- .../service/ProductOnboardingService.kt | 291 ------------------ .../tap/features/wallet/redux/WalletAction.kt | 2 +- .../res/layout/layout_onboarding_note.xml | 2 +- .../layout/lp_onboarding_create_wallet.xml | 2 +- .../layout/lp_onboarding_done_activation.xml | 2 +- .../res/layout/lp_onboarding_topup_wallet.xml | 2 +- 34 files changed, 443 insertions(+), 615 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/common/extensions/Transition.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/OnboardingManager.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingHelper.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingNoteService.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingOtherCardsService.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/service/ProductOnboardingService.kt diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index dbef280f6f..41b89b88e3 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -75,7 +75,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler { val backStackIsEmpty = supportFragmentManager.backStackEntryCount == 0 val isScannedBefore = store.state.globalState.scanResponse != null - val isOnboardingServiceActive = store.state.globalState.onboardingService != null + val isOnboardingServiceActive = store.state.globalState.onboardingManager != null if (backStackIsEmpty || (!isOnboardingServiceActive && !isScannedBefore)) { store.dispatch(NavigationAction.NavigateTo(AppScreen.Home)) } diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Transition.kt b/app/src/main/java/com/tangem/tap/common/extensions/Transition.kt new file mode 100644 index 0000000000..69fbba97b6 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/extensions/Transition.kt @@ -0,0 +1,24 @@ +package com.tangem.tap.common.extensions + +import androidx.transition.Transition + +/** +[REDACTED_AUTHOR] + */ +inline fun Transition.addListener( + crossinline onStart: (animator: Transition) -> Unit = {}, + crossinline onEnd: (animator: Transition) -> Unit = {}, + crossinline onCancel: (animator: Transition) -> Unit = {}, + crossinline onPause: (animator: Transition) -> Unit = {}, + crossinline onRepeat: (animator: Transition) -> Unit = {} +): Transition.TransitionListener { + val listener = object : Transition.TransitionListener { + override fun onTransitionStart(transition: Transition) = onStart(transition) + override fun onTransitionEnd(transition: Transition) = onEnd(transition) + override fun onTransitionCancel(transition: Transition) = onCancel(transition) + override fun onTransitionPause(transition: Transition) = onPause(transition) + override fun onTransitionResume(transition: Transition) = onRepeat(transition) + } + addListener(listener) + return listener +} \ No newline at end of file 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 683f1574a3..8f7402ff1f 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 @@ -7,7 +7,11 @@ 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 +import com.tangem.tap.features.wallet.redux.reducers.createAddressesData import com.tangem.tap.network.NetworkConnectivity +import com.tangem.tap.store /** [REDACTED_AUTHOR] @@ -29,4 +33,27 @@ suspend fun WalletManager.safeUpdate(): Result = try { Result.Failure(TapError.WalletManagerUpdate.InternalError(message)) } } +} + +fun WalletManager?.getToUpUrl(): String? { + val wallet = this?.wallet ?: return null + val config = store.state.globalState.configManager?.config ?: return null + val defaultAddress = wallet.address + + return TradeCryptoHelper.getUrl( + TradeCryptoHelper.Action.Buy, + wallet.blockchain, + wallet.blockchain.currency, + defaultAddress, + config.moonPayApiKey, + config.moonPayApiSecretKey + ) +} + +fun WalletManager?.getAddressData(): AddressData? { + val wallet = this?.wallet ?: return null + + val addressDataList = wallet.createAddressesData() + return if (addressDataList.isEmpty()) null + else addressDataList[0] } \ No newline at end of file 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 50efb26994..b29536f6d3 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 @@ -4,6 +4,7 @@ import com.tangem.blockchain.common.WalletManager import com.tangem.common.CompletionResult import com.tangem.common.core.TangemError import com.tangem.tap.common.redux.* +import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.domain.TapError import com.tangem.tap.domain.configurable.config.ConfigManager import com.tangem.tap.domain.configurable.warningMessage.WarningMessage @@ -12,7 +13,6 @@ import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.features.details.redux.SecurityOption import com.tangem.tap.features.feedback.EmailData import com.tangem.tap.features.feedback.FeedbackManager -import com.tangem.tap.features.onboarding.service.ProductOnboardingService import com.tangem.tap.network.moonpay.MoonPayUserStatus import org.rekotlin.Action @@ -29,11 +29,11 @@ sealed class GlobalAction : Action { object HideDialog : GlobalAction() sealed class Onboarding { - data class Activate(val onboardingService: ProductOnboardingService) : GlobalAction() - object Deactivate : GlobalAction() + data class Start(val scanResponse: ScanResponse, val fromScreen: AppScreen) : GlobalAction() + object Stop : GlobalAction() } - data class ReadCard( + data class ScanCard( val onSuccess: ((ScanResponse) -> Unit)? = null, val onFailure: ((TangemError) -> Unit)? = null, val messageResId: Int? = null, 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 2f8cbfffc4..ea80ab463c 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 @@ -87,7 +87,7 @@ private val globalMiddlewareHandler: Middleware = { dispatch, appState } } } - is GlobalAction.ReadCard -> { + is GlobalAction.ScanCard -> { scope.launch { val result = tangemSdkManager.scanProduct(FirebaseAnalyticsHandler, action.messageResId) store.dispatch(GlobalAction.ScanFailsCounter.ChooseBehavior(result)) 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 30eabac6e4..0d1894f736 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 @@ -1,6 +1,8 @@ package com.tangem.tap.common.redux.global import com.tangem.tap.common.redux.AppState +import com.tangem.tap.features.onboarding.OnboardingManager +import com.tangem.tap.preferencesStorage import org.rekotlin.Action fun globalReducer(action: Action, state: AppState): GlobalState { @@ -10,11 +12,13 @@ fun globalReducer(action: Action, state: AppState): GlobalState { val globalState = state.globalState return when (action) { - is GlobalAction.Onboarding.Activate -> { - globalState.copy(onboardingService = action.onboardingService) + is GlobalAction.Onboarding.Start -> { + val usedCardsPrefStorage = preferencesStorage.usedCardsPrefStorage + val onboardingState = OnboardingManager(action.scanResponse, action.fromScreen, usedCardsPrefStorage) + globalState.copy(onboardingManager = onboardingState) } - is GlobalAction.Onboarding.Deactivate -> { - globalState.copy(onboardingService = null) + is GlobalAction.Onboarding.Stop -> { + globalState.copy(onboardingManager = null) } is GlobalAction.ScanFailsCounter.Increment -> { globalState.copy(scanCardFailsCounter = globalState.scanCardFailsCounter + 1) @@ -36,11 +40,11 @@ fun globalReducer(action: Action, state: AppState): GlobalState { is GlobalAction.SetWarningManager -> globalState.copy(warningManager = action.warningManager) is GlobalAction.UpdateWalletSignedHashes -> { val wallet = globalState.scanResponse?.card - ?.wallet(action.walletPublicKey) - ?.copy( - totalSignedHashes = action.walletSignedHashes, - remainingSignatures = action.remainingSignatures - ) + ?.wallet(action.walletPublicKey) + ?.copy( + totalSignedHashes = action.walletSignedHashes, + remainingSignatures = action.remainingSignatures + ) val card = globalState.scanResponse?.card wallet?.let { globalState.scanResponse.card.updateWallet(wallet) } 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 94eb578fc4..e27641b854 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 @@ -8,13 +8,14 @@ import com.tangem.tap.domain.configurable.config.ConfigManager import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.features.feedback.FeedbackManager -import com.tangem.tap.features.onboarding.service.ProductOnboardingService +import com.tangem.tap.features.onboarding.OnboardingManager import com.tangem.tap.network.coinmarketcap.CoinMarketCapService import com.tangem.tap.network.moonpay.MoonPayUserStatus import org.rekotlin.StateType data class GlobalState( val scanResponse: ScanResponse? = null, + val onboardingManager: OnboardingManager? = null, val cardVerifiedOnline: Boolean = false, val tapWalletManager: TapWalletManager = TapWalletManager(), val payIdManager: PayIdManager = PayIdManager(), @@ -26,7 +27,6 @@ data class GlobalState( val scanCardFailsCounter: Int = 0, val dialog: StateDialog? = null, val moonPayUserStatus: MoonPayUserStatus? = null, - val onboardingService: ProductOnboardingService? = null, ) : StateType typealias CryptoCurrencyName = String 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 76a9bc46f1..32403ae363 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -17,7 +17,6 @@ import com.tangem.tap.domain.extensions.* import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.domain.tokens.CardCurrencies import com.tangem.tap.domain.twins.getTwinCardNumber -import com.tangem.tap.domain.twins.getTwinsCardId import com.tangem.tap.domain.twins.isTangemTwin import com.tangem.tap.features.tokens.redux.TokensAction import com.tangem.tap.features.wallet.redux.Currency @@ -91,12 +90,8 @@ class TapWalletManager { store.dispatch(WalletAction.SetIfTestnetCard(data.card.isTestCard)) store.dispatch(WalletAction.MultiWallet.SetIsMultiwalletAllowed(data.card.isMultiwalletAllowed)) if (data.card.isTangemTwin()) { - val secondCardId = data.card.getTwinsCardId() - val cardNumber = data.card.getTwinCardNumber() - if (secondCardId != null && cardNumber != null) { - store.dispatch(WalletAction.TwinsAction.SetTwinCard( - secondCardId, cardNumber, isCreatingTwinCardsAllowed = true - )) + data.card.getTwinCardNumber()?.let { + store.dispatch(WalletAction.TwinsAction.SetTwinCard(null, it, true)) } } diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt b/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt index dc3fc1a618..124ae27387 100644 --- a/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt +++ b/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt @@ -3,7 +3,11 @@ package com.tangem.tap.domain.extensions import com.tangem.common.card.Card import com.tangem.common.card.CardWallet import com.tangem.common.extensions.toHexString +import com.tangem.common.services.Result +import com.tangem.operations.attestation.CardVerifyAndGetInfo import com.tangem.operations.attestation.OnlineCardVerifier +import com.tangem.tap.domain.twins.TwinCardNumber +import com.tangem.tap.domain.twins.getTwinCardNumber import com.tangem.tap.features.wallet.redux.Artwork fun Card.getSingleWallet(): CardWallet? { @@ -24,6 +28,35 @@ fun Card.signedHashesCount(): Int { return wallets.map { it.totalSignedHashes ?: 0 }.sum() } +suspend fun Card.getOrLoadCardArtworkUrl(cardInfo: Result? = null): String { + fun ifAnyError(): String { + return when { + cardId.startsWith(Artwork.SERGIO_CARD_ID) -> Artwork.SERGIO_CARD_URL + cardId.startsWith(Artwork.MARTA_CARD_ID) -> Artwork.MARTA_CARD_URL + else -> { + when (getTwinCardNumber()) { + TwinCardNumber.First -> Artwork.TWIN_CARD_1 + TwinCardNumber.Second -> Artwork.TWIN_CARD_2 + else -> Artwork.DEFAULT_IMG_URL + } + } + } + } + + val cardInfoResult = cardInfo ?: OnlineCardVerifier().getCardInfo(cardId, cardPublicKey) + return when (cardInfoResult) { + is Result.Success -> { + val artworkId = cardInfoResult.data.artwork?.id + if (artworkId == null || artworkId.isEmpty()) { + ifAnyError() + } else { + OnlineCardVerifier.getUrlForArtwork(cardId, cardPublicKey.toHexString(), artworkId) + } + } + is Result.Failure -> ifAnyError() + } +} + fun Card.getArtworkUrl(artworkId: String?): String? { return when { artworkId != null -> { 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 d879826f12..d2d8aa143c 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 @@ -21,7 +21,8 @@ import com.tangem.tap.tangemSdkManager class TwinCardsManager(private val scanResponse: ScanResponse, context: Context) { private val currentCardId: String = scanResponse.card.cardId - private val secondCardId: String? = TwinsHelper.getTwinsCardId(currentCardId) +// private val secondCardId: String? = TwinsHelper.getTwinsCardId(currentCardId) + private val secondCardId: String? = null private var currentCardPublicKey: String? = null private var secondCardPublicKey: String? = null diff --git a/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt b/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt index 8c66c05bfa..65c4d7cb53 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt @@ -1,7 +1,6 @@ package com.tangem.tap.domain.twins import com.tangem.common.card.Card -import com.tangem.tap.common.extensions.isEven class TwinsHelper { companion object { @@ -21,22 +20,6 @@ class TwinsHelper { } } - fun getTwinsCardId(cardId: String): String? { - val cardIdWithNewSeries = when (getTwinCardNumber(cardId) ?: return null) { - TwinCardNumber.First -> { - val index = if (cardId.startsWith(firstCardSeries[0])) 0 else 1 - cardId.replace(firstCardSeries[index], secondCardSeries[index]) - } - TwinCardNumber.Second -> { - val index = if (cardId.startsWith(secondCardSeries[0])) 0 else 1 - cardId.replace(secondCardSeries[index], firstCardSeries[index]) - } - } - val cardIdWithoutChecksum = cardIdWithNewSeries.dropLast(1) - val checkSum = cardIdWithoutChecksum.calculateLuhn() - return cardIdWithoutChecksum + checkSum - } - fun getTwinCardIdForUser(cardId: String): String { if (cardId.length < 16) return cardId @@ -50,21 +33,6 @@ class TwinsHelper { } } -private fun String.calculateLuhn(): Int { - val checksum = this.reversed() - .mapIndexed { index, c -> - val digit = if (c in '0'..'9') c - '0' else c - 'A' - if (!index.isEven()) { - digit - } else { - val newDigit = digit * 2 - if (newDigit >= 10) newDigit - 9 else newDigit - } - }.sum() - .rem(10) - return (10 - checksum) % 10 -} - enum class TwinCardNumber(val number: Int) { First(1), Second(2); @@ -82,10 +50,6 @@ fun Card.getTwinCardNumber(): TwinCardNumber? { return TwinsHelper.getTwinCardNumber(this.cardId) } -fun Card.getTwinsCardId(): String? { - return TwinsHelper.getTwinsCardId(this.cardId) -} - fun Card.getTwinCardIdForUser(): String { return TwinsHelper.getTwinCardIdForUser(this.cardId) } \ No newline at end of file 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 3e8ccb008b..4eb68d750c 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,24 +1,19 @@ package com.tangem.tap.features.home.redux import com.tangem.tap.common.entities.IndeterminateProgressButton -import com.tangem.tap.common.extensions.dispatchErrorNotification import com.tangem.tap.common.extensions.dispatchOpenUrl -import com.tangem.tap.common.extensions.withMainContext import com.tangem.tap.common.post import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.common.redux.navigation.FragmentShareTransition import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.DELAY_SDK_DIALOG_CLOSE -import com.tangem.tap.domain.TapWalletManager -import com.tangem.tap.domain.tasks.product.ScanResponse -import com.tangem.tap.features.onboarding.service.OnboardingHelper +import com.tangem.tap.features.onboarding.OnboardingHelper import com.tangem.tap.features.send.redux.states.ButtonState -import com.tangem.tap.features.wallet.redux.ProgressState import com.tangem.tap.preferencesStorage import com.tangem.tap.scope import com.tangem.tap.store -import kotlinx.coroutines.delay import kotlinx.coroutines.launch import org.rekotlin.Middleware @@ -52,55 +47,30 @@ private fun handleReadCard() { if (!preferencesStorage.wasDisclaimerAccepted()) { store.dispatch(NavigationAction.NavigateTo(AppScreen.Disclaimer)) } else { - fun changeButtonState(state: ButtonState) { - val btnState = IndeterminateProgressButton(state) - store.dispatch(HomeAction.ChangeScanCardButtonState(btnState)) - } - - fun proceedToWalletScreen(tapWalletManager: TapWalletManager, scanResponse: ScanResponse) { - scope.launch { - tapWalletManager.onCardScanned(scanResponse) - delay(DELAY_SDK_DIALOG_CLOSE) - withMainContext { changeButtonState(ButtonState.ENABLED) } - store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) - } - } changeButtonState(ButtonState.PROGRESS) - - store.dispatch(GlobalAction.ReadCard({ scanResponse -> - val tapWalletManager = store.state.globalState.tapWalletManager - if (!OnboardingHelper.isOnboardingCase(scanResponse)) { - proceedToWalletScreen(tapWalletManager, scanResponse) - return@ReadCard + store.dispatch(GlobalAction.ScanCard({ scanResponse -> + if (OnboardingHelper.isOnboardingCase(scanResponse)) { + val navigateTo = OnboardingHelper.whereToNavigate(scanResponse) + store.dispatch(GlobalAction.Onboarding.Start(scanResponse, AppScreen.Home)) + navigateTo(navigateTo, store.state.homeState.shareTransition) + } else { + scope.launch { store.state.globalState.tapWalletManager.onCardScanned(scanResponse) } + navigateTo(AppScreen.Wallet, null) } - - val service = OnboardingHelper.createService( - AppScreen.Home, - scanResponse, - tapWalletManager.walletManagerFactory - ) - if (service == null) { - proceedToWalletScreen(tapWalletManager, scanResponse) - return@ReadCard - } - - service.onInitializationProgress = { - when (it) { - ProgressState.Loading -> changeButtonState(ButtonState.PROGRESS) - ProgressState.Done -> changeButtonState(ButtonState.ENABLED) - ProgressState.Error -> changeButtonState(ButtonState.ENABLED) - } - } - service.onInitialized = { - store.dispatch(GlobalAction.Onboarding.Activate(service)) - store.dispatch(NavigationAction.NavigateTo(it, store.state.homeState.shareTransition)) - } - service.onError = { - store.dispatchErrorNotification(it) - } - post(DELAY_SDK_DIALOG_CLOSE) { service.initializeOnboarding() } }, { changeButtonState(ButtonState.ENABLED) })) } +} + +private fun changeButtonState(state: ButtonState) { + val btnState = IndeterminateProgressButton(state) + store.dispatch(HomeAction.ChangeScanCardButtonState(btnState)) +} + +private fun navigateTo(screen: AppScreen, transition: FragmentShareTransition?) { + post(DELAY_SDK_DIALOG_CLOSE) { + changeButtonState(ButtonState.ENABLED) + store.dispatch(NavigationAction.NavigateTo(screen, transition)) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt new file mode 100644 index 0000000000..38b2517ad1 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt @@ -0,0 +1,27 @@ +package com.tangem.tap.features.onboarding + +import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.domain.ProductType +import com.tangem.tap.domain.extensions.hasWallets +import com.tangem.tap.domain.tasks.product.ScanResponse +import com.tangem.tap.preferencesStorage + +/** +[REDACTED_AUTHOR] + */ +class OnboardingHelper { + companion object { + + fun isOnboardingCase(response: ScanResponse): Boolean = if (response.card.hasWallets()) + preferencesStorage.usedCardsPrefStorage.activationIsStarted(response.card.cardId) else true + + fun whereToNavigate(scanResponse: ScanResponse): AppScreen { + return when (scanResponse.productType) { + ProductType.Note -> AppScreen.OnboardingNote + ProductType.Wallet -> AppScreen.OnboardingWallet + ProductType.Twin -> AppScreen.OnboardingTwins + ProductType.Other -> AppScreen.OnboardingOther + } + } + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..62358e80c0 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingManager.kt @@ -0,0 +1,129 @@ +package com.tangem.tap.features.onboarding + +import com.tangem.blockchain.common.AmountType +import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.common.WalletManager +import com.tangem.common.extensions.isZero +import com.tangem.common.services.Result +import com.tangem.operations.attestation.CardVerifyAndGetInfo +import com.tangem.operations.attestation.OnlineCardVerifier +import com.tangem.tap.common.extensions.isPositive +import com.tangem.tap.common.extensions.safeUpdate +import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.domain.TapError +import com.tangem.tap.domain.UrlBitmapLoader +import com.tangem.tap.domain.extensions.getOrLoadCardArtworkUrl +import com.tangem.tap.domain.tasks.product.ScanResponse +import com.tangem.tap.features.wallet.redux.Artwork +import com.tangem.tap.features.wallet.redux.Currency +import com.tangem.tap.features.wallet.redux.ProgressState +import com.tangem.tap.persistence.UsedCardsPrefStorage +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.math.BigDecimal +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine + +/** +[REDACTED_AUTHOR] + */ +class OnboardingManager( + var scanResponse: ScanResponse, + val fromScreen: AppScreen, + val usedCardsPrefStorage: UsedCardsPrefStorage, +) { + + var cardInfo: Result? = null + private set + + 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 { + return when (val result = walletManager.safeUpdate()) { + is Result.Success -> { + val wallet = walletManager.wallet + val valueOfAmount = wallet.amounts[AmountType.Coin]?.value + if (valueOfAmount == null) { + val customError = TapError.CustomError("Amount is NULL") + OnboardingWalletBalance.criticalError(customError) + } else { + val currency = Currency.Blockchain(walletManager.wallet.blockchain) + val balance = if (valueOfAmount.isZero()) { + OnboardingWalletBalance.done(valueOfAmount, wallet.recentTransactions.isNotEmpty(), currency) + } else { + OnboardingWalletBalance.done(valueOfAmount, false, currency) + } + balance + } + } + is Result.Failure -> { + val error = (result.error as? TapError) ?: TapError.UnknownError + when (error) { + is TapError.WalletManagerUpdate.NoAccountError -> OnboardingWalletBalance.error(error) + // NoInternetConnection, WalletManagerUpdate.InternalError + else -> OnboardingWalletBalance.criticalError(error) + } + } + } + } + + fun activationStarted(cardId: String) { + usedCardsPrefStorage.activationStarted(cardId) + } + + fun activationFinished(cardId: String) { + usedCardsPrefStorage.activationFinished(cardId) + } +} + +data class OnboardingWalletBalance( + val value: BigDecimal = BigDecimal.ZERO, + val currency: Currency.Blockchain = Currency.Blockchain(Blockchain.Unknown), + val hasIncomingTransaction: Boolean = false, + val state: ProgressState, + val error: TapError? = null, + val criticalError: TapError? = null, +) { + + fun balanceIsToppedUp(): Boolean = value.isPositive() || hasIncomingTransaction + + val amountToCreateAccount: String? + get() = if (error is TapError.WalletManagerUpdate.NoAccountError) error.customMessage else null + + companion object { + fun error(error: TapError): OnboardingWalletBalance = OnboardingWalletBalance( + state = ProgressState.Error, + error = error + ) + + fun criticalError(error: TapError): OnboardingWalletBalance = OnboardingWalletBalance( + state = ProgressState.Error, + criticalError = error + ) + + fun loading(value: BigDecimal = BigDecimal.ZERO): OnboardingWalletBalance = OnboardingWalletBalance( + value, + state = ProgressState.Loading + ) + + fun done(value: BigDecimal, hasTransactions: Boolean, currency: Currency.Blockchain): OnboardingWalletBalance = + OnboardingWalletBalance(value, currency, hasTransactions, ProgressState.Done) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/BaseOnboardingFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/BaseOnboardingFragment.kt index b27d17e06a..dde273aa2f 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/BaseOnboardingFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/BaseOnboardingFragment.kt @@ -24,7 +24,7 @@ abstract class BaseOnboardingFragment : BaseStoreFragment(R.layout.fragment_o protected var fromScreen: AppScreen? = null override fun onCreate(savedInstanceState: Bundle?) { - fromScreen = store.state.globalState.onboardingService?.fromScreen + fromScreen = store.state.globalState.onboardingManager?.fromScreen super.onCreate(savedInstanceState) } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt index 7712bbc3da..97d31afd41 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt @@ -1,13 +1,18 @@ package com.tangem.tap.features.onboarding.products.note +import android.graphics.Bitmap +import android.graphics.drawable.BitmapDrawable import android.os.Bundle import android.view.View import android.view.animation.OvershootInterpolator +import android.widget.ImageView import androidx.annotation.LayoutRes import androidx.constraintlayout.widget.ConstraintSet import androidx.core.view.isVisible import androidx.transition.TransitionManager import com.tangem.blockchain.common.Blockchain +import com.tangem.tangem_sdk_new.extensions.fadeIn +import com.tangem.tangem_sdk_new.extensions.fadeOut import com.tangem.tap.common.extensions.getDrawableCompat import com.tangem.tap.common.extensions.stripZeroPlainString import com.tangem.tap.common.postUi @@ -28,6 +33,7 @@ import kotlinx.android.synthetic.main.view_onboarding_tv_balance.* class OnboardingNoteFragment : BaseOnboardingFragment() { private lateinit var btnRefreshBalanceWidget: RefreshBalanceWidget + private var currentStep: OnboardingNoteStep = OnboardingNoteStep.None override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_note @@ -36,11 +42,12 @@ class OnboardingNoteFragment : BaseOnboardingFragment() { btnRefreshBalanceWidget = RefreshBalanceWidget(onboarding_main_container) val resources = AndroidResources( - OnboardingStringResources( - R.string.copy_toast_msg - ) + OnboardingStringResources( + R.string.copy_toast_msg + ) ) + store.dispatch(OnboardingNoteAction.LoadCardArtwork) store.dispatch(OnboardingNoteAction.SetResources(resources)) store.dispatch(OnboardingNoteAction.DetermineStepOfScreen) } @@ -57,7 +64,7 @@ class OnboardingNoteFragment : BaseOnboardingFragment() { override fun newState(state: OnboardingNoteState) { if (activity == null) return - state.artworkBitmap?.let { imv_front_card.setImageBitmap(it) } + state.cardArtwork?.let { imv_front_card.swapToBitmapDrawable(it.artwork) } pb_state.max = state.steps.size - 1 pb_state.progress = state.progress @@ -69,13 +76,14 @@ class OnboardingNoteFragment : BaseOnboardingFragment() { } setBalance(state) showConfetti(state.showConfetti) + currentStep = state.currentStep } private fun setBalance(state: OnboardingNoteState) { - if (state.balanceCurrency.blockchain == Blockchain.Unknown) return + if (state.walletBalance.currency.blockchain == Blockchain.Unknown) return - val balanceValue = state.balanceValue.stripZeroPlainString() - val currency = state.balanceCurrency.currencySymbol + val balanceValue = state.walletBalance.value.stripZeroPlainString() + val currency = state.walletBalance.currency.currencySymbol tv_balance_value.text = "$balanceValue $currency" } @@ -104,7 +112,7 @@ class OnboardingNoteFragment : BaseOnboardingFragment() { tv_body.setText(R.string.onboarding_create_wallet_body) imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_circle)) - updateConstraints(R.layout.lp_onboarding_create_wallet) + updateConstraints(state.currentStep, R.layout.lp_onboarding_create_wallet) } private fun setupTopUpWalletState(state: OnboardingNoteState) { @@ -121,7 +129,7 @@ class OnboardingNoteFragment : BaseOnboardingFragment() { tv_header.setText(R.string.onboarding_top_up_header) tv_body.setText(R.string.onboarding_top_up_body) - btnRefreshBalanceWidget.changeState(state.balanceState) + btnRefreshBalanceWidget.changeState(state.walletBalance.state) if (btnRefreshBalanceWidget.isShowing != true) { postUi(300) { btnRefreshBalanceWidget.mainView.setOnClickListener { @@ -130,7 +138,7 @@ class OnboardingNoteFragment : BaseOnboardingFragment() { } } imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_rectangle_rounded_8)) - updateConstraints(R.layout.lp_onboarding_topup_wallet) + updateConstraints(state.currentStep, R.layout.lp_onboarding_topup_wallet) } private fun setupDoneState(state: OnboardingNoteState) { @@ -149,10 +157,12 @@ class OnboardingNoteFragment : BaseOnboardingFragment() { tv_body.setText(R.string.onboarding_done_body) imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_rectangle_rounded_8)) - updateConstraints(R.layout.lp_onboarding_done_activation) + updateConstraints(state.currentStep, R.layout.lp_onboarding_done_activation) } - private fun updateConstraints(@LayoutRes layoutId: Int) { + private fun updateConstraints(currentStep: OnboardingNoteStep, @LayoutRes layoutId: Int) { + if (this.currentStep == currentStep) return + val constraintSet = ConstraintSet() constraintSet.clone(requireContext(), layoutId) constraintSet.applyTo(onboarding_main_container) @@ -160,4 +170,13 @@ class OnboardingNoteFragment : BaseOnboardingFragment() { transition.interpolator = OvershootInterpolator() TransitionManager.beginDelayedTransition(onboarding_main_container, transition) } +} + +fun ImageView.swapToBitmapDrawable(bitmap: Bitmap?, duration: Long = 200) { + if (drawable is BitmapDrawable && (drawable as BitmapDrawable).bitmap == bitmap) return + + fadeOut(duration) { + setImageBitmap(bitmap) + fadeIn(duration) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt index 9c1644dda1..798f8f4136 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt @@ -1,6 +1,8 @@ package com.tangem.tap.features.onboarding.products.note.redux -import com.tangem.tap.features.onboarding.service.OnboardingWalletBalance +import com.tangem.blockchain.common.WalletManager +import com.tangem.tap.features.onboarding.OnboardingWalletBalance +import com.tangem.tap.features.wallet.redux.Artwork import org.rekotlin.Action /** @@ -9,11 +11,14 @@ import org.rekotlin.Action sealed class OnboardingNoteAction : Action { // from user, ui data class SetResources(val resources: AndroidResources) : OnboardingNoteAction() + object LoadCardArtwork : OnboardingNoteAction() object CreateWallet : OnboardingNoteAction() object TopUp : OnboardingNoteAction() object ShowAddressInfoDialog : OnboardingNoteAction() // from redux + class SetArtworkUrl(val artwork: Artwork) : OnboardingNoteAction() + data class SetWalletManager(val walletManager: WalletManager) : OnboardingNoteAction() object DetermineStepOfScreen : OnboardingNoteAction() object Done : OnboardingNoteAction() diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt index 83ea98d0d0..e63b32b9b4 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt @@ -1,6 +1,7 @@ package com.tangem.tap.features.onboarding.products.note.redux import com.tangem.common.CompletionResult +import com.tangem.common.extensions.guard import com.tangem.tap.common.extensions.* import com.tangem.tap.common.postUi import com.tangem.tap.common.redux.AppDialog @@ -8,13 +9,16 @@ import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction -import com.tangem.tap.features.onboarding.service.balanceIsToppedUp +import com.tangem.tap.domain.TapError +import com.tangem.tap.domain.extensions.hasWallets +import com.tangem.tap.domain.extensions.makePrimaryWalletManager import com.tangem.tap.features.wallet.redux.ProgressState import com.tangem.tap.scope import com.tangem.tap.store import com.tangem.tap.tangemSdkManager import kotlinx.coroutines.launch import org.rekotlin.Action +import org.rekotlin.DispatchFunction import org.rekotlin.Middleware class OnboardingNoteMiddleware { @@ -26,44 +30,57 @@ class OnboardingNoteMiddleware { private val onboardingNoteMiddleware: Middleware = { dispatch, state -> { next -> { action -> - handleNoteAction(action) + handleNoteAction(action, dispatch) next(action) } } } -private fun handleNoteAction(action: Action) { +private fun handleNoteAction(action: Action, dispatch: DispatchFunction) { if (action !is OnboardingNoteAction) return - val service = store.state.onboardingNoteState.onboardingService ?: return + val globalState = store.state.globalState + val onboardingManager = globalState.onboardingManager ?: return + + val scanResponse = onboardingManager.scanResponse + val card = onboardingManager.scanResponse.card val noteState = store.state.onboardingNoteState when (action) { + is OnboardingNoteAction.LoadCardArtwork -> { + scope.launch { + val artwork = onboardingManager.loadArtwork() + withMainContext { store.dispatch(OnboardingNoteAction.SetArtworkUrl(artwork)) } + } + } is OnboardingNoteAction.DetermineStepOfScreen -> { val step = when { - service.walletManager == null -> OnboardingNoteStep.CreateWallet - service.balanceIsToppedUp() -> OnboardingNoteStep.Done + !card.hasWallets() -> OnboardingNoteStep.CreateWallet + noteState.walletBalance.balanceIsToppedUp() -> OnboardingNoteStep.Done else -> OnboardingNoteStep.TopUpWallet } store.dispatch((OnboardingNoteAction.SetStepOfScreen(step))) } is OnboardingNoteAction.SetStepOfScreen -> { - if (action.step == OnboardingNoteStep.Done) { - service.activationFinished() - postUi(500) { store.dispatch(OnboardingNoteAction.Confetti.Show) } + when (action.step) { + OnboardingNoteStep.TopUpWallet -> { + store.dispatch(OnboardingNoteAction.Balance.Update) + } + OnboardingNoteStep.Done -> { + onboardingManager.activationFinished(card.cardId) + postUi(500) { store.dispatch(OnboardingNoteAction.Confetti.Show) } + } } } is OnboardingNoteAction.CreateWallet -> { scope.launch { - val result = tangemSdkManager.createProductWallet(service.scanResponse) + val result = tangemSdkManager.createProductWallet(scanResponse) withMainContext { when (result) { is CompletionResult.Success -> { - val updatedResponse = service.scanResponse.copy(card = result.data) - service.scanResponse = updatedResponse - service.activationStarted() - + val updatedResponse = scanResponse.copy(card = result.data) + onboardingManager.scanResponse = updatedResponse + onboardingManager.activationStarted(updatedResponse.card.cardId) store.dispatch(OnboardingNoteAction.SetStepOfScreen(OnboardingNoteStep.TopUpWallet)) - store.dispatch(OnboardingNoteAction.Balance.Update) } is CompletionResult.Failure -> { // do nothing @@ -73,44 +90,57 @@ private fun handleNoteAction(action: Action) { } } is OnboardingNoteAction.Balance.Update -> { - val loadingBalance = service.getBalance().copy( - state = ProgressState.Loading, - error = null, - criticalError = null + val walletManager = if (noteState.walletManager != null) { + noteState.walletManager + } else { + val wmFactory = globalState.tapWalletManager.walletManagerFactory + val walletManager = wmFactory.makePrimaryWalletManager(scanResponse).guard { + val message = "Loading cancelled. Cause: wallet manager didn't created" + val customError = TapError.CustomError(message) + store.dispatchErrorNotification(customError) + return + } + dispatch(OnboardingNoteAction.SetWalletManager(walletManager)) + walletManager + } + + val balanceIsLoading = noteState.walletBalance.copy( + state = ProgressState.Loading, + error = null, + criticalError = null ) - store.dispatch(OnboardingNoteAction.Balance.Set(loadingBalance)) + store.dispatch(OnboardingNoteAction.Balance.Set(balanceIsLoading)) scope.launch { - val loadedBalance = service.updateBalance() - if (loadedBalance.criticalError != null) { - store.dispatchErrorNotification(loadedBalance.criticalError) - } + val loadedBalance = onboardingManager.updateBalance(walletManager) + loadedBalance.criticalError?.let { store.dispatchErrorNotification(it) } withMainContext { store.dispatch(OnboardingNoteAction.Balance.Set(loadedBalance)) } } + } is OnboardingNoteAction.Balance.Set -> { - if (service.balanceIsToppedUp()) { + if (action.balance.balanceIsToppedUp()) { store.dispatch(OnboardingNoteAction.SetStepOfScreen(OnboardingNoteStep.Done)) } } is OnboardingNoteAction.ShowAddressInfoDialog -> { - val addressData = noteState.onboardingService?.getAddressData() ?: return + val addressData = noteState.walletManager?.getAddressData() ?: return val addressWasCopied = noteState.resources.strings.addressWasCopied val appDialog = AppDialog.AddressInfoDialog( - addressData, - onCopyAddress = { store.dispatchToastNotification(addressWasCopied) }, - onExploreAddress = { store.dispatchOpenUrl(addressData.exploreUrl) } + addressData, + onCopyAddress = { store.dispatchToastNotification(addressWasCopied) }, + onExploreAddress = { store.dispatchOpenUrl(addressData.exploreUrl) } ) store.dispatchDialogShow(appDialog) } is OnboardingNoteAction.TopUp -> { - val topUpUrl = noteState.onboardingService?.getToUpUrl() ?: return + val topUpUrl = noteState.walletManager?.getToUpUrl() ?: return store.dispatchOpenUrl(topUpUrl) } OnboardingNoteAction.Done -> { - store.dispatch(GlobalAction.Onboarding.Deactivate) + store.dispatch(GlobalAction.Onboarding.Stop) scope.launch { - store.state.globalState.tapWalletManager.onCardScanned(service.scanResponse) + globalState.tapWalletManager.onCardScanned(scanResponse) store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt index efe2540b66..03415eec84 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt @@ -2,7 +2,6 @@ package com.tangem.tap.features.onboarding.products.note.redux import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.features.onboarding.service.OnboardingNoteService import org.rekotlin.Action class OnboardingNoteReducer { @@ -15,34 +14,20 @@ private fun internalReduce(action: Action, appState: AppState): OnboardingNoteSt var state = appState.onboardingNoteState when (action) { - is GlobalAction.Onboarding.Activate -> { - if (action.onboardingService is OnboardingNoteService) { - val service = action.onboardingService - val balance = service.getBalance() - state = state.copy( - onboardingService = service, - artworkBitmap = service.getArtwork().value?.artwork, - balanceValue = balance.value, - balanceCurrency = balance.currency, - balanceState = balance.state, - amountToCreateAccount = balance.amountToCreateAccount, - showConfetti = false - ) - } - } - GlobalAction.Onboarding.Deactivate -> { + is GlobalAction.Onboarding.Start -> { state = OnboardingNoteState() } + is OnboardingNoteAction.SetArtworkUrl -> { + state = state.copy(cardArtwork = action.artwork) + } + is OnboardingNoteAction.SetWalletManager -> { + state = state.copy(walletManager = action.walletManager) + } is OnboardingNoteAction.SetResources -> { state = state.copy(resources = action.resources) } is OnboardingNoteAction.Balance.Set -> { - state = state.copy( - balanceValue = action.balance.value, - balanceCurrency = action.balance.currency, - balanceState = action.balance.state, - amountToCreateAccount = action.balance.amountToCreateAccount, - ) + state = state.copy(walletBalance = action.balance) } is OnboardingNoteAction.SetStepOfScreen -> { if (action.step != state.currentStep && state.steps.contains(action.step)) { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt index b95c58510d..d4f1c2bcb7 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt @@ -1,28 +1,22 @@ package com.tangem.tap.features.onboarding.products.note.redux -import android.graphics.Bitmap -import com.tangem.blockchain.common.Blockchain -import com.tangem.tap.features.onboarding.service.OnboardingNoteService -import com.tangem.tap.features.wallet.redux.Currency -import com.tangem.tap.features.wallet.redux.ProgressState +import com.tangem.blockchain.common.WalletManager +import com.tangem.tap.features.onboarding.OnboardingWalletBalance +import com.tangem.tap.features.wallet.redux.Artwork import org.rekotlin.StateType -import java.math.BigDecimal /** [REDACTED_AUTHOR] */ data class OnboardingNoteState( - val onboardingService: OnboardingNoteService? = null, + val walletManager: WalletManager? = null, val resources: AndroidResources = AndroidResources(), - // UI - val showConfetti: Boolean = false, - val artworkBitmap: Bitmap? = null, - val balanceValue: BigDecimal = BigDecimal.ZERO, - val balanceCurrency: Currency = Currency.Blockchain(Blockchain.Unknown), - val balanceState: ProgressState = ProgressState.Done, - val amountToCreateAccount: String? = null, + // UI + val cardArtwork: Artwork? = null, + val walletBalance: OnboardingWalletBalance = OnboardingWalletBalance.loading(), val currentStep: OnboardingNoteStep = OnboardingNoteStep.None, val steps: List = OnboardingNoteStep.values().toList(), + val showConfetti: Boolean = false, ) : StateType { val progress: Int diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt index 99ce6f4280..3b2dcf7952 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt @@ -10,6 +10,7 @@ import androidx.transition.TransitionManager import com.tangem.tap.common.extensions.getDrawableCompat import com.tangem.tap.common.transitions.InternalNoteLayoutTransition import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment +import com.tangem.tap.features.onboarding.products.note.swapToBitmapDrawable import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsAction import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsState import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsStep @@ -29,6 +30,7 @@ class OnboardingOtherCardsFragment : BaseOnboardingFragment = { dispatch, state -> { next -> { action -> - handleOtherCardsAction(action) + handleOtherCardsAction(action, dispatch) next(action) } } } -private fun handleOtherCardsAction(action: Action) { +private fun handleOtherCardsAction(action: Action, dispatch: DispatchFunction) { if (action !is OnboardingOtherCardsAction) return - val service = store.state.onboardingOtherCardsState.onboardingService ?: return - val noteState = store.state.onboardingOtherCardsState + val globalState = store.state.globalState + val onboardingManager = globalState.onboardingManager ?: return + + val card = onboardingManager.scanResponse.card when (action) { + is OnboardingOtherCardsAction.LoadCardArtwork -> { + scope.launch { + val artwork = onboardingManager.loadArtwork() + withMainContext { store.dispatch(OnboardingOtherCardsAction.SetArtworkUrl(artwork)) } + } + } is OnboardingOtherCardsAction.DetermineStepOfScreen -> { val step = when { - service.walletManager == null -> OnboardingOtherCardsStep.CreateWallet + !card.hasWallets() -> OnboardingOtherCardsStep.CreateWallet else -> OnboardingOtherCardsStep.Done } store.dispatch((OnboardingOtherCardsAction.SetStepOfScreen(step))) } is OnboardingOtherCardsAction.SetStepOfScreen -> { if (action.step == OnboardingOtherCardsStep.Done) { - service.activationFinished() + onboardingManager.activationFinished(card.cardId) postUi(500) { store.dispatch(OnboardingOtherCardsAction.Confetti.Show) } } } is OnboardingOtherCardsAction.CreateWallet -> { scope.launch { - val result = tangemSdkManager.createProductWallet(service.scanResponse) + val result = tangemSdkManager.createProductWallet(onboardingManager.scanResponse) withMainContext { when (result) { is CompletionResult.Success -> { - val updatedResponse = service.scanResponse.copy(card = result.data) - service.scanResponse = updatedResponse - service.activationStarted() + val updatedResponse = onboardingManager.scanResponse.copy(card = result.data) + onboardingManager.scanResponse = updatedResponse + onboardingManager.activationStarted(updatedResponse.card.cardId) delay(DELAY_SDK_DIALOG_CLOSE) store.dispatch(OnboardingOtherCardsAction.SetStepOfScreen(OnboardingOtherCardsStep.Done)) @@ -71,9 +81,9 @@ private fun handleOtherCardsAction(action: Action) { } } OnboardingOtherCardsAction.Done -> { - store.dispatch(GlobalAction.Onboarding.Deactivate) + store.dispatch(GlobalAction.Onboarding.Stop) scope.launch { - store.state.globalState.tapWalletManager.onCardScanned(service.scanResponse) + globalState.tapWalletManager.onCardScanned(onboardingManager.scanResponse) store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsReducer.kt index 316f97f636..a6313b9199 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsReducer.kt @@ -2,7 +2,6 @@ package com.tangem.tap.features.onboarding.products.otherCards.redux import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.features.onboarding.service.OnboardingOtherCardsService import org.rekotlin.Action class OnboardingOtherCardsReducer { @@ -15,19 +14,12 @@ private fun internalReduce(action: Action, appState: AppState): OnboardingOtherC var state = appState.onboardingOtherCardsState when (action) { - is GlobalAction.Onboarding.Activate -> { - if (action.onboardingService is OnboardingOtherCardsService) { - val service = action.onboardingService - state = state.copy( - onboardingService = service, - artworkBitmap = service.getArtwork().value?.artwork, - showConfetti = false - ) - } - } - GlobalAction.Onboarding.Deactivate -> { + is GlobalAction.Onboarding.Start -> { state = OnboardingOtherCardsState() } + is OnboardingOtherCardsAction.SetArtworkUrl -> { + state = state.copy(cardArtwork = action.artwork) + } is OnboardingOtherCardsAction.SetStepOfScreen -> { if (action.step != state.currentStep && state.steps.contains(action.step)) { state = state.copy(currentStep = action.step) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsState.kt index bcf232bd85..b306101506 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsState.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsState.kt @@ -1,17 +1,15 @@ package com.tangem.tap.features.onboarding.products.otherCards.redux -import android.graphics.Bitmap -import com.tangem.tap.features.onboarding.service.OnboardingOtherCardsService +import com.tangem.tap.features.wallet.redux.Artwork import org.rekotlin.StateType /** [REDACTED_AUTHOR] */ data class OnboardingOtherCardsState( - val onboardingService: OnboardingOtherCardsService? = null, - // UI + // UI + val cardArtwork: Artwork? = null, val showConfetti: Boolean = false, - val artworkBitmap: Bitmap? = null, val currentStep: OnboardingOtherCardsStep = OnboardingOtherCardsStep.None, val steps: List = OnboardingOtherCardsStep.values().toList(), ) : StateType { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingHelper.kt b/app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingHelper.kt deleted file mode 100644 index 8f7eb6f726..0000000000 --- a/app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingHelper.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.tangem.tap.features.onboarding.service - -import com.tangem.blockchain.common.WalletManagerFactory -import com.tangem.tap.common.redux.navigation.AppScreen -import com.tangem.tap.domain.ProductType -import com.tangem.tap.domain.extensions.hasWallets -import com.tangem.tap.domain.tasks.product.ScanResponse -import com.tangem.tap.preferencesStorage - -/** -[REDACTED_AUTHOR] - */ -class OnboardingHelper { - companion object { - - fun isOnboardingCase(response: ScanResponse): Boolean = if (response.card.hasWallets()) - preferencesStorage.usedCardsPrefStorage.activationIsStarted(response.card.cardId) else true - - fun createService( - fromScreen: AppScreen, - scanResponse: ScanResponse, - walletManagerFactory: WalletManagerFactory, - ): ProductOnboardingService? { - val cardInfoStorage = preferencesStorage.usedCardsPrefStorage - return when(scanResponse.productType) { - ProductType.Note -> OnboardingNoteService(fromScreen, scanResponse, cardInfoStorage, walletManagerFactory) - ProductType.Wallet -> null - ProductType.Twin -> null - ProductType.Other -> OnboardingOtherCardsService(fromScreen, scanResponse, cardInfoStorage, walletManagerFactory) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingNoteService.kt b/app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingNoteService.kt deleted file mode 100644 index feaa76eb5c..0000000000 --- a/app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingNoteService.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.tangem.tap.features.onboarding.service - -import com.tangem.blockchain.common.WalletManagerFactory -import com.tangem.tap.common.redux.navigation.AppScreen -import com.tangem.tap.domain.tasks.product.ScanResponse -import com.tangem.tap.persistence.UsedCardsPrefStorage - -/** -[REDACTED_AUTHOR] - */ -class OnboardingNoteService( - override val fromScreen: AppScreen, - override var scanResponse: ScanResponse, - cardInfoStorage: UsedCardsPrefStorage, - walletManagerFactory: WalletManagerFactory, -) : ProductOnboardingService(cardInfoStorage, walletManagerFactory) { - - override fun navigateToScreen(): AppScreen { - return AppScreen.OnboardingNote - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingOtherCardsService.kt b/app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingOtherCardsService.kt deleted file mode 100644 index 5e81842312..0000000000 --- a/app/src/main/java/com/tangem/tap/features/onboarding/service/OnboardingOtherCardsService.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.tangem.tap.features.onboarding.service - -import com.tangem.blockchain.common.WalletManagerFactory -import com.tangem.tap.common.extensions.withMainContext -import com.tangem.tap.common.redux.navigation.AppScreen -import com.tangem.tap.domain.TapError -import com.tangem.tap.domain.tasks.product.ScanResponse -import com.tangem.tap.features.wallet.redux.AddressData -import com.tangem.tap.persistence.UsedCardsPrefStorage - -/** -[REDACTED_AUTHOR] - */ -class OnboardingOtherCardsService( - override val fromScreen: AppScreen, - override var scanResponse: ScanResponse, - cardInfoStorage: UsedCardsPrefStorage, - walletManagerFactory: WalletManagerFactory, -) : ProductOnboardingService(cardInfoStorage, walletManagerFactory) { - - override fun navigateToScreen(): AppScreen { - return AppScreen.OnboardingOther - } - - // Return any value and any state. The balance of the card is not important in this service - override suspend fun updateBalance(): OnboardingWalletBalance { - val customError = TapError.CustomError("Loading cancelled. Cause: wallet manager didn't created") - withMainContext { loadedBalance = OnboardingWalletBalance.error(customError) } - return loadedBalance - } - - override fun getToUpUrl(): String? { - throw UnsupportedOperationException() - } - - override fun getAddressData(): AddressData? { - throw UnsupportedOperationException() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/service/ProductOnboardingService.kt b/app/src/main/java/com/tangem/tap/features/onboarding/service/ProductOnboardingService.kt deleted file mode 100644 index 6faff4073e..0000000000 --- a/app/src/main/java/com/tangem/tap/features/onboarding/service/ProductOnboardingService.kt +++ /dev/null @@ -1,291 +0,0 @@ -package com.tangem.tap.features.onboarding.service - -import android.util.Log -import com.tangem.blockchain.common.* -import com.tangem.common.card.Card -import com.tangem.common.extensions.guard -import com.tangem.common.extensions.isZero -import com.tangem.common.services.Result -import com.tangem.operations.attestation.OnlineCardVerifier -import com.tangem.tap.common.extensions.dispatchDebugErrorNotification -import com.tangem.tap.common.extensions.isPositive -import com.tangem.tap.common.extensions.safeUpdate -import com.tangem.tap.common.extensions.withMainContext -import com.tangem.tap.common.redux.navigation.AppScreen -import com.tangem.tap.domain.TapError -import com.tangem.tap.domain.UrlBitmapLoader -import com.tangem.tap.domain.extensions.getArtworkUrl -import com.tangem.tap.domain.extensions.makePrimaryWalletManager -import com.tangem.tap.domain.tasks.product.ScanResponse -import com.tangem.tap.domain.topup.TradeCryptoHelper -import com.tangem.tap.features.wallet.redux.AddressData -import com.tangem.tap.features.wallet.redux.Artwork -import com.tangem.tap.features.wallet.redux.Currency -import com.tangem.tap.features.wallet.redux.ProgressState -import com.tangem.tap.features.wallet.redux.reducers.createAddressesData -import com.tangem.tap.persistence.UsedCardsPrefStorage -import com.tangem.tap.store -import kotlinx.coroutines.* -import java.io.PrintWriter -import java.io.StringWriter -import java.math.BigDecimal -import kotlin.coroutines.resume -import kotlin.coroutines.suspendCoroutine - -/** -[REDACTED_AUTHOR] - */ -interface OnboardingService { - val fromScreen: AppScreen - val scanResponse: ScanResponse - - // val cardInfoStorage: UsedCardsPrefStorage - var walletManager: WalletManager? - - var onInitializationProgress: ((ProgressState) -> Unit)? - var onInitialized: ((AppScreen) -> Unit)? - var onError: ((TapError) -> Unit)? - - fun initializeOnboarding() - fun getArtwork(): OnboardingArtwork - fun getBalance(): OnboardingWalletBalance - suspend fun updateBalance(): OnboardingWalletBalance - fun getToUpUrl(): String? - fun getAddressData(): AddressData? - - fun activationStarted() - fun activationFinished() -} - -abstract class ProductOnboardingService( - private val cardInfoStorage: UsedCardsPrefStorage, - private val walletManagerFactory: WalletManagerFactory, -) : OnboardingService { - - override var walletManager: WalletManager? = null - - override var onInitializationProgress: ((ProgressState) -> Unit)? = null - override var onInitialized: ((AppScreen) -> Unit)? = null - override var onError: ((TapError) -> Unit)? = null - - protected var isProceeded = false - - protected var loadedCardArtwork: OnboardingArtwork = OnboardingArtwork.loading() - set(value) { - field = value - tryToProceed() - } - - protected var loadedBalance: OnboardingWalletBalance = OnboardingWalletBalance.loading() - set(value) { - field = value - tryToProceed() - } - - protected val internalScope = CoroutineScope(Job() + Dispatchers.IO + coroutineExceptionHandler()) - - override fun initializeOnboarding() { - val card = scanResponse.card - onInitializationProgress?.invoke(ProgressState.Loading) - - internalScope.launch { - val artwork = loadCardArtwork(card) - withMainContext { - loadedCardArtwork = if (artwork == null) OnboardingArtwork.error() else OnboardingArtwork.done(artwork) - } - } - internalScope.launch { updateBalance() } - } - - override fun getArtwork(): OnboardingArtwork = loadedCardArtwork - - override fun getBalance(): OnboardingWalletBalance = loadedBalance - - override fun getToUpUrl(): String? { - val wallet = walletManager?.wallet ?: return null - val config = store.state.globalState.configManager?.config ?: return null - val defaultAddress = wallet.address - - return TradeCryptoHelper.getUrl( - TradeCryptoHelper.Action.Buy, - wallet.blockchain, - wallet.blockchain.currency, - defaultAddress, - config.moonPayApiKey, - config.moonPayApiSecretKey - ) - } - - override fun getAddressData(): AddressData? { - val wallet = walletManager?.wallet ?: return null - - val addressDataList = wallet.createAddressesData() - return if (addressDataList.isEmpty()) null - else addressDataList[0] - } - - protected suspend fun loadCardArtwork(card: Card): Artwork? { - return when (val cardInfoResult = OnlineCardVerifier().getCardInfo(card.cardId, card.cardPublicKey)) { - is Result.Success -> { - val artworkUrl = card.getArtworkUrl(cardInfoResult.data.artwork?.id) ?: Artwork.DEFAULT_IMG_URL - loadBitmap(artworkUrl) - } - is Result.Failure -> { - loadBitmap(Artwork.DEFAULT_IMG_URL) - } - } - } - - override suspend fun updateBalance(): OnboardingWalletBalance { - withMainContext { loadedBalance = OnboardingWalletBalance.loading(loadedBalance.value) } - - val walletManager = walletManager ?: walletManagerFactory.makePrimaryWalletManager(scanResponse).guard { - val customError = TapError.CustomError("Loading cancelled. Cause: wallet manager didn't created") - withMainContext { loadedBalance = OnboardingWalletBalance.error(customError) } - return loadedBalance - } - - this.walletManager = walletManager - val currency = Currency.Blockchain(walletManager.wallet.blockchain) - val updatedBalance = when (val result = walletManager.safeUpdate()) { - is Result.Success -> { - val wallet = walletManager.wallet - val valueOfAmount = wallet.amounts[AmountType.Coin]?.value - if (valueOfAmount == null) { - val customError = TapError.CustomError("Amount is NULL") - OnboardingWalletBalance.criticalError(customError) - } else { - val balance = if (valueOfAmount.isZero()) { - OnboardingWalletBalance.done(valueOfAmount, hasForIncomingTransactions(wallet), currency) - } else { - OnboardingWalletBalance.done(valueOfAmount, false, currency) - } - balance - } - } - is Result.Failure -> { - val error = (result.error as? TapError) ?: TapError.UnknownError - when (error) { - is TapError.WalletManagerUpdate.NoAccountError -> OnboardingWalletBalance.error(error) -// NoInternetConnection, WalletManagerUpdate.InternalError - else -> OnboardingWalletBalance.criticalError(error) - } - } - } - withMainContext { loadedBalance = updatedBalance } - return loadedBalance - } - - override fun activationStarted() { - cardInfoStorage.activationStarted(scanResponse.card.cardId) - } - - override fun activationFinished() { - cardInfoStorage.activationFinished(scanResponse.card.cardId) - } - - protected open suspend fun hasForIncomingTransactions(wallet: Wallet): Boolean { - return wallet.recentTransactions.isNotEmpty() - } - - protected open fun tryToProceed() { - if (isProceeded) return - - loadedBalance.criticalError?.let { - sendError(it) - return - } - if (!isReadyToProceed()) return - - isProceeded = true - onInitializationProgress?.invoke(ProgressState.Done) - onInitialized?.invoke(navigateToScreen()) - } - - protected open fun isReadyToProceed(): Boolean { - return loadedBalance.state != ProgressState.Loading && loadedCardArtwork.state != ProgressState.Loading - } - - protected fun sendError(error: TapError) { - onInitializationProgress?.invoke(ProgressState.Error) - onError?.invoke(error) - } - - protected abstract fun navigateToScreen(): AppScreen - - private suspend fun loadBitmap(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 -> null - } - continuation.resume(result) - } - } - } - } - - private fun coroutineExceptionHandler(): CoroutineExceptionHandler = CoroutineExceptionHandler { _, throwable -> - StringWriter().apply { - throwable.printStackTrace(PrintWriter(this)) - Log.e("Coroutine", this.toString()) - store.dispatchDebugErrorNotification(this.toString()) - } - } -} - -data class OnboardingArtwork( - val value: Artwork? = null, - val state: ProgressState -) { - companion object { - // balance was not loaded at all when any error occurred - fun error(): OnboardingArtwork = OnboardingArtwork(state = ProgressState.Error) - - // balance is loading - fun loading(): OnboardingArtwork = OnboardingArtwork(state = ProgressState.Loading) - - // balance was loaded - fun done(artwork: Artwork): OnboardingArtwork = OnboardingArtwork(artwork, ProgressState.Done) - } -} - -data class OnboardingWalletBalance( - val value: BigDecimal = BigDecimal.ZERO, - val currency: Currency.Blockchain = Currency.Blockchain(Blockchain.Unknown), - val hasIncomingTransaction: Boolean = false, - val state: ProgressState, - val error: TapError? = null, - val criticalError: TapError? = null, -) { - - val amountToCreateAccount: String? - get() = if (error is TapError.WalletManagerUpdate.NoAccountError) error.customMessage else null - - companion object { - fun error(error: TapError): OnboardingWalletBalance = OnboardingWalletBalance( - state = ProgressState.Error, - error = error - ) - - fun criticalError(error: TapError): OnboardingWalletBalance = OnboardingWalletBalance( - state = ProgressState.Error, - criticalError = error - ) - - fun loading(value: BigDecimal = BigDecimal.ZERO): OnboardingWalletBalance = OnboardingWalletBalance( - value, - state = ProgressState.Loading - ) - - fun done(value: BigDecimal, hasTransactions: Boolean, currency: Currency.Blockchain): OnboardingWalletBalance = - OnboardingWalletBalance(value, currency, hasTransactions, ProgressState.Done) - } -} - -fun ProductOnboardingService.balanceIsToppedUp(): Boolean { - val balance = getBalance() - return balance.value.isPositive() || balance.hasIncomingTransaction -} \ No newline at end of file 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 51d6c3740d..55e7fb4f66 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 @@ -151,7 +151,7 @@ sealed class WalletAction : Action { object ShowOnboarding : TwinsAction() object SetOnboardingShown : TwinsAction() data class SetTwinCard( - val secondCardId: String, val number: TwinCardNumber, + val secondCardId: String?, val number: TwinCardNumber, val isCreatingTwinCardsAllowed: Boolean, ) : TwinsAction() } diff --git a/app/src/main/res/layout/layout_onboarding_note.xml b/app/src/main/res/layout/layout_onboarding_note.xml index e2cb262a46..90cfd95782 100644 --- a/app/src/main/res/layout/layout_onboarding_note.xml +++ b/app/src/main/res/layout/layout_onboarding_note.xml @@ -39,7 +39,7 @@ android:layout_marginStart="32dp" android:layout_marginTop="106dp" android:layout_marginEnd="32dp" - android:src="@drawable/card_placeholder" + android:src="@drawable/ic_home_front_card" android:transitionName="imv_front_card" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.495" diff --git a/app/src/main/res/layout/lp_onboarding_create_wallet.xml b/app/src/main/res/layout/lp_onboarding_create_wallet.xml index 1a45daa73c..076cf8c8ec 100644 --- a/app/src/main/res/layout/lp_onboarding_create_wallet.xml +++ b/app/src/main/res/layout/lp_onboarding_create_wallet.xml @@ -39,7 +39,7 @@ android:layout_marginStart="32dp" android:layout_marginTop="106dp" android:layout_marginEnd="32dp" - android:src="@drawable/card_placeholder" + android:src="@drawable/ic_home_front_card" android:transitionName="imv_front_card" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.495" diff --git a/app/src/main/res/layout/lp_onboarding_done_activation.xml b/app/src/main/res/layout/lp_onboarding_done_activation.xml index a3e9722587..e476f28e3d 100644 --- a/app/src/main/res/layout/lp_onboarding_done_activation.xml +++ b/app/src/main/res/layout/lp_onboarding_done_activation.xml @@ -38,7 +38,7 @@ android:layout_width="140dp" android:layout_height="88dp" android:scaleType="fitXY" - android:src="@drawable/card_placeholder" + android:src="@drawable/ic_home_front_card" android:transitionName="imv_front_card" android:translationY="-30dp" android:visibility="visible" diff --git a/app/src/main/res/layout/lp_onboarding_topup_wallet.xml b/app/src/main/res/layout/lp_onboarding_topup_wallet.xml index 2783b41196..d0261db37a 100644 --- a/app/src/main/res/layout/lp_onboarding_topup_wallet.xml +++ b/app/src/main/res/layout/lp_onboarding_topup_wallet.xml @@ -38,7 +38,7 @@ android:layout_width="140dp" android:layout_height="88dp" android:scaleType="fitXY" - android:src="@drawable/card_placeholder" + android:src="@drawable/ic_home_front_card" android:transitionName="imv_front_card" android:translationY="-30dp" android:visibility="visible" From 2af5f84f123221c89f05cb64aa5db5972016e8d7 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sun, 17 Oct 2021 23:07:28 +0300 Subject: [PATCH 073/189] Updated on 2026-08-14 --- .../onboarding/products/note/redux/OnboardingNoteMiddleware.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt index e63b32b9b4..0a925dc7ee 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt @@ -16,6 +16,7 @@ import com.tangem.tap.features.wallet.redux.ProgressState import com.tangem.tap.scope import com.tangem.tap.store import com.tangem.tap.tangemSdkManager +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import org.rekotlin.Action import org.rekotlin.DispatchFunction @@ -104,6 +105,7 @@ private fun handleNoteAction(action: Action, dispatch: DispatchFunction) { walletManager } + val isLoadedBefore = noteState.walletBalance.state != ProgressState.Loading val balanceIsLoading = noteState.walletBalance.copy( state = ProgressState.Loading, error = null, @@ -114,6 +116,7 @@ private fun handleNoteAction(action: Action, dispatch: DispatchFunction) { scope.launch { val loadedBalance = onboardingManager.updateBalance(walletManager) loadedBalance.criticalError?.let { store.dispatchErrorNotification(it) } + delay(if (isLoadedBefore) 0 else 300) withMainContext { store.dispatch(OnboardingNoteAction.Balance.Set(loadedBalance)) } } From c6a201f5571ce81f21d45b91f7ef9ef4f0e124de Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 18 Oct 2021 19:46:29 +0300 Subject: [PATCH 074/189] Updated on 2026-08-14 --- .../walletconnect/WalletConnectMiddleware.kt | 64 +++++++------------ .../tangem/tap/features/home/HomeFragment.kt | 2 +- .../tap/features/home/redux/HomeAction.kt | 2 + .../tap/features/home/redux/HomeMiddleware.kt | 7 ++ .../tap/features/home/redux/HomeReducer.kt | 4 +- .../tap/features/home/redux/HomeState.kt | 2 +- .../redux/middlewares/WalletMiddleware.kt | 16 +---- 7 files changed, 39 insertions(+), 58 deletions(-) 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 d316ca59ec..dff6c35c72 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,11 +1,10 @@ package com.tangem.tap.features.details.redux.walletconnect import com.tangem.blockchain.common.* -import com.tangem.common.CompletionResult import com.tangem.common.card.Card +import com.tangem.common.extensions.guard import com.tangem.common.extensions.toHexString import com.tangem.tap.* -import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.getFromClipboard import com.tangem.tap.common.redux.AppState @@ -18,9 +17,6 @@ import com.tangem.tap.domain.isMultiwalletAllowed import com.tangem.tap.domain.walletconnect.WalletConnectManager import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.wallet.R -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import org.rekotlin.Middleware class WalletConnectMiddleware { @@ -132,43 +128,29 @@ class WalletConnectMiddleware { } private fun handleScanCard(wcUri: String) { - scope.launch { - val result = tangemSdkManager.scanProduct( - FirebaseAnalyticsHandler, - R.string.wallet_connect_scan_card_message - ) - withContext(Dispatchers.Main) { - when (result) { - is CompletionResult.Success -> { - val card = result.data.card - - if (!card.isMultiwalletAllowed) { - store.dispatchOnMain(WalletConnectAction.UnsupportedCard) - return@withContext - } - - val walletManager = getWalletManager(card) - if (walletManager == null) { - store.dispatchOnMain(WalletConnectAction.UnsupportedCard) - return@withContext - } - - val key = walletManager.wallet.publicKey - store.dispatchOnMain(WalletConnectAction.OpenSession( - wcUri = wcUri, - wallet = WalletForSession( - card.cardId, key.toHexString(), - isTestNet = card.isTestCard - ), - )) - } - is CompletionResult.Failure -> - store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession( - null - )) - } + store.dispatch(GlobalAction.ScanCard({ scanResponse -> + val card = scanResponse.card + if (!card.isMultiwalletAllowed) { + store.dispatchOnMain(WalletConnectAction.UnsupportedCard) + return@ScanCard } - } + + val walletManager = getWalletManager(card).guard { + store.dispatchOnMain(WalletConnectAction.UnsupportedCard) + return@ScanCard + } + + val key = walletManager.wallet.publicKey + store.dispatchOnMain(WalletConnectAction.OpenSession( + wcUri = wcUri, + wallet = WalletForSession( + card.cardId, key.toHexString(), + isTestNet = card.isTestCard + ), + )) + }, { + store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession(null)) + }, R.string.wallet_connect_scan_card_message)) } private fun getWalletManager(card: Card): WalletManager? { diff --git a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt index 71e0b7a83c..314a302527 100644 --- a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt @@ -27,6 +27,7 @@ class HomeFragment : Fragment(R.layout.fragment_home), StoreSubscriber = { dispatch, state -> store.dispatch(GlobalAction.GetMoonPayUserStatus) store.dispatch(HomeAction.SetTermsOfUseState(preferencesStorage.wasDisclaimerAccepted())) } + is HomeAction.ShouldScanCardOnResume -> { + if (action.shouldScanCard) { + store.dispatch(HomeAction.ShouldScanCardOnResume(false)) + postUi(700) { store.dispatch(HomeAction.ReadCard) } + } + } is HomeAction.ReadCard -> handleReadCard() is HomeAction.GoToShop -> store.dispatchOpenUrl(HomeMiddleware.CARD_SHOP_URI) } diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt index 0b9b044622..d4f11f31f9 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt @@ -18,8 +18,8 @@ private fun internalReduce(action: Action, state: AppState): HomeState { is HomeAction.SetFragmentShareTransition -> { state = state.copy(shareTransition = action.shareTransition) } - is HomeAction.SetTermsOfUseState -> { - state = state.copy(isDisclaimerAccepted = action.isDisclaimerAccepted) + is HomeAction.ShouldScanCardOnResume -> { + state = state.copy(shouldScanCardOnResume = action.shouldScanCard) } is HomeAction.ChangeScanCardButtonState -> { state = state.copy(btnScanState = action.state) diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt index 29a7825aae..8c9949cf4c 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt @@ -6,7 +6,7 @@ import com.tangem.tap.features.send.redux.states.ButtonState import org.rekotlin.StateType data class HomeState( - val isDisclaimerAccepted: Boolean = false, + val shouldScanCardOnResume: Boolean = false, val shareTransition: FragmentShareTransition? = null, val btnScanState: IndeterminateProgressButton = IndeterminateProgressButton(ButtonState.ENABLED), ) : StateType \ No newline at end of file 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 ea654a6777..3b3a4f31c1 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 @@ -22,10 +22,10 @@ import com.tangem.tap.domain.extensions.toSendableAmounts import com.tangem.tap.domain.twins.TwinsHelper import com.tangem.tap.features.details.redux.DetailsAction import com.tangem.tap.features.details.redux.twins.CreateTwinWallet +import com.tangem.tap.features.home.redux.HomeAction import com.tangem.tap.features.send.redux.PrepareSendScreen import com.tangem.tap.features.wallet.redux.* import com.tangem.tap.network.NetworkStateChanged -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.rekotlin.Action import org.rekotlin.Middleware @@ -145,18 +145,8 @@ class WalletMiddleware { } is WalletAction.Scan -> { - scope.launch { - val result = tangemSdkManager.scanProduct(FirebaseAnalyticsHandler) - scope.launch(Dispatchers.Main) { - store.dispatch(GlobalAction.ScanFailsCounter.ChooseBehavior(result)) - when (result) { - is CompletionResult.Success -> { - tangemSdkManager.changeDisplayedCardIdNumbersCount(result.data.card) - globalState?.tapWalletManager?.onCardScanned(result.data, true) - } - } - } - } + store.dispatch(HomeAction.ShouldScanCardOnResume(true)) + store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) } is WalletAction.LoadCardInfo -> { scope.launch { From 5dcb39b560e0dcf0a68045d147fe47fb09608076 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 18 Oct 2021 21:44:06 +0300 Subject: [PATCH 075/189] Updated on 2026-08-14 --- app/src/main/java/com/tangem/tap/MainActivity.kt | 10 ++++++++-- app/src/main/res/values/colors.xml | 4 +++- app/src/main/res/values/styles.xml | 8 ++++---- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index 41b89b88e3..6f99403572 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -59,8 +59,8 @@ class MainActivity : AppCompatActivity(), SnackbarHandler { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) + systemActions() store.state.globalState.feedbackManager?.updateAcivity(this) - requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; store.dispatch(NavigationAction.ActivityCreated(WeakReference(this))) tangemSdk = TangemSdk.init(this, TangemSdkManager.config) @@ -69,6 +69,12 @@ class MainActivity : AppCompatActivity(), SnackbarHandler { store.dispatch(WalletConnectAction.RestoreSessions) } + private fun systemActions() { + // makes the status bar text dark + window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR + requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + } + override fun onResume() { super.onResume() notificationsHandler = NotificationsHandler(fragment_container) @@ -107,7 +113,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler { if (snackbar != null) return snackbar = Snackbar.make( - fragment_container, getString(text), Snackbar.LENGTH_INDEFINITE + fragment_container, getString(text), Snackbar.LENGTH_INDEFINITE ) if (buttonTitle != null && action != null) { snackbar?.setAction(getString(buttonTitle), action) diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index b4334ef500..5d0fe290b1 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -34,9 +34,11 @@ #F3F3F3 #F8F8FB + #DADADF + #D0D0D5 #C7C7CC #D1D1D6 - #F2F2F7 + #C9C9CD #1F000000 #14212121 diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 703a685858..65e33f9906 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -6,10 +6,10 @@ @color/colorSecondary @color/darkGray6 @color/backgroundLightGray - - - - + false + true + true + @null - + + + + + + - \ No newline at end of file + + + + + + + + \ No newline at end of file From 5ec60f0cce8ebf4ff7114ab945d4861aa97a3b70 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 21 Oct 2021 19:19:59 +0300 Subject: [PATCH 078/189] Updated on 2026-08-14 --- .../java/com/tangem/tap/domain/ProductType.kt | 2 +- .../tangem/tap/domain/tasks/ScanNoteTask.kt | 4 +-- .../CreateProductWalletAndRescanTask.kt | 2 +- .../domain/tasks/product/ScanProductTask.kt | 26 +++++++++++-------- .../tap/domain/twins/TwinCardsManager.kt | 12 ++------- .../tangem/tap/domain/twins/TwinsHelper.kt | 9 +++++++ .../features/onboarding/OnboardingHelper.kt | 20 +++++++++++--- 7 files changed, 47 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/domain/ProductType.kt b/app/src/main/java/com/tangem/tap/domain/ProductType.kt index 02f0246c82..07cbea8905 100644 --- a/app/src/main/java/com/tangem/tap/domain/ProductType.kt +++ b/app/src/main/java/com/tangem/tap/domain/ProductType.kt @@ -4,5 +4,5 @@ package com.tangem.tap.domain [REDACTED_AUTHOR] */ enum class ProductType { - Note, Wallet, Twin, Other + Note, Wallet, Twins, Other } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt index b2b415dc15..3b01ae1c98 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt @@ -16,7 +16,7 @@ import com.tangem.tap.domain.TapWorkarounds.isExcluded import com.tangem.tap.domain.TapWorkarounds.isTangemNote import com.tangem.tap.domain.extensions.getSingleWallet import com.tangem.tap.domain.tasks.product.ScanResponse -import com.tangem.tap.domain.twins.TwinCardsManager +import com.tangem.tap.domain.twins.TwinsHelper import com.tangem.tap.domain.twins.isTangemTwin class ScanNoteTask(val card: Card? = null) : CardSessionRunnable { @@ -92,7 +92,7 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable { callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, null))) return@run } - val verified = TwinCardsManager.verifyTwinPublicKey( + val verified = TwinsHelper.verifyTwinPublicKey( readDataResult.data.issuerData, publicKey ) if (verified) { diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletAndRescanTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletAndRescanTask.kt index 3b3c6ea2ae..92beb01238 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletAndRescanTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletAndRescanTask.kt @@ -45,7 +45,7 @@ private class CreateProductWalletTask( override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { val commandProcessor = when (type) { ProductType.Note -> CreateWalletTangemNote() - ProductType.Twin -> throw UnsupportedOperationException("Use the TwinCardsManager to create a wallet") + ProductType.Twins -> throw UnsupportedOperationException("Use the TwinCardsManager to create a wallet") ProductType.Wallet -> CreateWalletTangemWallet() else -> CreateWalletOtherCards() } 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 cdb63d456b..34f8f201f7 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 @@ -24,7 +24,7 @@ import com.tangem.tap.domain.TapWorkarounds.isExcluded import com.tangem.tap.domain.TapWorkarounds.isTangemNote import com.tangem.tap.domain.TapWorkarounds.isTangemWallet import com.tangem.tap.domain.extensions.getSingleWallet -import com.tangem.tap.domain.twins.TwinCardsManager +import com.tangem.tap.domain.twins.TwinsHelper import com.tangem.tap.domain.twins.isTangemTwin data class ScanResponse( @@ -43,13 +43,17 @@ data class ScanResponse( fun getPrimaryToken(): Token? { val cardToken = walletData?.token ?: return null return Token( - cardToken.name, - cardToken.symbol, - cardToken.contractAddress, - cardToken.decimals, - Blockchain.fromId(walletData.blockchain) + cardToken.name, + cardToken.symbol, + cardToken.contractAddress, + cardToken.decimals, + Blockchain.fromId(walletData.blockchain) ) } + + fun twinsIsTwinned(): Boolean { + return walletData != null && secondTwinPublicKey != null + } } class ScanProductTask(val card: Card? = null) : CardSessionRunnable { @@ -120,21 +124,21 @@ private class ScanTwinProcessor : ProductCommandProcessor { is CompletionResult.Success -> { val publicKey = card.getSingleWallet()?.publicKey if (publicKey == null) { - callback(CompletionResult.Success(ScanResponse(card, ProductType.Twin, null))) + callback(CompletionResult.Success(ScanResponse(card, ProductType.Twins, null))) return@run } - val verified = TwinCardsManager.verifyTwinPublicKey(readDataResult.data.issuerData, publicKey) + val verified = TwinsHelper.verifyTwinPublicKey(readDataResult.data.issuerData, publicKey) if (verified) { val twinPublicKey = readDataResult.data.issuerData.sliceArray(0 until 65) val walletData = session.environment.walletData - val response = ScanResponse(card, ProductType.Twin, walletData, twinPublicKey.toHexString()) + val response = ScanResponse(card, ProductType.Twins, walletData, twinPublicKey.toHexString()) callback(CompletionResult.Success(response)) } else { - callback(CompletionResult.Success(ScanResponse(card, ProductType.Twin, null))) + callback(CompletionResult.Success(ScanResponse(card, ProductType.Twins, null))) } } is CompletionResult.Failure -> - callback(CompletionResult.Success(ScanResponse(card, ProductType.Twin, null))) + callback(CompletionResult.Success(ScanResponse(card, ProductType.Twins, null))) } } } 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 d2d8aa143c..df48e76971 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,7 +11,6 @@ import com.tangem.common.KeyPair 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.tap.common.analytics.FirebaseAnalyticsHandler import com.tangem.tap.common.extensions.readAssetAsString import com.tangem.tap.domain.tasks.product.ScanResponse @@ -21,7 +20,8 @@ import com.tangem.tap.tangemSdkManager class TwinCardsManager(private val scanResponse: ScanResponse, context: Context) { private val currentCardId: String = scanResponse.card.cardId -// private val secondCardId: String? = TwinsHelper.getTwinsCardId(currentCardId) + + // private val secondCardId: String? = TwinsHelper.getTwinsCardId(currentCardId) private val secondCardId: String? = null private var currentCardPublicKey: String? = null @@ -108,14 +108,6 @@ class TwinCardsManager(private val scanResponse: ScanResponse, context: Context) } companion object { - fun verifyTwinPublicKey(issuerData: ByteArray, cardWalletPublicKey: ByteArray?): Boolean { - if (issuerData.size < 65 || cardWalletPublicKey == null) return false - - val publicKey = issuerData.sliceArray(0 until 65) - val signedKey = issuerData.sliceArray(65 until issuerData.size) - return CryptoUtils.verify(cardWalletPublicKey, publicKey, signedKey) - } - private fun getIssuerKeys(context: Context, publicKey: String): KeyPair { val issuer = getIssuers(context).first { it.publicKey == publicKey } return KeyPair( diff --git a/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt b/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt index 65c4d7cb53..89affa15d1 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt @@ -1,11 +1,20 @@ package com.tangem.tap.domain.twins import com.tangem.common.card.Card +import com.tangem.crypto.CryptoUtils class TwinsHelper { companion object { const val TWIN_FILE_NAME = "TwinPublicKey" + fun verifyTwinPublicKey(issuerData: ByteArray, cardWalletPublicKey: ByteArray?): Boolean { + if (issuerData.size < 65 || cardWalletPublicKey == null) return false + + val publicKey = issuerData.sliceArray(0 until 65) + val signedKey = issuerData.sliceArray(65 until issuerData.size) + return CryptoUtils.verify(cardWalletPublicKey, publicKey, signedKey) + } + fun getTwinCardNumber(cardId: String): TwinCardNumber? { return when { firstCardSeries.map { cardId.startsWith(it) }.contains(true) -> { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt index 38b2517ad1..6a097f6511 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt @@ -12,14 +12,28 @@ import com.tangem.tap.preferencesStorage class OnboardingHelper { companion object { - fun isOnboardingCase(response: ScanResponse): Boolean = if (response.card.hasWallets()) - preferencesStorage.usedCardsPrefStorage.activationIsStarted(response.card.cardId) else true + fun isOnboardingCase(response: ScanResponse): Boolean { + val cardInfoStorage = preferencesStorage.usedCardsPrefStorage + return when { + response.productType == ProductType.Twins -> { + if (!response.twinsIsTwinned()) { + true + } else { + cardInfoStorage.activationIsStarted(response.card.cardId) + } + } + response.card.hasWallets() -> { + cardInfoStorage.activationIsStarted(response.card.cardId) + } + else -> true + } + } fun whereToNavigate(scanResponse: ScanResponse): AppScreen { return when (scanResponse.productType) { ProductType.Note -> AppScreen.OnboardingNote ProductType.Wallet -> AppScreen.OnboardingWallet - ProductType.Twin -> AppScreen.OnboardingTwins + ProductType.Twins -> AppScreen.OnboardingTwins ProductType.Other -> AppScreen.OnboardingOther } } From 7379f3555a965c7f5d4898b1689af1ab6dd58bab Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 21 Oct 2021 20:42:12 +0300 Subject: [PATCH 079/189] Updated on 2026-08-14 --- .../tap/common/extensions/Navigation.kt | 16 +- .../com/tangem/tap/common/redux/AppState.kt | 26 ++-- .../com/tangem/tap/domain/TapWalletManager.kt | 26 ++-- .../features/details/redux/DetailsAction.kt | 42 ------ .../details/redux/DetailsMiddleware.kt | 5 +- .../features/details/redux/DetailsReducer.kt | 61 ++++---- .../features/details/redux/DetailsState.kt | 40 ++--- .../redux/twins/CreateTwinWalletMiddleware.kt | 119 --------------- .../redux/twins/CreateTwinWalletReducer.kt | 68 --------- .../features/details/ui/DetailsFragment.kt | 13 +- .../twins}/OnboardingTwinsFragment.kt | 21 ++- .../features/tokens/ui/AddTokensFragment.kt | 1 - .../twins/redux/CreateTwinWalletAction.kt | 52 +++++++ .../twins/redux/CreateTwinWalletMiddleware.kt | 138 ++++++++++++++++++ .../twins/redux/CreateTwinWalletReducer.kt | 67 +++++++++ .../redux}/CreateTwinWalletState.kt | 2 +- .../ui}/CreateTwinWalletFragment.kt | 40 ++--- .../ui}/TwinWalletWarningFragment.kt | 15 +- .../redux/middlewares/WalletMiddleware.kt | 35 ++--- .../wallet/ui/WalletDetailsFragment.kt | 30 ++-- ...rds.xml => fragment_twin_cards_create.xml} | 0 ...ng.xml => fragment_twin_cards_warning.xml} | 6 +- ...ds.xml => fragment_twin_cards_welcome.xml} | 8 +- 23 files changed, 429 insertions(+), 402 deletions(-) delete mode 100644 app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletReducer.kt rename app/src/main/java/com/tangem/tap/features/{wallet/ui => onboarding/products/twins}/OnboardingTwinsFragment.kt (74%) create mode 100644 app/src/main/java/com/tangem/tap/features/twins/redux/CreateTwinWalletAction.kt create mode 100644 app/src/main/java/com/tangem/tap/features/twins/redux/CreateTwinWalletMiddleware.kt create mode 100644 app/src/main/java/com/tangem/tap/features/twins/redux/CreateTwinWalletReducer.kt rename app/src/main/java/com/tangem/tap/features/{details/redux/twins => twins/redux}/CreateTwinWalletState.kt (91%) rename app/src/main/java/com/tangem/tap/features/{details/ui/twins => twins/ui}/CreateTwinWalletFragment.kt (79%) rename app/src/main/java/com/tangem/tap/features/{details/ui/twins => twins/ui}/TwinWalletWarningFragment.kt (81%) rename app/src/main/res/layout/{fragment_details_twin_cards.xml => fragment_twin_cards_create.xml} (100%) rename app/src/main/res/layout/{fragment_details_twin_cards_warning.xml => fragment_twin_cards_warning.xml} (97%) rename app/src/main/res/layout/{fragment_twin_cards.xml => fragment_twin_cards_welcome.xml} (95%) diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt index 02c794329c..135e0cbd9c 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt @@ -10,26 +10,26 @@ import com.tangem.tap.common.redux.navigation.FragmentShareTransition import com.tangem.tap.features.details.ui.DetailsConfirmFragment import com.tangem.tap.features.details.ui.DetailsFragment import com.tangem.tap.features.details.ui.DetailsSecurityFragment -import com.tangem.tap.features.details.ui.twins.CreateTwinWalletFragment -import com.tangem.tap.features.details.ui.twins.TwinWalletWarningFragment import com.tangem.tap.features.details.ui.walletconnect.QrScanFragment import com.tangem.tap.features.details.ui.walletconnect.WalletConnectSessionsFragment import com.tangem.tap.features.disclaimer.ui.DisclaimerFragment import com.tangem.tap.features.home.HomeFragment import com.tangem.tap.features.onboarding.products.note.OnboardingNoteFragment import com.tangem.tap.features.onboarding.products.otherCards.OnboardingOtherCardsFragment +import com.tangem.tap.features.onboarding.products.twins.OnboardingTwinsFragment import com.tangem.tap.features.onboarding.products.wallet.OnboardingWalletFragment import com.tangem.tap.features.send.ui.SendFragment import com.tangem.tap.features.tokens.ui.AddTokensFragment -import com.tangem.tap.features.wallet.ui.OnboardingTwinsFragment +import com.tangem.tap.features.twins.ui.CreateTwinWalletFragment +import com.tangem.tap.features.twins.ui.TwinWalletWarningFragment import com.tangem.tap.features.wallet.ui.WalletDetailsFragment import com.tangem.tap.features.wallet.ui.WalletFragment import com.tangem.wallet.R fun FragmentActivity.openFragment( - screen: AppScreen, - addToBackstack: Boolean, - fgShareTransition: FragmentShareTransition? = null + screen: AppScreen, + addToBackstack: Boolean, + fgShareTransition: FragmentShareTransition? = null ) { val transaction = this.supportFragmentManager.beginTransaction() val fragment = fragmentFactory(screen) @@ -60,8 +60,8 @@ fun FragmentActivity.getPreviousScreen(): AppScreen? { } fun FragmentActivity.addOnBackPressedDispatcher( - isEnabled: Boolean = true, - onBackPressed: VoidCallback + isEnabled: Boolean = true, + onBackPressed: VoidCallback ): OnBackPressedCallback = (object : OnBackPressedCallback(isEnabled) { override fun handleOnBackPressed() { onBackPressed() diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt index 40e0c80077..f883d78840 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt @@ -22,6 +22,7 @@ import com.tangem.tap.features.send.redux.middlewares.SendMiddleware import com.tangem.tap.features.send.redux.states.SendState import com.tangem.tap.features.tokens.redux.TokensMiddleware import com.tangem.tap.features.tokens.redux.TokensState +import com.tangem.tap.features.twins.redux.CreateTwinWalletMiddleware import com.tangem.tap.features.wallet.redux.WalletState import com.tangem.tap.features.wallet.redux.middlewares.WalletMiddleware import org.rekotlin.Middleware @@ -45,18 +46,19 @@ data class AppState( companion object { fun getMiddleware(): List> { return listOf( - logMiddleware, navigationMiddleware, notificationsMiddleware, - GlobalMiddleware.handler, - HomeMiddleware.handler, - OnboardingNoteMiddleware.handler, - OnboardingWalletMiddleware.handler, - OnboardingOtherCardsMiddleware.handler, - WalletMiddleware().walletMiddleware, - SendMiddleware().sendMiddleware, - DetailsMiddleware().detailsMiddleware, - DisclaimerMiddleware().disclaimerMiddleware, - TokensMiddleware().tokensMiddleware, - WalletConnectMiddleware().walletConnectMiddleware + logMiddleware, navigationMiddleware, notificationsMiddleware, + GlobalMiddleware.handler, + HomeMiddleware.handler, + OnboardingNoteMiddleware.handler, + OnboardingWalletMiddleware.handler, + OnboardingOtherCardsMiddleware.handler, + WalletMiddleware().walletMiddleware, + CreateTwinWalletMiddleware.handler, + SendMiddleware().sendMiddleware, + DetailsMiddleware().detailsMiddleware, + DisclaimerMiddleware().disclaimerMiddleware, + TokensMiddleware().tokensMiddleware, + WalletConnectMiddleware().walletConnectMiddleware ) } } 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 32403ae363..163359460d 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -59,8 +59,8 @@ class TapWalletManager { suspend fun loadFiatRate(fiatCurrency: FiatCurrencyName, wallet: Wallet) { Timber.d(wallet.getTokens().toString()) val currencies = wallet.getTokens() - .map { Currency.Token(it) } - .plus(Currency.Blockchain(wallet.blockchain)) + .map { Currency.Token(it) } + .plus(Currency.Blockchain(wallet.blockchain)) loadFiatRate(fiatCurrency, currencies) } @@ -126,11 +126,13 @@ class TapWalletManager { store.dispatch(WalletAction.LoadCardInfo(data.card)) when { + data.card.isTangemTwin() && data.secondTwinPublicKey == null -> { + store.dispatch(WalletAction.EmptyWallet) + } data.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> { store.dispatch(WalletAction.LoadData.Failure(TapError.UnknownBlockchain)) } - data.card.wallets.isEmpty() || - (data.card.isTangemTwin() && data.secondTwinPublicKey == null) -> { + data.card.wallets.isEmpty() -> { store.dispatch(WalletAction.EmptyWallet) } else -> { @@ -171,7 +173,7 @@ class TapWalletManager { } private fun loadMultiWalletData( - card: Card, primaryBlockchain: Blockchain?, primaryWalletManager: WalletManager? + card: Card, primaryBlockchain: Blockchain?, primaryWalletManager: WalletManager? ) { val primaryTokens = primaryWalletManager?.cardTokens ?: emptySet() val savedCurrencies = currenciesRepository.loadCardCurrencies(card.cardId) @@ -179,9 +181,9 @@ class TapWalletManager { if (savedCurrencies == null) { if (primaryBlockchain != null && primaryWalletManager != null) { store.dispatch(WalletAction.MultiWallet.SaveCurrencies( - CardCurrencies( - blockchains = setOf(primaryBlockchain), tokens = primaryTokens - ))) + CardCurrencies( + blockchains = setOf(primaryBlockchain), tokens = primaryTokens + ))) store.dispatch(WalletAction.MultiWallet.AddWalletManagers(primaryWalletManager)) store.dispatch(WalletAction.MultiWallet.AddBlockchains(listOf(primaryBlockchain))) store.dispatch(WalletAction.MultiWallet.AddTokens(primaryTokens.toList())) @@ -191,7 +193,7 @@ class TapWalletManager { CardCurrencies(blockchains = blockchains, tokens = emptySet()) )) val walletManagers = - walletManagerFactory.makeWalletManagersForApp(card, blockchains.toList()) + walletManagerFactory.makeWalletManagersForApp(card, blockchains.toList()) store.dispatch(WalletAction.MultiWallet.AddWalletManagers(walletManagers)) store.dispatch(WalletAction.MultiWallet.AddBlockchains(blockchains.toList())) } @@ -200,12 +202,12 @@ class TapWalletManager { } else { val blockchains = savedCurrencies.blockchains.toList() val walletManagers = if ( - primaryTokens.isNotEmpty() && - primaryWalletManager != null && primaryBlockchain != null + primaryTokens.isNotEmpty() && + primaryWalletManager != null && primaryBlockchain != null ) { val blockchainsWithoutPrimary = blockchains.filterNot { it == primaryBlockchain } walletManagerFactory.makeWalletManagersForApp(card, blockchainsWithoutPrimary) - .plus(primaryWalletManager) + .plus(primaryWalletManager) } else { walletManagerFactory.makeWalletManagersForApp(card, blockchains) } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt index 74b37f90f4..0e7a1f5e21 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt @@ -1,7 +1,5 @@ package com.tangem.tap.features.details.redux -import android.content.Context -import com.tangem.Message import com.tangem.blockchain.common.Wallet import com.tangem.common.card.Card import com.tangem.operations.pins.CheckUserCodesResponse @@ -9,8 +7,6 @@ import com.tangem.tap.common.redux.NotificationAction import com.tangem.tap.common.redux.global.FiatCurrencyName import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.domain.termsOfUse.CardTou -import com.tangem.tap.domain.twins.TwinCardNumber -import com.tangem.tap.features.details.redux.twins.CreateTwinWallet import com.tangem.tap.network.coinmarketcap.FiatCurrency import com.tangem.wallet.R import org.rekotlin.Action @@ -48,44 +44,6 @@ sealed class DetailsAction : Action { object Success : EraseWallet() } - sealed class CreateTwinWalletAction : DetailsAction() { - data class ShowWarning( - val twinCardNumber: TwinCardNumber?, - val createTwinWallet: CreateTwinWallet = CreateTwinWallet.RecreateWallet - ) : CreateTwinWalletAction() - object NotEmpty : CreateTwinWalletAction(), NotificationAction { - override val messageResource = R.string.details_notification_erase_wallet_not_possible - } - object ShowAlert : CreateTwinWalletAction() - object HideAlert : CreateTwinWalletAction() - object Proceed: CreateTwinWalletAction() - - object Cancel : CreateTwinWalletAction() { - object Confirm : CreateTwinWalletAction() - } - - data class LaunchFirstStep( - val message: Message, val context: Context - ) : CreateTwinWalletAction() { - object Success : CreateTwinWalletAction() - object Failure : CreateTwinWalletAction() - } - - data class LaunchSecondStep( - val initialMessage: Message, - val preparingMessage: Message, - val creatingWalletMessage: Message, - ) : CreateTwinWalletAction() { - object Success : CreateTwinWalletAction() - object Failure : CreateTwinWalletAction() - } - - data class LaunchThirdStep(val message: Message) : CreateTwinWalletAction() { - data class Success(val scanResponse: ScanResponse) : CreateTwinWalletAction() - object Failure : CreateTwinWalletAction() - } - } - sealed class AppCurrencyAction : DetailsAction() { data class SetCurrencies(val currencies: List) : AppCurrencyAction() object ChooseAppCurrency : AppCurrencyAction() 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 15eca00858..b539946faa 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 @@ -9,7 +9,6 @@ import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction -import com.tangem.tap.features.details.redux.twins.CreateTwinWalletMiddleware import com.tangem.tap.features.disclaimer.redux.DisclaimerAction import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.network.coinmarketcap.CoinMarketCapService @@ -26,7 +25,6 @@ class DetailsMiddleware { private val eraseWalletMiddleware = EraseWalletMiddleware() private val appCurrencyMiddleware = AppCurrencyMiddleware() private val manageSecurityMiddleware = ManageSecurityMiddleware() - private val twinWalletMiddleware = CreateTwinWalletMiddleware() val detailsMiddleware: Middleware = { dispatch, state -> { next -> { action -> @@ -35,7 +33,6 @@ class DetailsMiddleware { is DetailsAction.EraseWallet -> eraseWalletMiddleware.handle(action) is DetailsAction.AppCurrencyAction -> appCurrencyMiddleware.handle(action) is DetailsAction.ManageSecurity -> manageSecurityMiddleware.handle(action) - is DetailsAction.CreateTwinWalletAction -> twinWalletMiddleware.handle(action) is DetailsAction.ShowDisclaimer -> { store.dispatch(DisclaimerAction.ShowAcceptedDisclaimer) store.dispatch(NavigationAction.NavigateTo(AppScreen.Disclaimer)) @@ -84,7 +81,7 @@ class DetailsMiddleware { store.dispatch(NavigationAction.PopBackTo()) } is DetailsAction.EraseWallet.Confirm -> { - val card = store.state.detailsState.card ?: return + val card = store.state.detailsState.card ?: return scope.launch { val result = tangemSdkManager.eraseWallet(card) withContext(Dispatchers.Main) { diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt index 4e45d765d7..5989a084e2 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt @@ -9,8 +9,7 @@ import com.tangem.tap.domain.extensions.signedHashesCount import com.tangem.tap.domain.extensions.toSendableAmounts import com.tangem.tap.domain.twins.getTwinCardNumber import com.tangem.tap.domain.twins.isTangemTwin -import com.tangem.tap.features.details.redux.twins.CreateTwinWalletReducer -import com.tangem.tap.features.details.redux.twins.CreateTwinWalletState +import com.tangem.tap.features.twins.redux.CreateTwinWalletState import com.tangem.tap.features.wallet.models.hasPendingTransactions import org.rekotlin.Action import java.util.* @@ -38,9 +37,6 @@ private fun internalReduce(action: Action, state: AppState): DetailsState { is DetailsAction.ManageSecurity -> { handleSecurityAction(action, detailsState) } - is DetailsAction.CreateTwinWalletAction -> { - CreateTwinWalletReducer.handle(action, detailsState) - } else -> detailsState } } @@ -48,24 +44,24 @@ private fun internalReduce(action: Action, state: AppState): DetailsState { private fun handlePrepareScreen(action: DetailsAction.PrepareScreen, state: DetailsState): DetailsState { val twinsState = if (action.card.isTangemTwin()) { CreateTwinWalletState( - scanResponse = action.scanResponse, - twinCardNumber = action.card.getTwinCardNumber(), - createTwinWallet = null, - showAlert = false, - allowRecreatingWallet = action.isCreatingTwinWalletAllowed + scanResponse = action.scanResponse, + twinCardNumber = action.card.getTwinCardNumber(), + createTwinWallet = null, + showAlert = false, + allowRecreatingWallet = action.isCreatingTwinWalletAllowed ) } else { null } return DetailsState( - card = action.card, wallets = action.wallets, - cardInfo = action.card.toCardInfo(), - appCurrencyState = AppCurrencyState( - action.fiatCurrencyName - ), - createTwinWalletState = twinsState, - cardTermsOfUseUrl = action.cardTou.getUrl(action.card) + card = action.card, wallets = action.wallets, + cardInfo = action.card.toCardInfo(), + appCurrencyState = AppCurrencyState( + action.fiatCurrencyName + ), + createTwinWalletState = twinsState, + cardTermsOfUseUrl = action.cardTou.getUrl(action.card) ) } @@ -99,7 +95,7 @@ private fun handleEraseWallet(action: DetailsAction.EraseWallet, state: DetailsS } private fun handleAppCurrencyAction( - action: DetailsAction.AppCurrencyAction, state: DetailsState + action: DetailsAction.AppCurrencyAction, state: DetailsState ): DetailsState { return when (action) { is DetailsAction.AppCurrencyAction.SetCurrencies -> { @@ -113,9 +109,9 @@ private fun handleAppCurrencyAction( } is DetailsAction.AppCurrencyAction.SelectAppCurrency -> { state.copy( - appCurrencyState = state.appCurrencyState.copy( - fiatCurrencyName = action.fiatCurrencyName, showAppCurrencyDialog = false - ) + appCurrencyState = state.appCurrencyState.copy( + fiatCurrencyName = action.fiatCurrencyName, showAppCurrencyDialog = false + ) ) } else -> state @@ -123,7 +119,7 @@ private fun handleAppCurrencyAction( } private fun handleSecurityAction( - action: DetailsAction.ManageSecurity, state: DetailsState + action: DetailsAction.ManageSecurity, state: DetailsState ): DetailsState { return when (action) { is DetailsAction.ManageSecurity.SetCurrentOption -> { @@ -143,8 +139,8 @@ private fun handleSecurityAction( is DetailsAction.ManageSecurity.OpenSecurity -> { if (state.card?.isStart2Coin == true) { return state.copy(securityScreenState = state.securityScreenState?.copy( - allowedOptions = EnumSet.of(SecurityOption.LongTap), - selectedOption = state.securityScreenState.currentOption + allowedOptions = EnumSet.of(SecurityOption.LongTap), + selectedOption = state.securityScreenState.currentOption )) } @@ -152,13 +148,13 @@ private fun handleSecurityAction( state.card, state.securityScreenState?.currentOption ) state.copy(securityScreenState = state.securityScreenState?.copy( - allowedOptions = allowedSecurityOptions, - selectedOption = state.securityScreenState.currentOption + allowedOptions = allowedSecurityOptions, + selectedOption = state.securityScreenState.currentOption )) } is DetailsAction.ManageSecurity.SelectOption -> { state.copy(securityScreenState = state.securityScreenState?.copy( - selectedOption = action.option + selectedOption = action.option )) } is DetailsAction.ManageSecurity.ConfirmSelection -> { @@ -172,10 +168,10 @@ private fun handleSecurityAction( is DetailsAction.ManageSecurity.SaveChanges.Success -> { // Setting options to show only LongTap from now on state.copy( - securityScreenState = state.securityScreenState?.copy( - currentOption = state.securityScreenState.selectedOption, - allowedOptions = EnumSet.of(SecurityOption.LongTap) - )) + securityScreenState = state.securityScreenState?.copy( + currentOption = state.securityScreenState.selectedOption, + allowedOptions = EnumSet.of(SecurityOption.LongTap) + )) } else -> state @@ -205,4 +201,5 @@ private fun Card.toCardInfo(): CardInfo? { val cardId = this.cardId.chunked(4).joinToString(separator = " ") val issuer = this.issuer.name val signedHashes = this.signedHashesCount() - return CardInfo(cardId, issuer, signedHashes)} \ No newline at end of file + return CardInfo(cardId, issuer, signedHashes) +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt index bf16dd3674..bd55c9aa95 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt @@ -6,41 +6,41 @@ import com.tangem.common.card.Card import com.tangem.tap.common.entities.Button import com.tangem.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY import com.tangem.tap.common.redux.global.FiatCurrencyName -import com.tangem.tap.features.details.redux.twins.CreateTwinWalletState +import com.tangem.tap.features.twins.redux.CreateTwinWalletState import com.tangem.tap.network.coinmarketcap.FiatCurrency import org.rekotlin.StateType import java.util.* data class DetailsState( - val card: Card? = null, - val wallets: List = emptyList(), - val cardInfo: CardInfo? = null, - val appCurrencyState: AppCurrencyState = AppCurrencyState(), - val eraseWalletState: EraseWalletState? = null, - val confirmScreenState: ConfirmScreenState? = null, - val securityScreenState: SecurityScreenState? = null, - val createTwinWalletState: CreateTwinWalletState? = null, - val cardTermsOfUseUrl: Uri? = null, + val card: Card? = null, + val wallets: List = emptyList(), + val cardInfo: CardInfo? = null, + val appCurrencyState: AppCurrencyState = AppCurrencyState(), + val eraseWalletState: EraseWalletState? = null, + val confirmScreenState: ConfirmScreenState? = null, + val securityScreenState: SecurityScreenState? = null, + val createTwinWalletState: CreateTwinWalletState? = null, + val cardTermsOfUseUrl: Uri? = null, ) : StateType data class CardInfo( - val cardId: String, - val issuer: String, - val signedHashes: Int, + val cardId: String, + val issuer: String, + val signedHashes: Int, ) enum class EraseWalletState { Allowed, NotAllowedByCard, NotEmpty } enum class ConfirmScreenState { EraseWallet, LongTap, AccessCode, PassCode } data class SecurityScreenState( - val currentOption: SecurityOption = SecurityOption.LongTap, - val selectedOption: SecurityOption = currentOption, - val allowedOptions: EnumSet = EnumSet.allOf(SecurityOption::class.java), - val buttonProceed: Button = Button(true), + val currentOption: SecurityOption = SecurityOption.LongTap, + val selectedOption: SecurityOption = currentOption, + val allowedOptions: EnumSet = EnumSet.allOf(SecurityOption::class.java), + val buttonProceed: Button = Button(true), ) enum class SecurityOption { LongTap, PassCode, AccessCode } data class AppCurrencyState( - val fiatCurrencyName: FiatCurrencyName = DEFAULT_FIAT_CURRENCY, - val showAppCurrencyDialog: Boolean = false, - val fiatCurrencies: List? = null, + val fiatCurrencyName: FiatCurrencyName = DEFAULT_FIAT_CURRENCY, + val showAppCurrencyDialog: Boolean = false, + val fiatCurrencies: List? = null, ) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt deleted file mode 100644 index 1a6c05c2b1..0000000000 --- a/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt +++ /dev/null @@ -1,119 +0,0 @@ -package com.tangem.tap.features.details.redux.twins - -import com.tangem.blockchain.extensions.Result -import com.tangem.blockchain.extensions.SimpleResult -import com.tangem.tap.common.redux.navigation.AppScreen -import com.tangem.tap.common.redux.navigation.NavigationAction -import com.tangem.tap.domain.extensions.toSendableAmounts -import com.tangem.tap.domain.twins.TwinCardsManager -import com.tangem.tap.features.details.redux.DetailsAction -import com.tangem.tap.scope -import com.tangem.tap.store -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext - -class CreateTwinWalletMiddleware { - var twinsManager: TwinCardsManager? = null - fun handle(action: DetailsAction.CreateTwinWalletAction) { - when (action) { - is DetailsAction.CreateTwinWalletAction.ShowWarning -> { - val wallet = store.state.detailsState.wallets.firstOrNull() - if (wallet == null) { - store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWalletWarning)) - return - } - val notEmpty = wallet.recentTransactions.isNotEmpty() || wallet.amounts.toSendableAmounts().isNotEmpty() - if (notEmpty) { - store.dispatch(DetailsAction.CreateTwinWalletAction.NotEmpty) - } else { - store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWalletWarning)) - } - } - is DetailsAction.CreateTwinWalletAction.Proceed -> - store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWallet)) - is DetailsAction.CreateTwinWalletAction.Cancel -> { - - val step = store.state.detailsState.createTwinWalletState?.step - if (step != null && step != CreateTwinWalletStep.FirstStep - ) { - store.dispatch(DetailsAction.CreateTwinWalletAction.ShowAlert) - } else { - twinsManager = null - store.dispatch(NavigationAction.PopBackTo()) - } - } - is DetailsAction.CreateTwinWalletAction.Cancel.Confirm -> { - twinsManager = null - store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) - } - is DetailsAction.CreateTwinWalletAction.LaunchFirstStep -> { - store.state.globalState.scanResponse?.let { - twinsManager = TwinCardsManager(it, action.context) - } - scope.launch { - val result = twinsManager?.createFirstWallet(action.message) - withContext(Dispatchers.Main) { - when (result) { - SimpleResult.Success -> - store.dispatch(DetailsAction.CreateTwinWalletAction.LaunchFirstStep.Success) - is SimpleResult.Failure -> - store.dispatch(DetailsAction.CreateTwinWalletAction.LaunchFirstStep.Failure) - } - } - } - } - DetailsAction.CreateTwinWalletAction.LaunchFirstStep.Success -> { - - } - DetailsAction.CreateTwinWalletAction.LaunchFirstStep.Failure -> { - - } - is DetailsAction.CreateTwinWalletAction.LaunchSecondStep -> - scope.launch { - val result = twinsManager?.createSecondWallet(action.initialMessage, - action.preparingMessage, action.creatingWalletMessage) - withContext(Dispatchers.Main) { - when (result) { - SimpleResult.Success -> - store.dispatch(DetailsAction.CreateTwinWalletAction.LaunchSecondStep.Success) - is SimpleResult.Failure -> - store.dispatch(DetailsAction.CreateTwinWalletAction.LaunchSecondStep.Failure) - } - } - } - DetailsAction.CreateTwinWalletAction.LaunchSecondStep.Success -> { - - } - DetailsAction.CreateTwinWalletAction.LaunchSecondStep.Failure -> { - - } - is DetailsAction.CreateTwinWalletAction.LaunchThirdStep -> { - scope.launch { - val result = twinsManager?.complete(action.message) - withContext(Dispatchers.Main) { - when (result) { - is Result.Success -> - store.dispatch( - DetailsAction.CreateTwinWalletAction - .LaunchThirdStep.Success(result.data) - ) - is Result.Failure -> - store.dispatch(DetailsAction.CreateTwinWalletAction.LaunchThirdStep.Failure) - } - } - } - } - is DetailsAction.CreateTwinWalletAction.LaunchThirdStep.Success -> { - scope.launch { - store.state.globalState.tapWalletManager.onCardScanned(action.scanResponse) - } - store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) - store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) - } - DetailsAction.CreateTwinWalletAction.LaunchThirdStep.Failure -> { - - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletReducer.kt deleted file mode 100644 index 7de266d076..0000000000 --- a/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletReducer.kt +++ /dev/null @@ -1,68 +0,0 @@ -package com.tangem.tap.features.details.redux.twins - -import com.tangem.tap.features.details.redux.DetailsAction -import com.tangem.tap.features.details.redux.DetailsState - -class CreateTwinWalletReducer { - companion object { - fun handle( - action: DetailsAction.CreateTwinWalletAction, state: DetailsState - ): DetailsState { - return when (action) { - is DetailsAction.CreateTwinWalletAction.ShowWarning -> { - state.copy(createTwinWalletState = CreateTwinWalletState( - scanResponse = null, - twinCardNumber = action.twinCardNumber - ?: state.createTwinWalletState?.twinCardNumber, - createTwinWallet = action.createTwinWallet, - showAlert = false, - allowRecreatingWallet = state.createTwinWalletState?.allowRecreatingWallet - )) - } - DetailsAction.CreateTwinWalletAction.NotEmpty -> state - DetailsAction.CreateTwinWalletAction.ShowAlert -> { - state.copy(createTwinWalletState = state.createTwinWalletState?.copy( - showAlert = true - )) - } - DetailsAction.CreateTwinWalletAction.HideAlert -> { - state.copy(createTwinWalletState = state.createTwinWalletState?.copy( - showAlert = false - )) - } - is DetailsAction.CreateTwinWalletAction.Proceed -> { - state - } - DetailsAction.CreateTwinWalletAction.Cancel -> state - DetailsAction.CreateTwinWalletAction.Cancel.Confirm -> state - is DetailsAction.CreateTwinWalletAction.LaunchFirstStep -> state - - DetailsAction.CreateTwinWalletAction.LaunchFirstStep.Success -> { - state.copy(createTwinWalletState = state.createTwinWalletState?.copy( - step = CreateTwinWalletStep.SecondStep - )) - } - DetailsAction.CreateTwinWalletAction.LaunchFirstStep.Failure -> state - - is DetailsAction.CreateTwinWalletAction.LaunchSecondStep -> state - DetailsAction.CreateTwinWalletAction.LaunchSecondStep.Success -> - state.copy(createTwinWalletState = state.createTwinWalletState?.copy( - step = CreateTwinWalletStep.ThirdStep - )) - DetailsAction.CreateTwinWalletAction.LaunchSecondStep.Failure -> { - state - } - is DetailsAction.CreateTwinWalletAction.LaunchThirdStep -> { - state - } - is DetailsAction.CreateTwinWalletAction.LaunchThirdStep.Success -> { - state - } - DetailsAction.CreateTwinWalletAction.LaunchThirdStep.Failure -> { - state - } - } - } - - } -} \ No newline at end of file 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 640cc47cf0..7d845228f7 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 @@ -16,8 +16,9 @@ import com.tangem.tap.domain.twins.isTangemTwin import com.tangem.tap.features.details.redux.DetailsAction import com.tangem.tap.features.details.redux.DetailsState import com.tangem.tap.features.details.redux.SecurityOption -import com.tangem.tap.features.details.redux.twins.CreateTwinWallet import com.tangem.tap.features.feedback.FeedbackEmail +import com.tangem.tap.features.twins.redux.CreateTwinWallet +import com.tangem.tap.features.twins.redux.CreateTwinWalletAction import com.tangem.tap.store import com.tangem.wallet.R import kotlinx.android.synthetic.main.fragment_details.* @@ -95,9 +96,7 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber = { dispatch, state -> + { next -> + { action -> + handle(action, dispatch) + next(action) + } + } +} + +private var twinsManager: TwinCardsManager? = null + +private fun handle(action: Action, dispatch: DispatchFunction) { + val action = action as? CreateTwinWalletAction ?: return + + when (action) { + is CreateTwinWalletAction.ShowWarning -> { + val wallet = store.state.detailsState.wallets.firstOrNull() + if (wallet == null) { + store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWalletWarning)) + return + } + val notEmpty = wallet.recentTransactions.isNotEmpty() || wallet.amounts.toSendableAmounts().isNotEmpty() + if (notEmpty) { + store.dispatch(CreateTwinWalletAction.NotEmpty) + } else { + store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWalletWarning)) + } + } + is CreateTwinWalletAction.Proceed -> + store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWallet)) + is CreateTwinWalletAction.Cancel -> { + + val step = store.state.detailsState.createTwinWalletState?.step + if (step != null && step != CreateTwinWalletStep.FirstStep + ) { + store.dispatch(CreateTwinWalletAction.ShowAlert) + } else { + twinsManager = null + store.dispatch(NavigationAction.PopBackTo()) + } + } + is CreateTwinWalletAction.Cancel.Confirm -> { + twinsManager = null + store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) + } + is CreateTwinWalletAction.LaunchFirstStep -> { + store.state.globalState.scanResponse?.let { + twinsManager = TwinCardsManager(it, action.context) + } + scope.launch { + val result = twinsManager?.createFirstWallet(action.message) + withContext(Dispatchers.Main) { + when (result) { + SimpleResult.Success -> + store.dispatch(CreateTwinWalletAction.LaunchFirstStep.Success) + is SimpleResult.Failure -> + store.dispatch(CreateTwinWalletAction.LaunchFirstStep.Failure) + } + } + } + } + CreateTwinWalletAction.LaunchFirstStep.Success -> { + + } + CreateTwinWalletAction.LaunchFirstStep.Failure -> { + + } + is CreateTwinWalletAction.LaunchSecondStep -> + scope.launch { + val result = twinsManager?.createSecondWallet(action.initialMessage, + action.preparingMessage, action.creatingWalletMessage) + withContext(Dispatchers.Main) { + when (result) { + SimpleResult.Success -> + store.dispatch(CreateTwinWalletAction.LaunchSecondStep.Success) + is SimpleResult.Failure -> + store.dispatch(CreateTwinWalletAction.LaunchSecondStep.Failure) + } + } + } + CreateTwinWalletAction.LaunchSecondStep.Success -> { + + } + CreateTwinWalletAction.LaunchSecondStep.Failure -> { + + } + is CreateTwinWalletAction.LaunchThirdStep -> { + scope.launch { + val result = twinsManager?.complete(action.message) + withContext(Dispatchers.Main) { + when (result) { + is Result.Success -> + store.dispatch( + CreateTwinWalletAction + .LaunchThirdStep.Success(result.data) + ) + is Result.Failure -> + store.dispatch(CreateTwinWalletAction.LaunchThirdStep.Failure) + } + } + } + } + is CreateTwinWalletAction.LaunchThirdStep.Success -> { + scope.launch { + store.state.globalState.tapWalletManager.onCardScanned(action.scanResponse) + } + store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) + store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) + } + CreateTwinWalletAction.LaunchThirdStep.Failure -> { + + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/twins/redux/CreateTwinWalletReducer.kt b/app/src/main/java/com/tangem/tap/features/twins/redux/CreateTwinWalletReducer.kt new file mode 100644 index 0000000000..6eeb6e18f5 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/twins/redux/CreateTwinWalletReducer.kt @@ -0,0 +1,67 @@ +package com.tangem.tap.features.twins.redux + +import com.tangem.tap.features.details.redux.DetailsState + +class CreateTwinWalletReducer { + companion object { + fun handle( + action: CreateTwinWalletAction, state: DetailsState + ): DetailsState { + return when (action) { + is CreateTwinWalletAction.ShowWarning -> { + state.copy(createTwinWalletState = CreateTwinWalletState( + scanResponse = null, + twinCardNumber = action.twinCardNumber + ?: state.createTwinWalletState?.twinCardNumber, + createTwinWallet = action.createTwinWallet, + showAlert = false, + allowRecreatingWallet = state.createTwinWalletState?.allowRecreatingWallet + )) + } + CreateTwinWalletAction.NotEmpty -> state + CreateTwinWalletAction.ShowAlert -> { + state.copy(createTwinWalletState = state.createTwinWalletState?.copy( + showAlert = true + )) + } + CreateTwinWalletAction.HideAlert -> { + state.copy(createTwinWalletState = state.createTwinWalletState?.copy( + showAlert = false + )) + } + is CreateTwinWalletAction.Proceed -> { + state + } + CreateTwinWalletAction.Cancel -> state + CreateTwinWalletAction.Cancel.Confirm -> state + is CreateTwinWalletAction.LaunchFirstStep -> state + + CreateTwinWalletAction.LaunchFirstStep.Success -> { + state.copy(createTwinWalletState = state.createTwinWalletState?.copy( + step = CreateTwinWalletStep.SecondStep + )) + } + CreateTwinWalletAction.LaunchFirstStep.Failure -> state + + is CreateTwinWalletAction.LaunchSecondStep -> state + CreateTwinWalletAction.LaunchSecondStep.Success -> + state.copy(createTwinWalletState = state.createTwinWalletState?.copy( + step = CreateTwinWalletStep.ThirdStep + )) + CreateTwinWalletAction.LaunchSecondStep.Failure -> { + state + } + is CreateTwinWalletAction.LaunchThirdStep -> { + state + } + is CreateTwinWalletAction.LaunchThirdStep.Success -> { + state + } + CreateTwinWalletAction.LaunchThirdStep.Failure -> { + state + } + } + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletState.kt b/app/src/main/java/com/tangem/tap/features/twins/redux/CreateTwinWalletState.kt similarity index 91% rename from app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletState.kt rename to app/src/main/java/com/tangem/tap/features/twins/redux/CreateTwinWalletState.kt index 22c96dd0bf..0a7c6e04ee 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletState.kt +++ b/app/src/main/java/com/tangem/tap/features/twins/redux/CreateTwinWalletState.kt @@ -1,4 +1,4 @@ -package com.tangem.tap.features.details.redux.twins +package com.tangem.tap.features.twins.redux import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.domain.twins.TwinCardNumber diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/twins/CreateTwinWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/twins/ui/CreateTwinWalletFragment.kt similarity index 79% rename from app/src/main/java/com/tangem/tap/features/details/ui/twins/CreateTwinWalletFragment.kt rename to app/src/main/java/com/tangem/tap/features/twins/ui/CreateTwinWalletFragment.kt index f8a09c7131..8c4b950a0d 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/twins/CreateTwinWalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/twins/ui/CreateTwinWalletFragment.kt @@ -1,4 +1,4 @@ -package com.tangem.tap.features.details.ui.twins +package com.tangem.tap.features.twins.ui import android.app.Dialog import android.os.Bundle @@ -11,17 +11,17 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.squareup.picasso.Picasso import com.tangem.Message import com.tangem.tap.domain.twins.TwinCardNumber -import com.tangem.tap.features.details.redux.DetailsAction import com.tangem.tap.features.details.redux.DetailsState -import com.tangem.tap.features.details.redux.twins.CreateTwinWallet -import com.tangem.tap.features.details.redux.twins.CreateTwinWalletStep +import com.tangem.tap.features.twins.redux.CreateTwinWallet +import com.tangem.tap.features.twins.redux.CreateTwinWalletAction +import com.tangem.tap.features.twins.redux.CreateTwinWalletStep import com.tangem.tap.features.wallet.redux.Artwork import com.tangem.tap.store import com.tangem.wallet.R -import kotlinx.android.synthetic.main.fragment_details_twin_cards.* +import kotlinx.android.synthetic.main.fragment_twin_cards_create.* import org.rekotlin.StoreSubscriber -class CreateTwinWalletFragment : Fragment(R.layout.fragment_details_twin_cards), +class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create), StoreSubscriber { private var dialog: Dialog? = null @@ -30,7 +30,7 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_details_twin_cards), super.onCreate(savedInstanceState) activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { - store.dispatch(DetailsAction.CreateTwinWalletAction.Cancel) + store.dispatch(CreateTwinWalletAction.Cancel) } }) val inflater = TransitionInflater.from(requireContext()) @@ -56,7 +56,7 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_details_twin_cards), super.onViewCreated(view, savedInstanceState) toolbar.setNavigationOnClickListener { - store.dispatch(DetailsAction.CreateTwinWalletAction.Cancel) + store.dispatch(CreateTwinWalletAction.Cancel) } Picasso.get() @@ -96,7 +96,7 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_details_twin_cards), v_step_2.setBackgroundColor(defaultColor) v_step_3.setBackgroundColor(defaultColor) btn_tap.setOnClickListener { - store.dispatch(DetailsAction.CreateTwinWalletAction.LaunchFirstStep( + store.dispatch(CreateTwinWalletAction.LaunchFirstStep( Message(getString( R.string.details_twins_recreate_title_format, twinCardNumberString @@ -105,7 +105,7 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_details_twin_cards), )) } btn_tap.text = getString(R.string.details_twins_recreate_button_format, - twinCardNumber.number.toString()) + twinCardNumber.number.toString()) twinCardNumberString } CreateTwinWalletStep.SecondStep -> { @@ -117,10 +117,10 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_details_twin_cards), v_step_2.setBackgroundColor(selectedColor) v_step_3.setBackgroundColor(defaultColor) btn_tap.setOnClickListener { - store.dispatch(DetailsAction.CreateTwinWalletAction.LaunchSecondStep( - Message(getString(R.string.details_twins_recreate_title_format, twinCardNumberString)), - Message(getString(R.string.details_twins_recreate_title_preparing)), - Message(getString(R.string.details_twins_recreate_title_creating_wallet)), + store.dispatch(CreateTwinWalletAction.LaunchSecondStep( + Message(getString(R.string.details_twins_recreate_title_format, twinCardNumberString)), + Message(getString(R.string.details_twins_recreate_title_preparing)), + Message(getString(R.string.details_twins_recreate_title_creating_wallet)), )) } btn_tap.text = getString(R.string.details_twins_recreate_button_format, "2") @@ -135,10 +135,10 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_details_twin_cards), v_step_2.setBackgroundColor(selectedColor) v_step_3.setBackgroundColor(selectedColor) btn_tap.setOnClickListener { - store.dispatch(DetailsAction.CreateTwinWalletAction.LaunchThirdStep( - Message(getString( - R.string.details_twins_recreate_title_format, twinCardNumberString) - ) + store.dispatch(CreateTwinWalletAction.LaunchThirdStep( + Message(getString( + R.string.details_twins_recreate_title_format, twinCardNumberString) + ) )) } btn_tap.text = getString(R.string.details_twins_recreate_button_format, twinCardNumberString) @@ -154,13 +154,13 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_details_twin_cards), dialog = MaterialAlertDialogBuilder(requireContext()) .setMessage(R.string.details_twins_recreate_alert) .setPositiveButton(R.string.common_ok) { _, _ -> - store.dispatch(DetailsAction.CreateTwinWalletAction.Cancel.Confirm) + store.dispatch(CreateTwinWalletAction.Cancel.Confirm) } .setNegativeButton(R.string.common_cancel) { _, _ -> dialog?.cancel() } .setOnCancelListener { - store.dispatch(DetailsAction.CreateTwinWalletAction.HideAlert) + store.dispatch(CreateTwinWalletAction.HideAlert) } .create() dialog?.show() diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/twins/TwinWalletWarningFragment.kt b/app/src/main/java/com/tangem/tap/features/twins/ui/TwinWalletWarningFragment.kt similarity index 81% rename from app/src/main/java/com/tangem/tap/features/details/ui/twins/TwinWalletWarningFragment.kt rename to app/src/main/java/com/tangem/tap/features/twins/ui/TwinWalletWarningFragment.kt index 678dddf2bb..a1103c5bc7 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/twins/TwinWalletWarningFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/twins/ui/TwinWalletWarningFragment.kt @@ -1,4 +1,4 @@ -package com.tangem.tap.features.details.ui.twins +package com.tangem.tap.features.twins.ui import android.os.Bundle import android.view.View @@ -7,15 +7,15 @@ import androidx.fragment.app.Fragment import androidx.transition.TransitionInflater import com.squareup.picasso.Picasso import com.tangem.tap.common.redux.navigation.NavigationAction -import com.tangem.tap.features.details.redux.DetailsAction -import com.tangem.tap.features.details.redux.twins.CreateTwinWallet +import com.tangem.tap.features.twins.redux.CreateTwinWallet +import com.tangem.tap.features.twins.redux.CreateTwinWalletAction import com.tangem.tap.features.wallet.redux.Artwork import com.tangem.tap.store import com.tangem.wallet.R -import kotlinx.android.synthetic.main.fragment_details_twin_cards_warning.* +import kotlinx.android.synthetic.main.fragment_twin_cards_warning.* import kotlinx.android.synthetic.main.layout_twin_cards_orange.* -class TwinWalletWarningFragment : Fragment(R.layout.fragment_details_twin_cards_warning) { +class TwinWalletWarningFragment : Fragment(R.layout.fragment_twin_cards_warning) { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -39,10 +39,7 @@ class TwinWalletWarningFragment : Fragment(R.layout.fragment_details_twin_cards_ } btn_cancel.setOnClickListener { store.dispatch(NavigationAction.PopBackTo()) } - btn_start.setOnClickListener { - store.dispatch( - DetailsAction.CreateTwinWalletAction.Proceed) - } + btn_start.setOnClickListener { store.dispatch(CreateTwinWalletAction.Proceed) } Picasso.get() .load(Artwork.TWIN_CARD_1) .placeholder(R.drawable.card_placeholder) 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 3b3a4f31c1..45ebaee21f 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 @@ -11,19 +11,17 @@ 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.extensions.copyToClipboard -import com.tangem.tap.common.extensions.dispatchOnMain -import com.tangem.tap.common.extensions.shareText +import com.tangem.tap.common.extensions.* import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.extensions.toSendableAmounts import com.tangem.tap.domain.twins.TwinsHelper -import com.tangem.tap.features.details.redux.DetailsAction -import com.tangem.tap.features.details.redux.twins.CreateTwinWallet import com.tangem.tap.features.home.redux.HomeAction import com.tangem.tap.features.send.redux.PrepareSendScreen +import com.tangem.tap.features.twins.redux.CreateTwinWallet +import com.tangem.tap.features.twins.redux.CreateTwinWalletAction import com.tangem.tap.features.wallet.redux.* import com.tangem.tap.network.NetworkStateChanged import kotlinx.coroutines.launch @@ -87,7 +85,7 @@ class WalletMiddleware { globalState?.tapWalletManager?.loadFiatRate( fiatCurrency = globalState.appCurrency, currencies = walletState?.wallets?.mapNotNull { it.currency } - ?: emptyList() + ?: emptyList() ) } } @@ -95,16 +93,15 @@ class WalletMiddleware { } is WalletAction.CreateWallet -> { if (walletState?.twinCardsState != null) { - store.dispatch(DetailsAction.CreateTwinWalletAction.ShowWarning( - globalState?.scanResponse?.card?.cardId?.let { - TwinsHelper.getTwinCardNumber(it) - }, - CreateTwinWallet.CreateWallet - )) + val cardId = globalState?.scanResponse?.card?.cardId + val twinCardNumber = cardId?.let { TwinsHelper.getTwinCardNumber(it) } + store.dispatch( + CreateTwinWalletAction.ShowWarning(twinCardNumber, CreateTwinWallet.CreateWallet) + ) } else { scope.launch { val result = tangemSdkManager.createWallet( - globalState?.scanResponse?.card?.cardId + globalState?.scanResponse?.card?.cardId ) when (result) { is CompletionResult.Success -> { @@ -236,12 +233,12 @@ class WalletMiddleware { when (currency) { is Currency.Blockchain -> { val amountToSend = amounts?.find { it.currencySymbol == currency.blockchain.currency } - ?: return WalletAction.Send.ChooseCurrency(amounts) + ?: return WalletAction.Send.ChooseCurrency(amounts) PrepareSendScreen(amountToSend, selectedWalletData.fiatRate, walletManager) } is Currency.Token -> { val amountToSend = amounts?.find { it.currencySymbol == currency.token.symbol } - ?: return WalletAction.Send.ChooseCurrency(amounts) + ?: return WalletAction.Send.ChooseCurrency(amounts) prepareSendActionForToken(amountToSend, state, selectedWalletData, wallet, walletManager) } } @@ -257,8 +254,8 @@ class WalletMiddleware { } private fun prepareSendActionForToken( - amount: Amount, state: WalletState?, selectedWalletData: WalletData?, wallet: Wallet?, - walletManager: WalletManager? + amount: Amount, state: WalletState?, selectedWalletData: WalletData?, wallet: Wallet?, + walletManager: WalletManager? ): PrepareSendScreen { val coinRate = state?.getWalletData(wallet?.blockchain)?.fiatRate val tokenRate = if (state?.isMultiwalletAllowed == true) { @@ -267,7 +264,7 @@ class WalletMiddleware { selectedWalletData?.currencyData?.token?.fiatRate } return PrepareSendScreen( - wallet?.amounts?.get(AmountType.Coin), coinRate, walletManager, - amount, tokenRate) + wallet?.amounts?.get(AmountType.Coin), coinRate, walletManager, + amount, tokenRate) } } 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 1592ae4eb2..f2072f7cf3 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 @@ -20,9 +20,7 @@ import com.tangem.tap.features.wallet.ui.adapters.PendingTransactionsAdapter import com.tangem.tap.features.wallet.ui.dialogs.AmountToSendDialog import com.tangem.tap.store import com.tangem.wallet.R -import kotlinx.android.synthetic.main.fragment_details_twin_cards.* import kotlinx.android.synthetic.main.fragment_wallet_details.* -import kotlinx.android.synthetic.main.fragment_wallet_details.toolbar import kotlinx.android.synthetic.main.item_currency_wallet.view.* import kotlinx.android.synthetic.main.item_popular_token.view.* import kotlinx.android.synthetic.main.layout_balance_error.* @@ -98,12 +96,12 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS tv_currency_title.text = selectedWallet.currencyData.currency val currency = selectedWallet.currency - if (currency is Currency.Token) { - tv_currency_subtitle.text = currency.blockchain.tokenDisplayName() - tv_currency_subtitle.show() + if (currency is Currency.Token) { + tv_currency_subtitle.text = currency.blockchain.tokenDisplayName() + tv_currency_subtitle.show() } else { - tv_currency_subtitle.hide() - } + tv_currency_subtitle.hide() + } showPendingTransactionsIfPresent(selectedWallet.pendingTransactions) @@ -129,9 +127,9 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS srl_wallet_details.setOnRefreshListener { if (selectedWallet.currencyData.status != BalanceStatus.Loading) { store.dispatch(WalletAction.LoadWallet( - allowToBuy = selectedWallet.tradeCryptoState.buyingAllowed, - allowToSell = selectedWallet.tradeCryptoState.sellingAllowed, - blockchain = selectedWallet.currency?.blockchain + allowToBuy = selectedWallet.tradeCryptoState.buyingAllowed, + allowToSell = selectedWallet.tradeCryptoState.sellingAllowed, + blockchain = selectedWallet.currency?.blockchain )) } } @@ -180,8 +178,8 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS tv_address.text = state.walletAddresses.selectedAddress.address tv_explore?.setOnClickListener { store.dispatch(WalletAction.ExploreAddress( - state.walletAddresses.selectedAddress.exploreUrl, - requireContext())) + state.walletAddresses.selectedAddress.exploreUrl, + requireContext())) } iv_qr_code.setImageBitmap(state.walletAddresses.selectedAddress.shareUrl.toQrCode()) } @@ -192,8 +190,8 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS if (state.error == ErrorType.NoInternetConnection) { srl_wallet_details?.isRefreshing = false (activity as? SnackbarHandler)?.showSnackbar( - text = R.string.wallet_notification_no_internet, - buttonTitle = R.string.common_retry + text = R.string.wallet_notification_no_internet, + buttonTitle = R.string.common_retry ) { store.dispatch(WalletAction.LoadData) } } } else { @@ -249,8 +247,8 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS tv_error_title.text = getText(R.string.wallet_error_no_account) tv_error_descriptions.text = getString( - R.string.wallet_error_no_account_subtitle_format, - data.amountToCreateAccount, data.currencySymbol + R.string.wallet_error_no_account_subtitle_format, + data.amountToCreateAccount, data.currencySymbol ) } } diff --git a/app/src/main/res/layout/fragment_details_twin_cards.xml b/app/src/main/res/layout/fragment_twin_cards_create.xml similarity index 100% rename from app/src/main/res/layout/fragment_details_twin_cards.xml rename to app/src/main/res/layout/fragment_twin_cards_create.xml diff --git a/app/src/main/res/layout/fragment_details_twin_cards_warning.xml b/app/src/main/res/layout/fragment_twin_cards_warning.xml similarity index 97% rename from app/src/main/res/layout/fragment_details_twin_cards_warning.xml rename to app/src/main/res/layout/fragment_twin_cards_warning.xml index 1343fbe116..6ca8d8f68c 100644 --- a/app/src/main/res/layout/fragment_details_twin_cards_warning.xml +++ b/app/src/main/res/layout/fragment_twin_cards_warning.xml @@ -16,10 +16,10 @@ + android:background="@color/warning" + app:layout_constraintBottom_toTopOf="@id/l_twin_cards" + app:layout_constraintTop_toTopOf="parent" /> + android:background="@color/darkGray6" + app:layout_constraintBottom_toTopOf="@id/l_twin_cards" + app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintStart_toStartOf="parent" /> Date: Fri, 22 Oct 2021 00:15:34 +0300 Subject: [PATCH 080/189] Updated on 2026-08-14 --- .../redux/middlewares/WalletMiddleware.kt | 324 +++++++++--------- 1 file changed, 159 insertions(+), 165 deletions(-) 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 45ebaee21f..5f0ee586c7 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 @@ -26,6 +26,7 @@ import com.tangem.tap.features.wallet.redux.* import com.tangem.tap.network.NetworkStateChanged import kotlinx.coroutines.launch import org.rekotlin.Action +import org.rekotlin.DispatchFunction import org.rekotlin.Middleware class WalletMiddleware { @@ -37,180 +38,173 @@ class WalletMiddleware { val walletMiddleware: Middleware = { dispatch, state -> { next -> { action -> - val globalState = state()?.globalState - val walletState = state()?.walletState - when (action) { - is WalletAction.TradeCryptoAction -> tradeCryptoMiddleware.handle(action) - is WalletAction.TwinsAction -> twinsMiddleware.handle(action) - is WalletAction.Warnings -> warningsMiddleware.handle(action, globalState) - is WalletAction.MultiWallet -> - multiWalletMiddleware.handle(action, walletState, globalState) - is WalletAction.LoadWallet -> { - scope.launch { - if (action.blockchain == null) { - walletState?.walletManagers?.map { walletManager -> - globalState?.tapWalletManager?.loadWalletData(walletManager) - } - } else { - val walletManager = walletState?.getWalletManager(action.blockchain) - walletManager?.let { globalState?.tapWalletManager?.loadWalletData(it) } - } - } - } - is WalletAction.LoadWallet.Success -> { - val coinAmount = action.wallet.amounts[AmountType.Coin]?.value - if (coinAmount != null && !coinAmount.isZero()) { - if (walletState?.getWalletData(action.wallet.blockchain) == null) { - store.dispatch(WalletAction.MultiWallet.AddBlockchain(action.wallet.blockchain)) - store.dispatch(WalletAction.LoadWallet.Success(action.wallet)) - } - } - store.dispatch(WalletAction.Warnings.CheckHashesCount.CheckHashesCountOnline) - warningsMiddleware.tryToShowAppRatingWarning(action.wallet) - } - is WalletAction.LoadFiatRate -> { - scope.launch { - when { - action.wallet != null -> { - globalState?.tapWalletManager?.loadFiatRate( - globalState.appCurrency, action.wallet - ) - } - action.currency != null -> { - globalState?.tapWalletManager?.loadFiatRate( - globalState.appCurrency, action.currency - ) - } - else -> { - globalState?.tapWalletManager?.loadFiatRate( - fiatCurrency = globalState.appCurrency, - currencies = walletState?.wallets?.mapNotNull { it.currency } - ?: emptyList() - ) - } - } - } - } - is WalletAction.CreateWallet -> { - if (walletState?.twinCardsState != null) { - val cardId = globalState?.scanResponse?.card?.cardId - val twinCardNumber = cardId?.let { TwinsHelper.getTwinCardNumber(it) } - store.dispatch( - CreateTwinWalletAction.ShowWarning(twinCardNumber, CreateTwinWallet.CreateWallet) - ) - } else { - scope.launch { - val result = tangemSdkManager.createWallet( - globalState?.scanResponse?.card?.cardId - ) - when (result) { - is CompletionResult.Success -> { - val scanNoteResponse = - globalState?.scanResponse?.copy(card = result.data) - scanNoteResponse?.let { - globalState.tapWalletManager.onCardScanned(scanNoteResponse) - } - } - is CompletionResult.Failure -> { - (result.error as? TangemSdkError)?.let { error -> - FirebaseAnalyticsHandler.logCardSdkError( - error, - FirebaseAnalyticsHandler.ActionToLog.CreateWallet, - card = store.state.detailsState.card - ) - } - } - } - } - } - } - is WalletAction.UpdateWallet -> { - if (action.blockchain != null) { - scope.launch { - val walletManager = walletState?.getWalletManager(action.blockchain) - walletManager?.let { globalState?.tapWalletManager?.updateWallet(it) } - } - } else { - scope.launch { - if (walletState?.state == ProgressState.Done) { - walletState.walletManagers.map { walletManager -> - globalState?.tapWalletManager?.updateWallet(walletManager) - } - } - } - } + handleAction(action, dispatch) + next(action) + } + } + } + private fun handleAction(action: Action, dispatch: DispatchFunction) { + val globalState = store.state.globalState + val walletState = store.state.walletState + + when (action) { + is WalletAction.TradeCryptoAction -> tradeCryptoMiddleware.handle(action) + is WalletAction.TwinsAction -> twinsMiddleware.handle(action) + is WalletAction.Warnings -> warningsMiddleware.handle(action, globalState) + is WalletAction.MultiWallet -> multiWalletMiddleware.handle(action, walletState, globalState) + is WalletAction.LoadWallet -> { + scope.launch { + if (action.blockchain == null) { + walletState.walletManagers.map { walletManager -> + globalState.tapWalletManager.loadWalletData(walletManager) + } + } else { + val walletManager = walletState.getWalletManager(action.blockchain) + walletManager?.let { globalState.tapWalletManager.loadWalletData(it) } } - is WalletAction.Scan -> { - store.dispatch(HomeAction.ShouldScanCardOnResume(true)) - store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) + } + } + is WalletAction.LoadWallet.Success -> { + val coinAmount = action.wallet.amounts[AmountType.Coin]?.value + if (coinAmount != null && !coinAmount.isZero()) { + if (walletState.getWalletData(action.wallet.blockchain) == null) { + store.dispatch(WalletAction.MultiWallet.AddBlockchain(action.wallet.blockchain)) + store.dispatch(WalletAction.LoadWallet.Success(action.wallet)) } - is WalletAction.LoadCardInfo -> { - scope.launch { - val response = OnlineCardVerifier().getCardInfo( - action.card.cardId, action.card.cardPublicKey + } + store.dispatch(WalletAction.Warnings.CheckHashesCount.CheckHashesCountOnline) + warningsMiddleware.tryToShowAppRatingWarning(action.wallet) + } + is WalletAction.LoadFiatRate -> { + scope.launch { + when { + action.wallet != null -> { + globalState.tapWalletManager.loadFiatRate( + globalState.appCurrency, action.wallet ) - when (response) { - is Result.Success -> { - store.dispatchOnMain( - WalletAction.SetArtworkId(response.data.artwork?.id) - ) - store.dispatchOnMain( - WalletAction.LoadArtwork(action.card, response.data.artwork?.id) - ) - store.dispatchOnMain( - GlobalAction.SetIfCardVerifiedOnline(response.data.passed) - ) - } - is Result.Failure -> { - store.dispatchOnMain( - GlobalAction.SetIfCardVerifiedOnline(false) - ) - } - } - store.dispatchOnMain(WalletAction.Warnings.CheckIfNeeded) } - } - is WalletAction.LoadData -> { - scope.launch { - val scanNoteResponse = globalState?.scanResponse - ?: return@launch - if (!walletState?.wallets.isNullOrEmpty()) { - globalState.tapWalletManager.reloadData(scanNoteResponse) - } else { - globalState.tapWalletManager.loadData(scanNoteResponse) - } + action.currency != null -> { + globalState.tapWalletManager.loadFiatRate( + globalState.appCurrency, action.currency + ) } - } - is NetworkStateChanged -> { - globalState?.scanResponse?.let { scanNoteResponse -> - store.dispatch(WalletAction.Warnings.CheckHashesCount.CheckHashesCountOnline) - scope.launch { - globalState.tapWalletManager.loadData(scanNoteResponse) - } - } - } - is WalletAction.CopyAddress -> { - action.context.copyToClipboard(action.address) - store.dispatch(WalletAction.CopyAddress.Success) - } - is WalletAction.ShareAddress -> { - action.context.shareText(action.address) - } - is WalletAction.ExploreAddress -> { - val uri = Uri.parse(action.exploreUrl) - val intent = Intent(Intent.ACTION_VIEW, uri) - ContextCompat.startActivity(action.context, intent, null) - } - is WalletAction.Send -> { - val newAction = prepareSendAction(action.amount, state()?.walletState) - store.dispatch(newAction) - if (newAction is PrepareSendScreen) { - store.dispatch(NavigationAction.NavigateTo(AppScreen.Send)) + else -> { + globalState.tapWalletManager.loadFiatRate( + fiatCurrency = globalState.appCurrency, + currencies = walletState.wallets.mapNotNull { it.currency } + ) } } } - next(action) + } + is WalletAction.CreateWallet -> { + if (walletState.twinCardsState != null) { + val cardId = globalState.scanResponse?.card?.cardId + val twinCardNumber = cardId?.let { TwinsHelper.getTwinCardNumber(it) } + store.dispatch( + CreateTwinWalletAction.ShowWarning(twinCardNumber, CreateTwinWallet.CreateWallet) + ) + } else { + scope.launch { + val result = tangemSdkManager.createWallet( + globalState.scanResponse?.card?.cardId + ) + when (result) { + is CompletionResult.Success -> { + val scanNoteResponse = globalState.scanResponse?.copy(card = result.data) + scanNoteResponse?.let { + globalState.tapWalletManager.onCardScanned(scanNoteResponse) + } + } + is CompletionResult.Failure -> { + (result.error as? TangemSdkError)?.let { error -> + FirebaseAnalyticsHandler.logCardSdkError( + error, + FirebaseAnalyticsHandler.ActionToLog.CreateWallet, + card = store.state.detailsState.card + ) + } + } + } + } + } + } + is WalletAction.UpdateWallet -> { + if (action.blockchain != null) { + scope.launch { + val walletManager = walletState.getWalletManager(action.blockchain) + walletManager?.let { globalState.tapWalletManager.updateWallet(it) } + } + } else { + scope.launch { + if (walletState.state == ProgressState.Done) { + walletState.walletManagers.map { walletManager -> + globalState.tapWalletManager.updateWallet(walletManager) + } + } + } + } + } + is WalletAction.Scan -> { + store.dispatch(HomeAction.ShouldScanCardOnResume(true)) + store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) + } + is WalletAction.LoadCardInfo -> { + scope.launch { + val response = OnlineCardVerifier().getCardInfo( + action.card.cardId, action.card.cardPublicKey + ) + when (response) { + is Result.Success -> { + val actionList = listOf( + WalletAction.SetArtworkId(response.data.artwork?.id), + WalletAction.LoadArtwork(action.card, response.data.artwork?.id), + GlobalAction.SetIfCardVerifiedOnline(response.data.passed) + ) + actionList.forEach { store.dispatch(it) } + } + is Result.Failure -> { + store.dispatchOnMain(GlobalAction.SetIfCardVerifiedOnline(false)) + } + } + store.dispatchOnMain(WalletAction.Warnings.CheckIfNeeded) + } + } + is WalletAction.LoadData -> { + scope.launch { + val scanNoteResponse = globalState.scanResponse ?: return@launch + if (!walletState.wallets.isEmpty()) { + globalState.tapWalletManager.reloadData(scanNoteResponse) + } else { + globalState.tapWalletManager.loadData(scanNoteResponse) + } + } + } + is NetworkStateChanged -> { + globalState.scanResponse?.let { scanNoteResponse -> + store.dispatch(WalletAction.Warnings.CheckHashesCount.CheckHashesCountOnline) + scope.launch { globalState.tapWalletManager.loadData(scanNoteResponse) } + } + } + is WalletAction.CopyAddress -> { + action.context.copyToClipboard(action.address) + store.dispatch(WalletAction.CopyAddress.Success) + } + is WalletAction.ShareAddress -> { + action.context.shareText(action.address) + } + is WalletAction.ExploreAddress -> { + val uri = Uri.parse(action.exploreUrl) + val intent = Intent(Intent.ACTION_VIEW, uri) + ContextCompat.startActivity(action.context, intent, null) + } + is WalletAction.Send -> { + val newAction = prepareSendAction(action.amount, store.state.walletState) + store.dispatch(newAction) + if (newAction is PrepareSendScreen) { + store.dispatch(NavigationAction.NavigateTo(AppScreen.Send)) + } } } } From 66f7ab98e9b998951cf56e00be23257f167ee41e Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 22 Oct 2021 02:59:15 +0300 Subject: [PATCH 081/189] Updated on 2026-08-14 --- .../com/tangem/tap/common/redux/AppReducer.kt | 2 + .../com/tangem/tap/common/redux/AppState.kt | 6 +- .../com/tangem/tap/domain/TapWalletManager.kt | 131 +++++++++--------- .../com/tangem/tap/domain/TapWorkarounds.kt | 11 +- .../tangem/tap/domain/tasks/ScanNoteTask.kt | 2 +- .../CreateProductWalletAndRescanTask.kt | 2 +- .../domain/tasks/product/ScanProductTask.kt | 22 ++- .../tangem/tap/domain/twins/TwinsHelper.kt | 1 + .../features/details/redux/DetailsAction.kt | 3 - .../features/details/redux/DetailsReducer.kt | 27 +--- .../features/details/redux/DetailsState.kt | 17 ++- .../features/details/ui/DetailsFragment.kt | 13 +- .../products/twins/OnboardingTwinsFragment.kt | 6 +- .../twins/redux/CreateTwinWalletAction.kt | 52 ------- .../twins/redux/CreateTwinWalletReducer.kt | 67 --------- .../twins/redux/CreateTwinWalletState.kt | 17 --- .../features/twins/redux/TwinCardsAction.kt | 62 +++++++++ ...etMiddleware.kt => TwinCardsMiddleware.kt} | 67 ++++----- .../features/twins/redux/TwinCardsReducer.kt | 63 +++++++++ .../features/twins/redux/TwinCardsState.kt | 34 +++++ .../twins/ui/CreateTwinWalletFragment.kt | 40 +++--- .../twins/ui/TwinWalletWarningFragment.kt | 12 +- .../tap/features/wallet/redux/WalletAction.kt | 9 -- .../tap/features/wallet/redux/WalletState.kt | 89 ++++++------ .../redux/middlewares/TwinsMiddleware.kt | 19 --- .../redux/middlewares/WalletMiddleware.kt | 18 +-- .../wallet/redux/reducers/TwinsReducer.kt | 36 ----- .../wallet/redux/reducers/WalletReducer.kt | 8 +- .../tap/features/wallet/ui/WalletFragment.kt | 13 +- .../wallet/ui/wallet/SingleWalletView.kt | 5 +- 30 files changed, 412 insertions(+), 442 deletions(-) delete mode 100644 app/src/main/java/com/tangem/tap/features/twins/redux/CreateTwinWalletAction.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/twins/redux/CreateTwinWalletReducer.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/twins/redux/CreateTwinWalletState.kt create mode 100644 app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsAction.kt rename app/src/main/java/com/tangem/tap/features/twins/redux/{CreateTwinWalletMiddleware.kt => TwinCardsMiddleware.kt} (61%) create mode 100644 app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsReducer.kt create mode 100644 app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsState.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TwinsMiddleware.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/TwinsReducer.kt diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt index 38c76819b3..ed4dbe69aa 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt @@ -11,6 +11,7 @@ import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOt import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletReducer import com.tangem.tap.features.send.redux.reducers.SendScreenReducer import com.tangem.tap.features.tokens.redux.TokensReducer +import com.tangem.tap.features.twins.redux.TwinCardsReducer import com.tangem.tap.features.wallet.redux.reducers.WalletReducer import org.rekotlin.Action @@ -26,6 +27,7 @@ fun appReducer(action: Action, state: AppState?): AppState { onboardingWalletState = OnboardingWalletReducer.reduce(action, state), onboardingOtherCardsState = OnboardingOtherCardsReducer.reduce(action, state), walletState = WalletReducer.reduce(action, state), + twinCardsState = TwinCardsReducer.reduce(action, state), sendState = SendScreenReducer.reduce(action, state.sendState), detailsState = DetailsReducer.reduce(action, state), disclaimerState = DisclaimerReducer.reduce(action, state), diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt index f883d78840..98827674a0 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt @@ -22,7 +22,8 @@ import com.tangem.tap.features.send.redux.middlewares.SendMiddleware import com.tangem.tap.features.send.redux.states.SendState import com.tangem.tap.features.tokens.redux.TokensMiddleware import com.tangem.tap.features.tokens.redux.TokensState -import com.tangem.tap.features.twins.redux.CreateTwinWalletMiddleware +import com.tangem.tap.features.twins.redux.TwinCardsMiddleware +import com.tangem.tap.features.twins.redux.TwinCardsState import com.tangem.tap.features.wallet.redux.WalletState import com.tangem.tap.features.wallet.redux.middlewares.WalletMiddleware import org.rekotlin.Middleware @@ -36,6 +37,7 @@ data class AppState( val onboardingWalletState: OnboardingWalletState = OnboardingWalletState(), val onboardingOtherCardsState: OnboardingOtherCardsState = OnboardingOtherCardsState(), val walletState: WalletState = WalletState(), + val twinCardsState: TwinCardsState = TwinCardsState(), val sendState: SendState = SendState(), val detailsState: DetailsState = DetailsState(), val disclaimerState: DisclaimerState = DisclaimerState(), @@ -53,7 +55,7 @@ data class AppState( OnboardingWalletMiddleware.handler, OnboardingOtherCardsMiddleware.handler, WalletMiddleware().walletMiddleware, - CreateTwinWalletMiddleware.handler, + TwinCardsMiddleware.handler, SendMiddleware().sendMiddleware, DetailsMiddleware().detailsMiddleware, DisclaimerMiddleware().disclaimerMiddleware, 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 163359460d..edc4601d8c 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -19,6 +19,7 @@ import com.tangem.tap.domain.tokens.CardCurrencies import com.tangem.tap.domain.twins.getTwinCardNumber import com.tangem.tap.domain.twins.isTangemTwin import com.tangem.tap.features.tokens.redux.TokensAction +import com.tangem.tap.features.twins.redux.TwinCardsAction import com.tangem.tap.features.wallet.redux.Currency import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.network.NetworkConnectivity @@ -82,7 +83,8 @@ class TapWalletManager { FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.CARD_IS_SCANNED, data.card, data.walletData?.blockchain) } store.state.globalState.feedbackManager?.infoHolder?.setCardInfo(data.card) - updateConfigManager(data) + val configManager = store.state.globalState.configManager + updateConfigManager(configManager, data) withContext(Dispatchers.Main) { store.dispatch(WalletAction.ResetState) @@ -91,7 +93,8 @@ class TapWalletManager { store.dispatch(WalletAction.MultiWallet.SetIsMultiwalletAllowed(data.card.isMultiwalletAllowed)) if (data.card.isTangemTwin()) { data.card.getTwinCardNumber()?.let { - store.dispatch(WalletAction.TwinsAction.SetTwinCard(null, it, true)) + val isCreatingTwinsAllowed = configManager?.config?.isCreatingTwinCardsAllowed ?: false + store.dispatch(TwinCardsAction.SetTwinCard(it, null, isCreatingTwinsAllowed)) } } @@ -104,8 +107,7 @@ class TapWalletManager { } } - private fun updateConfigManager(data: ScanResponse) { - val configManager = store.state.globalState.configManager + private fun updateConfigManager(configManager: ConfigManager?, data: ScanResponse) { val blockchain = data.getBlockchain() if (data.card.isStart2Coin) { configManager?.turnOff(ConfigManager.isSendingToPayIdEnabled) @@ -122,53 +124,42 @@ class TapWalletManager { suspend fun loadData(data: ScanResponse) { withContext(Dispatchers.Main) { - store.dispatch(WalletAction.LoadCardInfo(data.card)) + getActionIfUnknownBlockchainOrEmptyWallet(data)?.let { + store.dispatch(it) + return@withContext + } - when { - data.card.isTangemTwin() && data.secondTwinPublicKey == null -> { - store.dispatch(WalletAction.EmptyWallet) + val config = store.state.globalState.configManager?.config ?: return@withContext + val blockchain = data.getBlockchain() + val primaryWalletManager = walletManagerFactory.makePrimaryWalletManager(data) + + if (blockchain != Blockchain.Unknown && primaryWalletManager != null) { + val primaryToken = data.getPrimaryToken() + + store.dispatch(WalletAction.MultiWallet.SetPrimaryBlockchain(blockchain)) + if (primaryToken != null) { + primaryWalletManager.addToken(primaryToken) + store.dispatch(WalletAction.MultiWallet.SetPrimaryToken(primaryToken)) } - data.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> { - store.dispatch(WalletAction.LoadData.Failure(TapError.UnknownBlockchain)) + if (data.card.isMultiwalletAllowed) { + loadMultiWalletData(data.card, blockchain, primaryWalletManager) + } else { + store.dispatch(WalletAction.MultiWallet.AddWalletManagers(primaryWalletManager)) + store.dispatch(WalletAction.MultiWallet.AddBlockchains(listOf(blockchain))) } - data.card.wallets.isEmpty() -> { - store.dispatch(WalletAction.EmptyWallet) - } - else -> { - val config = store.state.globalState.configManager?.config ?: return@withContext - val blockchain = data.getBlockchain() - val primaryWalletManager = walletManagerFactory.makePrimaryWalletManager(data) - - if (blockchain != Blockchain.Unknown && primaryWalletManager != null) { - val primaryToken = data.getPrimaryToken() - - store.dispatch(WalletAction.MultiWallet.SetPrimaryBlockchain(blockchain)) - if (primaryToken != null) { - primaryWalletManager.addToken(primaryToken) - store.dispatch(WalletAction.MultiWallet.SetPrimaryToken(primaryToken)) - } - if (data.card.isMultiwalletAllowed) { - loadMultiWalletData(data.card, blockchain, primaryWalletManager) - } else { - store.dispatch(WalletAction.MultiWallet.AddWalletManagers(primaryWalletManager)) - store.dispatch(WalletAction.MultiWallet.AddBlockchains(listOf(blockchain))) - } - - } else { - if (data.card.isMultiwalletAllowed) { - loadMultiWalletData(data.card, blockchain, null) - } - } - val moonPayUserStatus = store.state.globalState.moonPayUserStatus - store.dispatch(WalletAction.LoadWallet( - allowToBuy = config.isTopUpEnabled && moonPayUserStatus?.isBuyAllowed == true, - allowToSell = config.isTopUpEnabled && moonPayUserStatus?.isSellAllowed == true, - )) - store.dispatch(WalletAction.LoadFiatRate()) + } else { + if (data.card.isMultiwalletAllowed) { + loadMultiWalletData(data.card, blockchain, null) } } + val moonPayUserStatus = store.state.globalState.moonPayUserStatus + store.dispatch(WalletAction.LoadWallet( + allowToBuy = config.isTopUpEnabled && moonPayUserStatus?.isBuyAllowed == true, + allowToSell = config.isTopUpEnabled && moonPayUserStatus?.isSellAllowed == true, + )) + store.dispatch(WalletAction.LoadFiatRate()) } } @@ -220,28 +211,38 @@ class TapWalletManager { suspend fun reloadData(data: ScanResponse) { withContext(Dispatchers.Main) { - when { - data.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> { - store.dispatch(WalletAction.LoadData.Failure(TapError.UnknownBlockchain)) - } - data.card.wallets.isEmpty() || - (data.card.isTangemTwin() && data.secondTwinPublicKey == null) -> { - store.dispatch(WalletAction.EmptyWallet) - } - else -> { - if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) { - store.dispatch(WalletAction.LoadData.Failure(TapError.NoInternetConnection)) - return@withContext - } - val config = store.state.globalState.configManager?.config ?: return@withContext - val moonpayUserStatus = store.state.globalState.moonPayUserStatus - store.dispatch(WalletAction.LoadWallet( - allowToBuy = config.isTopUpEnabled && moonpayUserStatus?.isBuyAllowed == true, - allowToSell = config.isTopUpEnabled && moonpayUserStatus?.isSellAllowed == true, - )) - store.dispatch(WalletAction.LoadFiatRate()) - } + getActionIfUnknownBlockchainOrEmptyWallet(data)?.let { + store.dispatch(it) + return@withContext } + if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) { + store.dispatch(WalletAction.LoadData.Failure(TapError.NoInternetConnection)) + return@withContext + } + + val config = store.state.globalState.configManager?.config ?: return@withContext + val moonpayUserStatus = store.state.globalState.moonPayUserStatus + store.dispatch(WalletAction.LoadWallet( + allowToBuy = config.isTopUpEnabled && moonpayUserStatus?.isBuyAllowed == true, + allowToSell = config.isTopUpEnabled && moonpayUserStatus?.isSellAllowed == true, + )) + store.dispatch(WalletAction.LoadFiatRate()) + } + } + + private fun getActionIfUnknownBlockchainOrEmptyWallet(data: ScanResponse): WalletAction? { + return when { + // check order is important + data.card.isTangemTwin() && !data.twinsIsTwinned() -> { + WalletAction.EmptyWallet + } + data.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> { + WalletAction.LoadData.Failure(TapError.UnknownBlockchain) + } + data.card.wallets.isEmpty() -> { + WalletAction.EmptyWallet + } + else -> null } } diff --git a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt index 9003c9f4fc..01ef833c3d 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt @@ -28,10 +28,13 @@ object TapWorkarounds { return excludedBatch || excludedIssuerName } - fun Card.isTangemNote(): Boolean = tangemNoteBatches.contains(batchId) - fun Card.isTangemWallet(): Boolean = tangemWalletBatches.contains(batchId) + @Deprecated("Use ScanResponse.isTangemNote") + fun isTangemNote(card: Card): Boolean = tangemNoteBatches.contains(card.batchId) - fun Card.getTangemNoteBlockchain(): Blockchain? = tangemNoteBatches[batchId] + @Deprecated("Use ScanResponse.isTangemWallet") + fun isTangemWallet(card: Card): Boolean = tangemWalletBatches.contains(card.batchId) + + fun getTangemNoteBlockchain(card: Card): Blockchain? = tangemNoteBatches[card.batchId] private const val START_2_COIN_ISSUER = "start2coin" private const val TEST_CARD_BATCH = "99FF" @@ -64,7 +67,7 @@ val DELAY_SDK_DIALOG_CLOSE = 1400L val Card.isMultiwalletAllowed: Boolean get() { - return !isTangemTwin() && !isStart2Coin && !isTangemNote() + return !isTangemTwin() && !isStart2Coin && !isTangemNote(this) && (firmwareVersion >= FirmwareVersion.MultiWalletAvailable || getSingleWallet()?.curve == EllipticCurve.Secp256k1) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt index 3b01ae1c98..8b3c26a91e 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt @@ -40,7 +40,7 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable { if (card.isTangemTwin()) { dealWithTwinCard(card, session, callback) - } else if (!card.isTangemNote() && card.firmwareVersion >= FirmwareVersion.MultiWalletAvailable) { + } else if (!isTangemNote(card) && card.firmwareVersion >= FirmwareVersion.MultiWalletAvailable) { createMissingWalletsIfNeeded(card, session, callback) } else { callback(CompletionResult.Success( diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletAndRescanTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletAndRescanTask.kt index 92beb01238..f1024cdf11 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletAndRescanTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletAndRescanTask.kt @@ -64,7 +64,7 @@ private class CreateWalletTangemNote : ProductCommandProcessor { override fun run( @@ -74,9 +82,9 @@ class ScanProductTask(val card: Card? = null) : CardSessionRunnable ScanNoteProcessor() - card.isTangemTwin() -> ScanTwinProcessor() - card.isTangemWallet() -> ScanWalletProcessor() + TapWorkarounds.isTangemNote(card) -> ScanNoteProcessor() + TwinsHelper.getTwinCardNumber(card.cardId) != null -> ScanTwinProcessor() + TapWorkarounds.isTangemWallet(card) -> ScanWalletProcessor() else -> ScanOtherCardsProcessor() } commandProcessor.proceed(card, session, callback) diff --git a/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt b/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt index 89affa15d1..d74d5ccf0d 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt @@ -51,6 +51,7 @@ enum class TwinCardNumber(val number: Int) { } } +@Deprecated("Use ScanResponse.isTangemTwin") fun Card.isTangemTwin(): Boolean { return TwinsHelper.getTwinCardNumber(cardId) != null } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt index 0e7a1f5e21..3f8fbfccea 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt @@ -1,7 +1,6 @@ package com.tangem.tap.features.details.redux import com.tangem.blockchain.common.Wallet -import com.tangem.common.card.Card import com.tangem.operations.pins.CheckUserCodesResponse import com.tangem.tap.common.redux.NotificationAction import com.tangem.tap.common.redux.global.FiatCurrencyName @@ -14,10 +13,8 @@ import org.rekotlin.Action sealed class DetailsAction : Action { data class PrepareScreen( - val card: Card, val scanResponse: ScanResponse, val wallets: List, - val isCreatingTwinWalletAllowed: Boolean?, val cardTou: CardTou, val fiatCurrencyName: FiatCurrencyName, val fiatCurrencies: List? = null, diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt index 5989a084e2..52c9652e68 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt @@ -7,9 +7,6 @@ import com.tangem.tap.domain.TapWorkarounds.isStart2Coin import com.tangem.tap.domain.extensions.isWalletDataSupported import com.tangem.tap.domain.extensions.signedHashesCount import com.tangem.tap.domain.extensions.toSendableAmounts -import com.tangem.tap.domain.twins.getTwinCardNumber -import com.tangem.tap.domain.twins.isTangemTwin -import com.tangem.tap.features.twins.redux.CreateTwinWalletState import com.tangem.tap.features.wallet.models.hasPendingTransactions import org.rekotlin.Action import java.util.* @@ -42,26 +39,12 @@ private fun internalReduce(action: Action, state: AppState): DetailsState { } private fun handlePrepareScreen(action: DetailsAction.PrepareScreen, state: DetailsState): DetailsState { - val twinsState = if (action.card.isTangemTwin()) { - CreateTwinWalletState( - scanResponse = action.scanResponse, - twinCardNumber = action.card.getTwinCardNumber(), - createTwinWallet = null, - showAlert = false, - allowRecreatingWallet = action.isCreatingTwinWalletAllowed - ) - } else { - null - } - return DetailsState( - card = action.card, wallets = action.wallets, - cardInfo = action.card.toCardInfo(), - appCurrencyState = AppCurrencyState( - action.fiatCurrencyName - ), - createTwinWalletState = twinsState, - cardTermsOfUseUrl = action.cardTou.getUrl(action.card) + card = action.scanResponse.card, + wallets = action.wallets, + cardInfo = action.scanResponse.card.toCardInfo(), + appCurrencyState = AppCurrencyState(action.fiatCurrencyName), + cardTermsOfUseUrl = action.cardTou.getUrl(action.scanResponse.card) ) } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt index bd55c9aa95..40a6a6df31 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt @@ -6,10 +6,12 @@ import com.tangem.common.card.Card import com.tangem.tap.common.entities.Button import com.tangem.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY import com.tangem.tap.common.redux.global.FiatCurrencyName -import com.tangem.tap.features.twins.redux.CreateTwinWalletState +import com.tangem.tap.features.twins.redux.TwinCardsState import com.tangem.tap.network.coinmarketcap.FiatCurrency +import com.tangem.tap.store import org.rekotlin.StateType import java.util.* +import kotlin.properties.ReadOnlyProperty data class DetailsState( val card: Card? = null, @@ -19,9 +21,18 @@ data class DetailsState( val eraseWalletState: EraseWalletState? = null, val confirmScreenState: ConfirmScreenState? = null, val securityScreenState: SecurityScreenState? = null, - val createTwinWalletState: CreateTwinWalletState? = null, val cardTermsOfUseUrl: Uri? = null, -) : StateType +) : StateType { + + // if you do not delegate - the application crashes on startup, + // because twinCardsState has not been created yet + val twinCardsState: TwinCardsState by ReadOnlyProperty { thisRef, property -> + store.state.twinCardsState + } + + val isTangemTwins: Boolean + get() = store.state.globalState.scanResponse?.isTangemTwins() == true +} data class CardInfo( val cardId: String, 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 7d845228f7..81b6e91032 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 @@ -17,8 +17,8 @@ import com.tangem.tap.features.details.redux.DetailsAction import com.tangem.tap.features.details.redux.DetailsState import com.tangem.tap.features.details.redux.SecurityOption import com.tangem.tap.features.feedback.FeedbackEmail -import com.tangem.tap.features.twins.redux.CreateTwinWallet -import com.tangem.tap.features.twins.redux.CreateTwinWalletAction +import com.tangem.tap.features.twins.redux.CreateTwinWalletMode +import com.tangem.tap.features.twins.redux.TwinCardsAction import com.tangem.tap.store import com.tangem.wallet.R import kotlinx.android.synthetic.main.fragment_details.* @@ -91,12 +91,15 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber { - state.copy(createTwinWalletState = CreateTwinWalletState( - scanResponse = null, - twinCardNumber = action.twinCardNumber - ?: state.createTwinWalletState?.twinCardNumber, - createTwinWallet = action.createTwinWallet, - showAlert = false, - allowRecreatingWallet = state.createTwinWalletState?.allowRecreatingWallet - )) - } - CreateTwinWalletAction.NotEmpty -> state - CreateTwinWalletAction.ShowAlert -> { - state.copy(createTwinWalletState = state.createTwinWalletState?.copy( - showAlert = true - )) - } - CreateTwinWalletAction.HideAlert -> { - state.copy(createTwinWalletState = state.createTwinWalletState?.copy( - showAlert = false - )) - } - is CreateTwinWalletAction.Proceed -> { - state - } - CreateTwinWalletAction.Cancel -> state - CreateTwinWalletAction.Cancel.Confirm -> state - is CreateTwinWalletAction.LaunchFirstStep -> state - - CreateTwinWalletAction.LaunchFirstStep.Success -> { - state.copy(createTwinWalletState = state.createTwinWalletState?.copy( - step = CreateTwinWalletStep.SecondStep - )) - } - CreateTwinWalletAction.LaunchFirstStep.Failure -> state - - is CreateTwinWalletAction.LaunchSecondStep -> state - CreateTwinWalletAction.LaunchSecondStep.Success -> - state.copy(createTwinWalletState = state.createTwinWalletState?.copy( - step = CreateTwinWalletStep.ThirdStep - )) - CreateTwinWalletAction.LaunchSecondStep.Failure -> { - state - } - is CreateTwinWalletAction.LaunchThirdStep -> { - state - } - is CreateTwinWalletAction.LaunchThirdStep.Success -> { - state - } - CreateTwinWalletAction.LaunchThirdStep.Failure -> { - state - } - } - } - - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/twins/redux/CreateTwinWalletState.kt b/app/src/main/java/com/tangem/tap/features/twins/redux/CreateTwinWalletState.kt deleted file mode 100644 index 0a7c6e04ee..0000000000 --- a/app/src/main/java/com/tangem/tap/features/twins/redux/CreateTwinWalletState.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.tangem.tap.features.twins.redux - -import com.tangem.tap.domain.tasks.product.ScanResponse -import com.tangem.tap.domain.twins.TwinCardNumber - -data class CreateTwinWalletState( - val scanResponse: ScanResponse?, - val step: CreateTwinWalletStep = CreateTwinWalletStep.FirstStep, - val twinCardNumber: TwinCardNumber?, - val createTwinWallet: CreateTwinWallet?, - val showAlert: Boolean, - val allowRecreatingWallet: Boolean? = null -) - -enum class CreateTwinWalletStep { FirstStep, SecondStep, ThirdStep } - -enum class CreateTwinWallet { CreateWallet, RecreateWallet } diff --git a/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsAction.kt b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsAction.kt new file mode 100644 index 0000000000..cf7d2433dd --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsAction.kt @@ -0,0 +1,62 @@ +package com.tangem.tap.features.twins.redux + +import android.content.Context +import com.tangem.Message +import com.tangem.tap.common.redux.NotificationAction +import com.tangem.tap.domain.tasks.product.ScanResponse +import com.tangem.tap.domain.twins.TwinCardNumber +import com.tangem.wallet.R +import org.rekotlin.Action + +/** +[REDACTED_AUTHOR] + */ +sealed class TwinCardsAction : Action { + object ShowOnboarding : TwinCardsAction() + object SetOnboardingShown : TwinCardsAction() + data class SetTwinCard( + val number: TwinCardNumber, + val secondCardId: String?, + val isCreatingTwinCardsAllowed: Boolean, + ) : TwinCardsAction() + + sealed class CreateWallet : TwinCardsAction() { + data class Create( + val number: TwinCardNumber, + val createTwinWalletMode: CreateTwinWalletMode + ) : CreateWallet() + + object NotEmpty : CreateWallet(), NotificationAction { + override val messageResource = R.string.details_notification_erase_wallet_not_possible + } + + object ShowAlert : CreateWallet() + object HideAlert : CreateWallet() + object Proceed : CreateWallet() + + object Cancel : CreateWallet() { + object Confirm : CreateWallet() + } + + data class LaunchFirstStep( + val message: Message, val context: Context + ) : CreateWallet() { + object Success : CreateWallet() + object Failure : CreateWallet() + } + + data class LaunchSecondStep( + val initialMessage: Message, + val preparingMessage: Message, + val creatingWalletMessage: Message, + ) : CreateWallet() { + object Success : CreateWallet() + object Failure : CreateWallet() + } + + data class LaunchThirdStep(val message: Message) : CreateWallet() { + data class Success(val scanResponse: ScanResponse) : CreateWallet() + object Failure : CreateWallet() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/twins/redux/CreateTwinWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsMiddleware.kt similarity index 61% rename from app/src/main/java/com/tangem/tap/features/twins/redux/CreateTwinWalletMiddleware.kt rename to app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsMiddleware.kt index 69f72f798d..673e0616d5 100644 --- a/app/src/main/java/com/tangem/tap/features/twins/redux/CreateTwinWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsMiddleware.kt @@ -7,6 +7,7 @@ import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.extensions.toSendableAmounts import com.tangem.tap.domain.twins.TwinCardsManager +import com.tangem.tap.preferencesStorage import com.tangem.tap.scope import com.tangem.tap.store import kotlinx.coroutines.Dispatchers @@ -16,7 +17,7 @@ import org.rekotlin.Action import org.rekotlin.DispatchFunction import org.rekotlin.Middleware -class CreateTwinWalletMiddleware { +class TwinCardsMiddleware { companion object { val handler = twinsWalletMiddleware } @@ -34,40 +35,47 @@ private val twinsWalletMiddleware: Middleware = { dispatch, state -> private var twinsManager: TwinCardsManager? = null private fun handle(action: Action, dispatch: DispatchFunction) { - val action = action as? CreateTwinWalletAction ?: return + val action = action as? TwinCardsAction ?: return + val twinCardsState = store.state.twinCardsState when (action) { - is CreateTwinWalletAction.ShowWarning -> { - val wallet = store.state.detailsState.wallets.firstOrNull() + is TwinCardsAction.SetTwinCard -> { + val showOnboarding = !preferencesStorage.wasTwinsOnboardingShown() + if (showOnboarding) store.dispatch(TwinCardsAction.ShowOnboarding) + } + TwinCardsAction.SetOnboardingShown -> { + preferencesStorage.saveTwinsOnboardingShown() + } + is TwinCardsAction.CreateWallet.Create -> { + val wallet = store.state.walletState.walletManagers.map { it.wallet }.firstOrNull() if (wallet == null) { store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWalletWarning)) return } val notEmpty = wallet.recentTransactions.isNotEmpty() || wallet.amounts.toSendableAmounts().isNotEmpty() if (notEmpty) { - store.dispatch(CreateTwinWalletAction.NotEmpty) + store.dispatch(TwinCardsAction.CreateWallet.NotEmpty) } else { store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWalletWarning)) } } - is CreateTwinWalletAction.Proceed -> + is TwinCardsAction.CreateWallet.Proceed -> { store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWallet)) - is CreateTwinWalletAction.Cancel -> { - - val step = store.state.detailsState.createTwinWalletState?.step - if (step != null && step != CreateTwinWalletStep.FirstStep - ) { - store.dispatch(CreateTwinWalletAction.ShowAlert) + } + is TwinCardsAction.CreateWallet.Cancel -> { + val step = twinCardsState.createWalletState?.step + if (step != null && step != CreateTwinWalletStep.FirstStep) { + store.dispatch(TwinCardsAction.CreateWallet.ShowAlert) } else { twinsManager = null store.dispatch(NavigationAction.PopBackTo()) } } - is CreateTwinWalletAction.Cancel.Confirm -> { + is TwinCardsAction.CreateWallet.Cancel.Confirm -> { twinsManager = null store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) } - is CreateTwinWalletAction.LaunchFirstStep -> { + is TwinCardsAction.CreateWallet.LaunchFirstStep -> { store.state.globalState.scanResponse?.let { twinsManager = TwinCardsManager(it, action.context) } @@ -76,62 +84,59 @@ private fun handle(action: Action, dispatch: DispatchFunction) { withContext(Dispatchers.Main) { when (result) { SimpleResult.Success -> - store.dispatch(CreateTwinWalletAction.LaunchFirstStep.Success) + store.dispatch(TwinCardsAction.CreateWallet.LaunchFirstStep.Success) is SimpleResult.Failure -> - store.dispatch(CreateTwinWalletAction.LaunchFirstStep.Failure) + store.dispatch(TwinCardsAction.CreateWallet.LaunchFirstStep.Failure) } } } } - CreateTwinWalletAction.LaunchFirstStep.Success -> { + TwinCardsAction.CreateWallet.LaunchFirstStep.Success -> { } - CreateTwinWalletAction.LaunchFirstStep.Failure -> { + TwinCardsAction.CreateWallet.LaunchFirstStep.Failure -> { } - is CreateTwinWalletAction.LaunchSecondStep -> + is TwinCardsAction.CreateWallet.LaunchSecondStep -> scope.launch { val result = twinsManager?.createSecondWallet(action.initialMessage, action.preparingMessage, action.creatingWalletMessage) withContext(Dispatchers.Main) { when (result) { SimpleResult.Success -> - store.dispatch(CreateTwinWalletAction.LaunchSecondStep.Success) + store.dispatch(TwinCardsAction.CreateWallet.LaunchSecondStep.Success) is SimpleResult.Failure -> - store.dispatch(CreateTwinWalletAction.LaunchSecondStep.Failure) + store.dispatch(TwinCardsAction.CreateWallet.LaunchSecondStep.Failure) } } } - CreateTwinWalletAction.LaunchSecondStep.Success -> { + TwinCardsAction.CreateWallet.LaunchSecondStep.Success -> { } - CreateTwinWalletAction.LaunchSecondStep.Failure -> { + TwinCardsAction.CreateWallet.LaunchSecondStep.Failure -> { } - is CreateTwinWalletAction.LaunchThirdStep -> { + is TwinCardsAction.CreateWallet.LaunchThirdStep -> { scope.launch { val result = twinsManager?.complete(action.message) withContext(Dispatchers.Main) { when (result) { is Result.Success -> - store.dispatch( - CreateTwinWalletAction - .LaunchThirdStep.Success(result.data) - ) + store.dispatch(TwinCardsAction.CreateWallet.LaunchThirdStep.Success(result.data)) is Result.Failure -> - store.dispatch(CreateTwinWalletAction.LaunchThirdStep.Failure) + store.dispatch(TwinCardsAction.CreateWallet.LaunchThirdStep.Failure) } } } } - is CreateTwinWalletAction.LaunchThirdStep.Success -> { + is TwinCardsAction.CreateWallet.LaunchThirdStep.Success -> { scope.launch { store.state.globalState.tapWalletManager.onCardScanned(action.scanResponse) } store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) } - CreateTwinWalletAction.LaunchThirdStep.Failure -> { + TwinCardsAction.CreateWallet.LaunchThirdStep.Failure -> { } } diff --git a/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsReducer.kt b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsReducer.kt new file mode 100644 index 0000000000..6f9e3a6f57 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsReducer.kt @@ -0,0 +1,63 @@ +package com.tangem.tap.features.twins.redux + +import com.tangem.tap.common.redux.AppState +import org.rekotlin.Action + +class TwinCardsReducer { + companion object { + fun reduce(action: Action, state: AppState): TwinCardsState = internalReduce(action, state) + } +} + +private fun internalReduce(action: Action, state: AppState): TwinCardsState { + if (action !is TwinCardsAction) return state.twinCardsState + + val twinCardsState = state.twinCardsState + return when (action) { + is TwinCardsAction.SetTwinCard -> { + twinCardsState.copy( + cardNumber = action.number, + secondCardId = action.secondCardId, + showTwinOnboarding = state.twinCardsState.showTwinOnboarding, + isCreatingTwinCardsAllowed = action.isCreatingTwinCardsAllowed, + ) + } + is TwinCardsAction.ShowOnboarding -> { + state.twinCardsState.copy(showTwinOnboarding = true) + } + is TwinCardsAction.SetOnboardingShown -> { + twinCardsState.copy(showTwinOnboarding = false) + } + is TwinCardsAction.CreateWallet.Create -> { + val prevState = twinCardsState.createWalletState + twinCardsState.copy(createWalletState = CreateTwinWalletState( + prevState?.scanResponse, + prevState?.number ?: action.number, + prevState?.mode ?: action.createTwinWalletMode, + )) + } + TwinCardsAction.CreateWallet.LaunchFirstStep.Success -> { + twinCardsState.copy(createWalletState = twinCardsState.createWalletState?.copy( + step = CreateTwinWalletStep.SecondStep + )) + } + TwinCardsAction.CreateWallet.LaunchSecondStep.Success -> + twinCardsState.copy(createWalletState = twinCardsState.createWalletState?.copy( + step = CreateTwinWalletStep.ThirdStep + )) + TwinCardsAction.CreateWallet.ShowAlert -> twinCardsState + TwinCardsAction.CreateWallet.HideAlert -> twinCardsState + is TwinCardsAction.CreateWallet.Proceed -> twinCardsState + TwinCardsAction.CreateWallet.NotEmpty -> twinCardsState + TwinCardsAction.CreateWallet.Cancel -> twinCardsState + TwinCardsAction.CreateWallet.Cancel.Confirm -> twinCardsState + is TwinCardsAction.CreateWallet.LaunchFirstStep -> twinCardsState + TwinCardsAction.CreateWallet.LaunchFirstStep.Failure -> twinCardsState + is TwinCardsAction.CreateWallet.LaunchSecondStep -> twinCardsState + TwinCardsAction.CreateWallet.LaunchSecondStep.Failure -> twinCardsState + is TwinCardsAction.CreateWallet.LaunchThirdStep -> twinCardsState + is TwinCardsAction.CreateWallet.LaunchThirdStep.Success -> twinCardsState + TwinCardsAction.CreateWallet.LaunchThirdStep.Failure -> twinCardsState + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsState.kt b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsState.kt new file mode 100644 index 0000000000..49a43b9d93 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsState.kt @@ -0,0 +1,34 @@ +package com.tangem.tap.features.twins.redux + +import com.tangem.tap.domain.tasks.product.ScanResponse +import com.tangem.tap.domain.twins.TwinCardNumber +import org.rekotlin.StateType + +/** +[REDACTED_AUTHOR] + */ +data class TwinCardsState( + // if cardNumber is not set -> the scanned card is not Twin + val cardNumber: TwinCardNumber? = null, + val secondCardId: String? = null, + val showTwinOnboarding: Boolean = false, + val isCreatingTwinCardsAllowed: Boolean = false, + val createWalletState: CreateTwinWalletState? = null +) : StateType { + val isReadyToUse: Boolean + get() = cardNumber != null +} + +data class CreateTwinWalletState( + val scanResponse: ScanResponse?, + val number: TwinCardNumber = TwinCardNumber.First, + val mode: CreateTwinWalletMode = CreateTwinWalletMode.CreateWallet, + val step: CreateTwinWalletStep = CreateTwinWalletStep.FirstStep, +) { + val showAlert: Boolean + get() = mode != CreateTwinWalletMode.RecreateWallet +} + +enum class CreateTwinWalletStep { FirstStep, SecondStep, ThirdStep } + +enum class CreateTwinWalletMode { CreateWallet, RecreateWallet } diff --git a/app/src/main/java/com/tangem/tap/features/twins/ui/CreateTwinWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/twins/ui/CreateTwinWalletFragment.kt index 8c4b950a0d..0c23906aaf 100644 --- a/app/src/main/java/com/tangem/tap/features/twins/ui/CreateTwinWalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/twins/ui/CreateTwinWalletFragment.kt @@ -11,10 +11,10 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.squareup.picasso.Picasso import com.tangem.Message import com.tangem.tap.domain.twins.TwinCardNumber -import com.tangem.tap.features.details.redux.DetailsState -import com.tangem.tap.features.twins.redux.CreateTwinWallet -import com.tangem.tap.features.twins.redux.CreateTwinWalletAction +import com.tangem.tap.features.twins.redux.CreateTwinWalletMode import com.tangem.tap.features.twins.redux.CreateTwinWalletStep +import com.tangem.tap.features.twins.redux.TwinCardsAction +import com.tangem.tap.features.twins.redux.TwinCardsState import com.tangem.tap.features.wallet.redux.Artwork import com.tangem.tap.store import com.tangem.wallet.R @@ -22,7 +22,7 @@ import kotlinx.android.synthetic.main.fragment_twin_cards_create.* import org.rekotlin.StoreSubscriber class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create), - StoreSubscriber { + StoreSubscriber { private var dialog: Dialog? = null @@ -30,7 +30,7 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create), super.onCreate(savedInstanceState) activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { - store.dispatch(CreateTwinWalletAction.Cancel) + store.dispatch(TwinCardsAction.CreateWallet.Cancel) } }) val inflater = TransitionInflater.from(requireContext()) @@ -42,8 +42,8 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create), super.onStart() store.subscribe(this) { state -> state.skipRepeats { oldState, newState -> - oldState.detailsState == newState.detailsState - }.select { it.detailsState } + oldState.twinCardsState == newState.twinCardsState + }.select { it.twinCardsState } } } @@ -56,7 +56,7 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create), super.onViewCreated(view, savedInstanceState) toolbar.setNavigationOnClickListener { - store.dispatch(CreateTwinWalletAction.Cancel) + store.dispatch(TwinCardsAction.CreateWallet.Cancel) } Picasso.get() @@ -73,21 +73,21 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create), } - override fun newState(state: DetailsState) { + override fun newState(state: TwinCardsState) { if (activity == null) return - toolbar.title = when (state.createTwinWalletState?.createTwinWallet) { - CreateTwinWallet.CreateWallet -> getText(R.string.wallet_button_create_wallet) - CreateTwinWallet.RecreateWallet, null -> getText(R.string.details_twins_recreate_toolbar) + toolbar.title = when (state.createWalletState?.mode) { + CreateTwinWalletMode.CreateWallet -> getText(R.string.wallet_button_create_wallet) + CreateTwinWalletMode.RecreateWallet, null -> getText(R.string.details_twins_recreate_toolbar) } val selectedColor = getColor(requireContext(), R.color.colorSecondary) val defaultColor = getColor(requireContext(), R.color.blue_pale) - val twinCardNumber = state.createTwinWalletState?.twinCardNumber ?: TwinCardNumber.First + val twinCardNumber = state.createWalletState?.number ?: TwinCardNumber.First - val cardNumber = when (state.createTwinWalletState?.step) { + val cardNumber = when (state.createWalletState?.step) { CreateTwinWalletStep.FirstStep -> { val twinCardNumberString = twinCardNumber.number.toString() tv_step_number.text = @@ -96,7 +96,7 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create), v_step_2.setBackgroundColor(defaultColor) v_step_3.setBackgroundColor(defaultColor) btn_tap.setOnClickListener { - store.dispatch(CreateTwinWalletAction.LaunchFirstStep( + store.dispatch(TwinCardsAction.CreateWallet.LaunchFirstStep( Message(getString( R.string.details_twins_recreate_title_format, twinCardNumberString @@ -117,7 +117,7 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create), v_step_2.setBackgroundColor(selectedColor) v_step_3.setBackgroundColor(defaultColor) btn_tap.setOnClickListener { - store.dispatch(CreateTwinWalletAction.LaunchSecondStep( + store.dispatch(TwinCardsAction.CreateWallet.LaunchSecondStep( Message(getString(R.string.details_twins_recreate_title_format, twinCardNumberString)), Message(getString(R.string.details_twins_recreate_title_preparing)), Message(getString(R.string.details_twins_recreate_title_creating_wallet)), @@ -135,7 +135,7 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create), v_step_2.setBackgroundColor(selectedColor) v_step_3.setBackgroundColor(selectedColor) btn_tap.setOnClickListener { - store.dispatch(CreateTwinWalletAction.LaunchThirdStep( + store.dispatch(TwinCardsAction.CreateWallet.LaunchThirdStep( Message(getString( R.string.details_twins_recreate_title_format, twinCardNumberString) ) @@ -149,18 +149,18 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create), btn_tap.text = getString(R.string.details_twins_recreate_button_format, cardNumber) tv_twin_title.text = getString(R.string.details_twins_recreate_title_format, cardNumber) - if (state.createTwinWalletState?.showAlert == true) { + if (state.createWalletState?.showAlert == true) { if (dialog == null) { dialog = MaterialAlertDialogBuilder(requireContext()) .setMessage(R.string.details_twins_recreate_alert) .setPositiveButton(R.string.common_ok) { _, _ -> - store.dispatch(CreateTwinWalletAction.Cancel.Confirm) + store.dispatch(TwinCardsAction.CreateWallet.Cancel.Confirm) } .setNegativeButton(R.string.common_cancel) { _, _ -> dialog?.cancel() } .setOnCancelListener { - store.dispatch(CreateTwinWalletAction.HideAlert) + store.dispatch(TwinCardsAction.CreateWallet.HideAlert) } .create() dialog?.show() diff --git a/app/src/main/java/com/tangem/tap/features/twins/ui/TwinWalletWarningFragment.kt b/app/src/main/java/com/tangem/tap/features/twins/ui/TwinWalletWarningFragment.kt index a1103c5bc7..fefff8729c 100644 --- a/app/src/main/java/com/tangem/tap/features/twins/ui/TwinWalletWarningFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/twins/ui/TwinWalletWarningFragment.kt @@ -7,8 +7,8 @@ import androidx.fragment.app.Fragment import androidx.transition.TransitionInflater import com.squareup.picasso.Picasso import com.tangem.tap.common.redux.navigation.NavigationAction -import com.tangem.tap.features.twins.redux.CreateTwinWallet -import com.tangem.tap.features.twins.redux.CreateTwinWalletAction +import com.tangem.tap.features.twins.redux.CreateTwinWalletMode +import com.tangem.tap.features.twins.redux.TwinCardsAction import com.tangem.tap.features.wallet.redux.Artwork import com.tangem.tap.store import com.tangem.wallet.R @@ -31,15 +31,15 @@ class TwinWalletWarningFragment : Fragment(R.layout.fragment_twin_cards_warning) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - val createTwinWallet = store.state.detailsState.createTwinWalletState?.createTwinWallet - if (createTwinWallet == CreateTwinWallet.CreateWallet) { + val createTwinWallet = store.state.twinCardsState.createWalletState?.mode + if (createTwinWallet == CreateTwinWalletMode.CreateWallet) { tv_twin_cards_description.text = getText(R.string.details_twins_recreate_subtitle) - } else if (createTwinWallet == CreateTwinWallet.RecreateWallet) { + } else if (createTwinWallet == CreateTwinWalletMode.RecreateWallet) { tv_twin_cards_description.text = getText(R.string.details_twins_recreate_warning) } btn_cancel.setOnClickListener { store.dispatch(NavigationAction.PopBackTo()) } - btn_start.setOnClickListener { store.dispatch(CreateTwinWalletAction.Proceed) } + btn_start.setOnClickListener { store.dispatch(TwinCardsAction.CreateWallet.Proceed) } Picasso.get() .load(Artwork.TWIN_CARD_1) .placeholder(R.drawable.card_placeholder) 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 55e7fb4f66..ae0a273c14 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 @@ -9,7 +9,6 @@ import com.tangem.tap.common.redux.NotificationAction import com.tangem.tap.domain.TapError import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.tokens.CardCurrencies -import com.tangem.tap.domain.twins.TwinCardNumber import com.tangem.wallet.R import org.rekotlin.Action import java.math.BigDecimal @@ -147,12 +146,4 @@ sealed class WalletAction : Action { data class ChangeSelectedAddress(val type: AddressType) : WalletAction() - sealed class TwinsAction : WalletAction() { - object ShowOnboarding : TwinsAction() - object SetOnboardingShown : TwinsAction() - data class SetTwinCard( - val secondCardId: String?, val number: TwinCardNumber, - val isCreatingTwinCardsAllowed: Boolean, - ) : TwinsAction() - } } \ 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 6c3db01f3c..aa6094f26c 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 @@ -12,39 +12,49 @@ import com.tangem.tap.common.toggleWidget.WidgetState import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.extensions.toSendableAmounts import com.tangem.tap.domain.topup.TradeCryptoHelper -import com.tangem.tap.domain.twins.TwinCardNumber +import com.tangem.tap.features.twins.redux.TwinCardsState import com.tangem.tap.features.wallet.models.PendingTransaction import com.tangem.tap.features.wallet.models.toPendingTransactions import com.tangem.tap.features.wallet.models.toPendingTransactionsForToken +import com.tangem.tap.features.wallet.ui.BalanceStatus import com.tangem.tap.features.wallet.ui.BalanceWidgetData import com.tangem.tap.store import org.rekotlin.StateType import java.math.BigDecimal +import kotlin.properties.ReadOnlyProperty data class WalletState( - val state: ProgressState = ProgressState.Done, - val error: ErrorType? = null, - val cardImage: Artwork? = null, - val hashesCountVerified: Boolean? = null, - val walletDialog: StateDialog? = null, - val twinCardsState: TwinCardsState? = null, - val mainWarningsList: List = mutableListOf(), - val wallets: List = emptyList(), - val walletManagers: List = emptyList(), - val isMultiwalletAllowed: Boolean = false, - val cardCurrency: CryptoCurrencyName? = null, - val selectedWallet: Currency? = null, - val primaryBlockchain: Blockchain? = null, - val primaryToken: Token? = null, - val isTestnet: Boolean = false, - val tradeCryptoAllowed: TradeCryptoAvailability = TradeCryptoAvailability() + val state: ProgressState = ProgressState.Done, + val error: ErrorType? = null, + val cardImage: Artwork? = null, + val hashesCountVerified: Boolean? = null, + val walletDialog: StateDialog? = null, + val mainWarningsList: List = mutableListOf(), + val wallets: List = emptyList(), + val walletManagers: List = emptyList(), + val isMultiwalletAllowed: Boolean = false, + val cardCurrency: CryptoCurrencyName? = null, + val selectedWallet: Currency? = null, + val primaryBlockchain: Blockchain? = null, + val primaryToken: Token? = null, + val isTestnet: Boolean = false, + val tradeCryptoAllowed: TradeCryptoAvailability = TradeCryptoAvailability() ) : StateType { + // if you do not delegate - the application crashes on startup, + // because twinCardsState has not been created yet + val twinCardsState: TwinCardsState by ReadOnlyProperty { thisRef, property -> + store.state.twinCardsState + } + + val isTangemTwins: Boolean + get() = store.state.globalState.scanResponse?.isTangemTwins() == true + val primaryWallet = if (wallets.isNotEmpty()) wallets[0] else null val shouldShowDetails: Boolean = - primaryWallet?.currencyData?.status != com.tangem.tap.features.wallet.ui.BalanceStatus.EmptyCard && - primaryWallet?.currencyData?.status != com.tangem.tap.features.wallet.ui.BalanceStatus.UnknownBlockchain + primaryWallet?.currencyData?.status != BalanceStatus.EmptyCard && + primaryWallet?.currencyData?.status != BalanceStatus.UnknownBlockchain val blockchains: List get() = walletManagers.map { it.wallet.blockchain } @@ -57,16 +67,16 @@ data class WalletState( return walletManagers.find { it.wallet.blockchain == token.blockchain } } - fun getWalletManager(currency: Currency?) : WalletManager? { + fun getWalletManager(currency: Currency?): WalletManager? { if (currency?.blockchain == null) return null return walletManagers.find { it.wallet.blockchain == currency.blockchain } } - fun getWalletManager(blockchain: Blockchain) : WalletManager? { + fun getWalletManager(blockchain: Blockchain): WalletManager? { return walletManagers.find { it.wallet.blockchain == blockchain } } - fun getWalletData(currency: Currency?) : WalletData? { + fun getWalletData(currency: Currency?): WalletData? { if (currency == null) return null return wallets.find { it.currency == currency } } @@ -93,7 +103,7 @@ data class WalletState( ?: return true if (walletData.currency is Currency.Blockchain && - walletManager.cardTokens.isNotEmpty() + walletManager.cardTokens.isNotEmpty() ) { return false } @@ -105,9 +115,9 @@ data class WalletState( wallet.amounts.toSendableAmounts().isEmpty() } else if (walletData.currency is Currency.Token) ( return wallet.recentTransactions.toPendingTransactionsForToken( - walletData.currency.token, wallet.address).isEmpty() + walletData.currency.token, wallet.address).isEmpty() && wallet.amounts[AmountType.Token(token = walletData.currency.token)] - ?.isAboveZero() != true + ?.isAboveZero() != true ) } return false @@ -137,9 +147,9 @@ data class WalletState( } } -sealed class WalletDialog: StateDialog { +sealed class WalletDialog : StateDialog { data class QrDialog( - val qrCode: Bitmap?, val shareUrl: String?, val currencyName: CryptoCurrencyName? + val qrCode: Bitmap?, val shareUrl: String?, val currencyName: CryptoCurrencyName? ) : WalletDialog() data class SelectAmountToSendDialog(val amounts: List?) : WalletDialog() @@ -147,7 +157,7 @@ sealed class WalletDialog: StateDialog { object ChooseTradeActionDialog : WalletDialog() } -enum class ProgressState: WidgetState { Loading, Done, Error } +enum class ProgressState : WidgetState { Loading, Done, Error } enum class ErrorType { NoInternetConnection } @@ -157,22 +167,22 @@ sealed class WalletMainButton(enabled: Boolean) : Button(enabled) { } data class WalletAddresses( - val selectedAddress: AddressData, - val list: List + val selectedAddress: AddressData, + val list: List ) data class AddressData( - val address: String, - val type: AddressType, - val shareUrl: String, - val exploreUrl: String, + val address: String, + val type: AddressType, + val shareUrl: String, + val exploreUrl: String, ) { val qrCode: Bitmap by lazy { shareUrl.toQrCode() } } data class Artwork( - val artworkId: String, - val artwork: Bitmap? = null + val artworkId: String, + val artwork: Bitmap? = null ) { companion object { const val DEFAULT_IMG_URL = "https://app.tangem.com/cards/card_default.png" @@ -198,13 +208,6 @@ data class TradeCryptoAvailability( val availableToSell: Set = TradeCryptoHelper.AVAILABLE_TO_SELL, ) -data class TwinCardsState( - val secondCardId: String?, - val cardNumber: TwinCardNumber?, - val showTwinOnboarding: Boolean, - val isCreatingTwinCardsAllowed: Boolean -) - data class WalletData( val pendingTransactions: List = emptyList(), val hashesCountVerified: Boolean? = null, diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TwinsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TwinsMiddleware.kt deleted file mode 100644 index 88ba60d1d1..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TwinsMiddleware.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.tangem.tap.features.wallet.redux.middlewares - -import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.preferencesStorage -import com.tangem.tap.store - -class TwinsMiddleware { - fun handle(action: WalletAction.TwinsAction) { - when (action) { - is WalletAction.TwinsAction.SetTwinCard -> { - val showOnboarding = !preferencesStorage.wasTwinsOnboardingShown() - if (showOnboarding) store.dispatch(WalletAction.TwinsAction.ShowOnboarding) - } - WalletAction.TwinsAction.SetOnboardingShown -> { - preferencesStorage.saveTwinsOnboardingShown() - } - } - } -} \ No newline at end of file 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 5f0ee586c7..61b4423e33 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 @@ -17,11 +17,10 @@ import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.extensions.toSendableAmounts -import com.tangem.tap.domain.twins.TwinsHelper import com.tangem.tap.features.home.redux.HomeAction import com.tangem.tap.features.send.redux.PrepareSendScreen -import com.tangem.tap.features.twins.redux.CreateTwinWallet -import com.tangem.tap.features.twins.redux.CreateTwinWalletAction +import com.tangem.tap.features.twins.redux.CreateTwinWalletMode +import com.tangem.tap.features.twins.redux.TwinCardsAction import com.tangem.tap.features.wallet.redux.* import com.tangem.tap.network.NetworkStateChanged import kotlinx.coroutines.launch @@ -31,7 +30,6 @@ import org.rekotlin.Middleware class WalletMiddleware { private val tradeCryptoMiddleware = TradeCryptoMiddleware() - private val twinsMiddleware = TwinsMiddleware() private val warningsMiddleware = WarningsMiddleware() private val multiWalletMiddleware = MultiWalletMiddleware() @@ -50,7 +48,6 @@ class WalletMiddleware { when (action) { is WalletAction.TradeCryptoAction -> tradeCryptoMiddleware.handle(action) - is WalletAction.TwinsAction -> twinsMiddleware.handle(action) is WalletAction.Warnings -> warningsMiddleware.handle(action, globalState) is WalletAction.MultiWallet -> multiWalletMiddleware.handle(action, walletState, globalState) is WalletAction.LoadWallet -> { @@ -99,12 +96,11 @@ class WalletMiddleware { } } is WalletAction.CreateWallet -> { - if (walletState.twinCardsState != null) { - val cardId = globalState.scanResponse?.card?.cardId - val twinCardNumber = cardId?.let { TwinsHelper.getTwinCardNumber(it) } - store.dispatch( - CreateTwinWalletAction.ShowWarning(twinCardNumber, CreateTwinWallet.CreateWallet) - ) + if (walletState.isTangemTwins) { + store.dispatch(TwinCardsAction.CreateWallet.Create( + walletState.twinCardsState.cardNumber!!, + CreateTwinWalletMode.CreateWallet + )) } else { scope.launch { val result = tangemSdkManager.createWallet( diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/TwinsReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/TwinsReducer.kt deleted file mode 100644 index 79025ca6cc..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/TwinsReducer.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.tangem.tap.features.wallet.redux.reducers - -import com.tangem.tap.features.wallet.redux.TwinCardsState -import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.features.wallet.redux.WalletState - -class TwinsReducer { - fun reduce(action: WalletAction.TwinsAction, state: WalletState): WalletState { - return when (action) { - is WalletAction.TwinsAction.SetTwinCard -> { - state.copy( - twinCardsState = TwinCardsState( - secondCardId = action.secondCardId, - cardNumber = action.number, - showTwinOnboarding = state.twinCardsState?.showTwinOnboarding - ?: false, - isCreatingTwinCardsAllowed = action.isCreatingTwinCardsAllowed - ) - ) - } - is WalletAction.TwinsAction.ShowOnboarding -> { - state.copy( - twinCardsState = state.twinCardsState?.copy(showTwinOnboarding = true) - ?: TwinCardsState(null, null, - showTwinOnboarding = true, - isCreatingTwinCardsAllowed = false) - ) - } - is WalletAction.TwinsAction.SetOnboardingShown -> { - state.copy( - twinCardsState = state.twinCardsState?.copy(showTwinOnboarding = false) - ) - } - } - } -} \ No newline at end of file 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 d999216bba..8e0e9df126 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 @@ -25,7 +25,6 @@ class WalletReducer { private fun internalReduce(action: Action, state: AppState): WalletState { val multiWalletReducer = MultiWalletReducer() - val twinsReducer = TwinsReducer() val onWalletLoadedReducer = OnWalletLoadedReducer() if (action !is WalletAction) return state.walletState @@ -34,15 +33,12 @@ private fun internalReduce(action: Action, state: AppState): WalletState { when (action) { is WalletAction.Warnings -> newState = handleCheckSignedHashesActions(action, newState) - is WalletAction.TwinsAction -> newState = twinsReducer.reduce(action, newState) is WalletAction.MultiWallet -> newState = multiWalletReducer.reduce(action, newState) is WalletAction.ResetState -> newState = WalletState() is WalletAction.SetIfTestnetCard -> newState = newState.copy(isTestnet = action.isTestnet) is WalletAction.EmptyWallet -> { - val creatingWalletAllowed = !(newState.twinCardsState != null && - newState.twinCardsState?.isCreatingTwinCardsAllowed != true) - + val creatingWalletAllowed = state.twinCardsState.isCreatingTwinCardsAllowed newState = newState.copy( state = ProgressState.Done, wallets = listOf( @@ -215,7 +211,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState { newState = setNewFiatRate(action.fiatRate, state.globalState.appCurrency, newState) is WalletAction.LoadArtwork -> { val artworkUrl = action.card.getArtworkUrl(action.artworkId) - ?: when (newState.twinCardsState?.cardNumber) { + ?: when (state.twinCardsState.cardNumber) { TwinCardNumber.First -> Artwork.TWIN_CARD_1 TwinCardNumber.Second -> Artwork.TWIN_CARD_2 else -> Artwork.DEFAULT_IMG_URL diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt index e8f2a250fd..fd65f1b619 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt @@ -135,8 +135,8 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber store.dispatch(DetailsAction.PrepareScreen( - scanNoteResponse.card, scanNoteResponse, - store.state.walletState.walletManagers.map { it.wallet }, - store.state.globalState.configManager?.config?.isCreatingTwinCardsAllowed, - CardTou(), - store.state.globalState.appCurrency + scanNoteResponse, + store.state.walletState.walletManagers.map { it.wallet }, + CardTou(), + store.state.globalState.appCurrency )) store.dispatch(NavigationAction.NavigateTo(AppScreen.Details)) true diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt index d2e4e77cc5..3c760f3e6e 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt @@ -10,6 +10,7 @@ import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.twins.TwinCardNumber +import com.tangem.tap.features.twins.redux.TwinCardsState import com.tangem.tap.features.wallet.models.PendingTransaction import com.tangem.tap.features.wallet.redux.* import com.tangem.tap.features.wallet.ui.BalanceWidget @@ -71,7 +72,7 @@ class SingleWalletView : WalletView { state.primaryWallet ?: return setupTwinCards(state.twinCardsState, fragment) - setupButtons(state.primaryWallet, state.twinCardsState != null, fragment) + setupButtons(state.primaryWallet, state.isTangemTwins, fragment) setupAddressCard(state.primaryWallet, fragment) showPendingTransactionsIfPresent(state.primaryWallet.pendingTransactions) setupBalance(state, state.primaryWallet) @@ -87,7 +88,7 @@ class SingleWalletView : WalletView { private fun setupBalance(state: WalletState, primaryWallet: WalletData) { fragment?.apply { this.l_balance.show() - BalanceWidget(this, primaryWallet.currencyData, state.twinCardsState != null).setup() + BalanceWidget(this, primaryWallet.currencyData, state.isTangemTwins).setup() } } From 913402bb7e219d80ea9b89ef14ded09727eca137 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 22 Oct 2021 03:14:09 +0300 Subject: [PATCH 082/189] Updated on 2026-08-14 --- .../common/redux/global/GlobalMidlleware.kt | 6 ++-- .../com/tangem/tap/domain/TangemSdkManager.kt | 21 ++++++------ .../com/tangem/tap/domain/TapWalletManager.kt | 5 ++- .../domain/extensions/WalletManagerFactory.kt | 3 +- .../tangem/tap/domain/tasks/ScanNoteTask.kt | 18 +++++------ .../domain/tasks/product/ScanProductTask.kt | 13 +++----- .../features/details/ui/DetailsFragment.kt | 9 +++--- .../redux/middlewares/WarningsMiddleware.kt | 32 ++++++++++--------- 8 files changed, 50 insertions(+), 57 deletions(-) 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 ea80ab463c..10aa0f1533 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 @@ -47,7 +47,7 @@ private val globalMiddlewareHandler: Middleware = { dispatch, appState } is GlobalAction.RestoreAppCurrency -> { store.dispatch(GlobalAction.RestoreAppCurrency.Success( - preferencesStorage.getAppCurrency() + preferencesStorage.getAppCurrency() )) } is GlobalAction.HideWarningMessage -> { @@ -81,7 +81,7 @@ private val globalMiddlewareHandler: Middleware = { dispatch, appState val userStatusResponse = MoonpayService().getUserStatus(apiKey) if (userStatusResponse is Result.Success) { store.dispatchOnMain( - GlobalAction.GetMoonPayUserStatus.Success(userStatusResponse.data) + GlobalAction.GetMoonPayUserStatus.Success(userStatusResponse.data) ) } } @@ -94,7 +94,7 @@ private val globalMiddlewareHandler: Middleware = { dispatch, appState withMainContext { when (result) { is CompletionResult.Success -> { - tangemSdkManager.changeDisplayedCardIdNumbersCount(result.data.card) + tangemSdkManager.changeDisplayedCardIdNumbersCount(result.data) action.onSuccess?.invoke(result.data) } is CompletionResult.Failure -> action.onFailure?.invoke(result.error) 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 5253a9f91c..3a3acc1a87 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -23,7 +23,6 @@ import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler import com.tangem.tap.domain.tasks.CreateWalletAndRescanTask import com.tangem.tap.domain.tasks.product.ScanProductTask import com.tangem.tap.domain.tasks.product.ScanResponse -import com.tangem.tap.domain.twins.isTangemTwin import com.tangem.wallet.R import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -45,9 +44,9 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co suspend fun createProductWallet(scanResponse: ScanResponse): CompletionResult { return runTaskAsync( - CreateProductWalletAndRescanTask(scanResponse.productType), - scanResponse.card.cardId, - Message(context.getString(R.string.initial_message_create_wallet_body)) + CreateProductWalletAndRescanTask(scanResponse.productType), + scanResponse.card.cardId, + Message(context.getString(R.string.initial_message_create_wallet_body)) ) } @@ -105,13 +104,13 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co suspend fun runTaskAsync( runnable: CardSessionRunnable, cardId: String? = null, initialMessage: Message? = null, ): CompletionResult = - withContext(Dispatchers.Main) { - suspendCoroutine { continuation -> - tangemSdk.startSessionWithRunnable(runnable, cardId, initialMessage) { result -> - continuation.resume(result) + withContext(Dispatchers.Main) { + suspendCoroutine { continuation -> + tangemSdk.startSessionWithRunnable(runnable, cardId, initialMessage) { result -> + continuation.resume(result) + } } } - } private suspend fun runTaskAsyncReturnOnMain( runnable: CardSessionRunnable, cardId: String? = null, initialMessage: Message? = null, @@ -120,8 +119,8 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co return withContext(Dispatchers.Main) { result } } - fun changeDisplayedCardIdNumbersCount(card: Card) { - tangemSdk.config.cardIdDisplayedNumbersCount = if (card.isTangemTwin()) 4 else null + fun changeDisplayedCardIdNumbersCount(scanResponse: ScanResponse) { + tangemSdk.config.cardIdDisplayedNumbersCount = if (scanResponse.isTangemTwins()) 4 else null } companion object { 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 edc4601d8c..75e8333a1c 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -17,7 +17,6 @@ import com.tangem.tap.domain.extensions.* import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.domain.tokens.CardCurrencies import com.tangem.tap.domain.twins.getTwinCardNumber -import com.tangem.tap.domain.twins.isTangemTwin import com.tangem.tap.features.tokens.redux.TokensAction import com.tangem.tap.features.twins.redux.TwinCardsAction import com.tangem.tap.features.wallet.redux.Currency @@ -91,7 +90,7 @@ class TapWalletManager { store.dispatch(GlobalAction.SaveScanNoteResponse(data)) store.dispatch(WalletAction.SetIfTestnetCard(data.card.isTestCard)) store.dispatch(WalletAction.MultiWallet.SetIsMultiwalletAllowed(data.card.isMultiwalletAllowed)) - if (data.card.isTangemTwin()) { + if (data.isTangemTwins()) { data.card.getTwinCardNumber()?.let { val isCreatingTwinsAllowed = configManager?.config?.isCreatingTwinCardsAllowed ?: false store.dispatch(TwinCardsAction.SetTwinCard(it, null, isCreatingTwinsAllowed)) @@ -233,7 +232,7 @@ class TapWalletManager { private fun getActionIfUnknownBlockchainOrEmptyWallet(data: ScanResponse): WalletAction? { return when { // check order is important - data.card.isTangemTwin() && !data.twinsIsTwinned() -> { + data.isTangemTwins() && !data.twinsIsTwinned() -> { WalletAction.EmptyWallet } data.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> { 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 99ed8231c4..aa85331ab2 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 @@ -9,7 +9,6 @@ import com.tangem.common.card.EllipticCurve import com.tangem.common.extensions.hexToBytes import com.tangem.tap.domain.TapWorkarounds.isTestCard import com.tangem.tap.domain.tasks.product.ScanResponse -import com.tangem.tap.domain.twins.isTangemTwin fun WalletManagerFactory.makeWalletManagerForApp( card: Card, @@ -65,7 +64,7 @@ fun WalletManagerFactory.makePrimaryWalletManager( val publicKey = wallet?.publicKey ?: return null val curveToUse = wallet.curve - return if (card.isTangemTwin() && data.secondTwinPublicKey != null) { + return if (data.isTangemTwins() && data.secondTwinPublicKey != null) { makeMultisigWalletManager( cardId = card.cardId, walletPublicKey = publicKey, pairPublicKey = data.secondTwinPublicKey.hexToBytes(), diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt index 8b3c26a91e..22a7e5ca87 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt @@ -17,8 +17,8 @@ import com.tangem.tap.domain.TapWorkarounds.isTangemNote import com.tangem.tap.domain.extensions.getSingleWallet import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.domain.twins.TwinsHelper -import com.tangem.tap.domain.twins.isTangemTwin +@Deprecated("Use ScanProductTask instead") class ScanNoteTask(val card: Card? = null) : CardSessionRunnable { override fun run( @@ -38,13 +38,13 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable { return@run } - if (card.isTangemTwin()) { + if (TwinsHelper.getTwinCardNumber(card.cardId) != null) { dealWithTwinCard(card, session, callback) } else if (!isTangemNote(card) && card.firmwareVersion >= FirmwareVersion.MultiWalletAvailable) { createMissingWalletsIfNeeded(card, session, callback) } else { callback(CompletionResult.Success( - ScanResponse(card, ProductType.Other, session.environment.walletData))) + ScanResponse(card, ProductType.Other, session.environment.walletData))) } } } @@ -93,16 +93,16 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable { return@run } val verified = TwinsHelper.verifyTwinPublicKey( - readDataResult.data.issuerData, publicKey + readDataResult.data.issuerData, publicKey ) if (verified) { val twinPublicKey = readDataResult.data.issuerData.sliceArray(0 until 65) callback(CompletionResult.Success( - ScanResponse( - card = card, - ProductType.Other, - walletData = session.environment.walletData, - secondTwinPublicKey = twinPublicKey.toHexString()) + ScanResponse( + card = card, + ProductType.Other, + walletData = session.environment.walletData, + secondTwinPublicKey = twinPublicKey.toHexString()) )) return@run } else { 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 3818c4b211..fa6a74564b 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 @@ -24,7 +24,6 @@ import com.tangem.tap.domain.TapWorkarounds.getTangemNoteBlockchain import com.tangem.tap.domain.TapWorkarounds.isExcluded import com.tangem.tap.domain.extensions.getSingleWallet import com.tangem.tap.domain.twins.TwinsHelper -import com.tangem.tap.domain.twins.isTangemTwin data class ScanResponse( val card: Card, @@ -55,13 +54,7 @@ data class ScanResponse( fun isTangemTwins(): Boolean = productType == ProductType.Twins fun isTangemOtherCards(): Boolean = productType == ProductType.Other - fun twinsIsTwinned(): Boolean { - return card.isTangemTwin() && walletData != null && secondTwinPublicKey != null - } -} - -private fun Card.isTangemTwin(): Boolean { - return TwinsHelper.getTwinCardNumber(cardId) != null + fun twinsIsTwinned(): Boolean = card.isTangemTwins() && walletData != null && secondTwinPublicKey != null } class ScanProductTask(val card: Card? = null) : CardSessionRunnable { @@ -83,7 +76,7 @@ class ScanProductTask(val card: Card? = null) : CardSessionRunnable ScanNoteProcessor() - TwinsHelper.getTwinCardNumber(card.cardId) != null -> ScanTwinProcessor() + card.isTangemTwins() -> ScanTwinProcessor() TapWorkarounds.isTangemWallet(card) -> ScanWalletProcessor() else -> ScanOtherCardsProcessor() } @@ -101,6 +94,8 @@ class ScanProductTask(val card: Card? = null) : CardSessionRunnable { override fun proceed( card: Card, 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 81b6e91032..efa81149b7 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 @@ -12,7 +12,6 @@ import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.twins.getTwinCardIdForUser -import com.tangem.tap.domain.twins.isTangemTwin import com.tangem.tap.features.details.redux.DetailsAction import com.tangem.tap.features.details.redux.DetailsState import com.tangem.tap.features.details.redux.SecurityOption @@ -69,8 +68,8 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber { if (action.remainingSignatures != null && - action.remainingSignatures <= WarningMessagesManager.REMAINING_SIGNATURES_WARNING + action.remainingSignatures <= WarningMessagesManager.REMAINING_SIGNATURES_WARNING ) { store.state.globalState.warningManager - ?.removeWarnings( - messageRes = R.string.warning_low_signatures_format - ) + ?.removeWarnings( + messageRes = R.string.warning_low_signatures_format + ) addWarningMessage( warning = WarningMessagesManager.remainingSignaturesNotEnough(action.remainingSignatures), @@ -81,7 +81,8 @@ class WarningsMiddleware { } private fun showCardWarningsIfNeeded(globalState: GlobalState?) { - globalState?.scanResponse?.card?.let { card -> + globalState?.scanResponse?.let { scanResponse -> + val card = scanResponse.card globalState.warningManager?.removeWarnings(WarningMessage.Origin.Local) if (card.isTestCard) { addWarningMessage(WarningMessagesManager.testCardWarning(), autoUpdate = true) @@ -92,7 +93,7 @@ class WarningsMiddleware { if (card.firmwareVersion.type != FirmwareVersion.FirmwareType.Release) { addWarningMessage(WarningMessagesManager.devCardWarning()) } else if (!preferencesStorage.usedCardsPrefStorage.wasScanned(card.cardId)) { - checkIfWarningNeeded(card)?.let { warning -> addWarningMessage(warning) } + checkIfWarningNeeded(scanResponse)?.let { warning -> addWarningMessage(warning) } } if (card.firmwareVersion.type == FirmwareVersion.FirmwareType.Release) { if (!globalState.cardVerifiedOnline) { @@ -106,7 +107,7 @@ class WarningsMiddleware { private fun showWarningLowRemainingSignaturesIfNeeded(card: Card) { val remainingSignatures = card.remainingSignatures if (remainingSignatures != null && - remainingSignatures <= WarningMessagesManager.REMAINING_SIGNATURES_WARNING + remainingSignatures <= WarningMessagesManager.REMAINING_SIGNATURES_WARNING ) { addWarningMessage( WarningMessagesManager.remainingSignaturesNotEnough( @@ -117,12 +118,12 @@ class WarningsMiddleware { } private fun checkIfWarningNeeded( - card: Card, + scanResponse: ScanResponse, ): WarningMessage? { - if (card.isTangemTwin()) return null + if (scanResponse.isTangemTwins()) return null - if (card.isMultiwalletAllowed) { - return if (card.hasSignedHashes()) { + if (scanResponse.card.isMultiwalletAllowed) { + return if (scanResponse.card.hasSignedHashes()) { WarningMessagesManager.signedHashesMultiWalletWarning() } else { store.dispatch(WalletAction.Warnings.CheckHashesCount.SaveCardId) @@ -133,7 +134,7 @@ class WarningsMiddleware { val validator = store.state.walletState.walletManagers.firstOrNull() as? SignatureCountValidator return if (validator == null) { - if (card.hasSignedHashes()) { + if (scanResponse.card.hasSignedHashes()) { WarningMessagesManager.alreadySignedHashesWarning() } else { store.dispatch(WalletAction.Warnings.CheckHashesCount.SaveCardId) @@ -149,10 +150,11 @@ class WarningsMiddleware { if (store.state.walletState.hashesCountVerified != false) return if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) return - val card = store.state.globalState.scanResponse?.card + val scanResponse = store.state.globalState.scanResponse + val card = scanResponse?.card if (card == null || preferencesStorage.usedCardsPrefStorage.wasScanned(card.cardId)) return - if (card.isTangemTwin() || card.isMultiwalletAllowed) return + if (scanResponse.isTangemTwins() || card.isMultiwalletAllowed) return val validator = store.state.walletState.walletManagers.firstOrNull() as? SignatureCountValidator From d087c388e253762d8df435a4c969fc1f4975250a Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 22 Oct 2021 03:45:54 +0300 Subject: [PATCH 083/189] Updated on 2026-08-14 --- .../com/tangem/tap/features/onboarding/OnboardingManager.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 59788c84ab..2b4894d5b9 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 @@ -13,6 +13,7 @@ import com.tangem.tap.domain.TapError import com.tangem.tap.domain.UrlBitmapLoader import com.tangem.tap.domain.extensions.getOrLoadCardArtworkUrl import com.tangem.tap.domain.tasks.product.ScanResponse +import com.tangem.tap.features.wallet.models.hasPendingTransactions import com.tangem.tap.features.wallet.redux.Artwork import com.tangem.tap.features.wallet.redux.Currency import com.tangem.tap.features.wallet.redux.ProgressState @@ -64,7 +65,7 @@ class OnboardingManager( } else { val currency = Currency.Blockchain(walletManager.wallet.blockchain) val balance = if (valueOfAmount.isZero()) { - OnboardingWalletBalance.done(valueOfAmount, wallet.recentTransactions.isNotEmpty(), currency) + OnboardingWalletBalance.done(valueOfAmount, wallet.hasPendingTransactions(), currency) } else { OnboardingWalletBalance.done(valueOfAmount, false, currency) } From f34aeba4ed019903270ee9be586de4e9fcf6c0f2 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 22 Oct 2021 05:54:34 +0300 Subject: [PATCH 084/189] Updated on 2026-08-14 --- .../com/tangem/tap/common/DialogManager.kt | 21 ++-- .../com/tangem/tap/domain/TapWalletManager.kt | 14 +-- .../tap/domain/twins/TwinCardsManager.kt | 22 ++-- ...{TwinsCardWidget.kt => TwinCardsWidget.kt} | 0 .../features/details/ui/DetailsFragment.kt | 2 +- .../tap/features/home/redux/HomeMiddleware.kt | 10 ++ .../products/note/OnboardingNoteFragment.kt | 13 ++- .../note/redux/OnboardingNoteAction.kt | 2 +- .../note/redux/OnboardingNoteState.kt | 16 +-- .../features/twins/redux/TwinCardsAction.kt | 56 +++++----- .../twins/redux/TwinCardsMiddleware.kt | 104 +++++++++--------- .../features/twins/redux/TwinCardsReducer.kt | 47 ++++---- .../features/twins/redux/TwinCardsState.kt | 21 +++- .../twins/ui/CreateTwinWalletFragment.kt | 62 +++++------ .../twins/ui/TwinWalletWarningFragment.kt | 2 +- .../ui/dialog/CreateWalletInterruptDialog.kt | 25 +++++ .../wallet/models/PendingTransaction.kt | 2 +- .../redux/middlewares/WalletMiddleware.kt | 2 +- 18 files changed, 222 insertions(+), 199 deletions(-) rename app/src/main/java/com/tangem/tap/domain/twins/{TwinsCardWidget.kt => TwinCardsWidget.kt} (100%) create mode 100644 app/src/main/java/com/tangem/tap/features/twins/ui/dialog/CreateWalletInterruptDialog.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 114c17c7cd..a604cbd643 100644 --- a/app/src/main/java/com/tangem/tap/common/DialogManager.kt +++ b/app/src/main/java/com/tangem/tap/common/DialogManager.kt @@ -7,6 +7,8 @@ import com.tangem.tap.common.redux.global.GlobalState import com.tangem.tap.features.details.redux.walletconnect.WalletConnectDialog import com.tangem.tap.features.details.ui.walletconnect.dialogs.* import com.tangem.tap.features.onboarding.AddressInfoBottomSheetDialog +import com.tangem.tap.features.twins.redux.TwinCardsAction +import com.tangem.tap.features.twins.ui.dialog.CreateWalletInterruptDialog import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog import com.tangem.tap.store import com.tangem.wallet.R @@ -43,24 +45,25 @@ class DialogManager : StoreSubscriber { dialog = when (state.dialog) { is AppDialog.ScanFailsDialog -> ScanFailsDialog.create(context) is AppDialog.AddressInfoDialog -> AddressInfoBottomSheetDialog(state.dialog, context) + is TwinCardsAction.Wallet.InterruptDialog -> CreateWalletInterruptDialog.create(state.dialog, context) is WalletConnectDialog.UnsupportedCard -> SimpleAlertDialog.create( - titleRes = R.string.wallet_connect, - messageRes = R.string.wallet_connect_scanner_error_no_ethereum_wallet, - context = context + titleRes = R.string.wallet_connect, + messageRes = R.string.wallet_connect_scanner_error_no_ethereum_wallet, + context = context ) is WalletConnectDialog.OpeningSessionRejected -> { SimpleAlertDialog.create( - titleRes = R.string.wallet_connect, - messageRes = R.string.wallet_connect_same_wcuri, - context = context + titleRes = R.string.wallet_connect, + messageRes = R.string.wallet_connect_same_wcuri, + context = context ) } is WalletConnectDialog.SessionTimeout -> { SimpleAlertDialog.create( - titleRes = R.string.wallet_connect, - messageRes = R.string.wallet_connect_error_timeout, - context = context + titleRes = R.string.wallet_connect, + messageRes = R.string.wallet_connect_error_timeout, + context = context ) } is WalletConnectDialog.ApproveWcSession -> 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 75e8333a1c..14f7b50b6b 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -16,9 +16,7 @@ import com.tangem.tap.domain.configurable.config.ConfigManager import com.tangem.tap.domain.extensions.* import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.domain.tokens.CardCurrencies -import com.tangem.tap.domain.twins.getTwinCardNumber import com.tangem.tap.features.tokens.redux.TokensAction -import com.tangem.tap.features.twins.redux.TwinCardsAction import com.tangem.tap.features.wallet.redux.Currency import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.network.NetworkConnectivity @@ -82,20 +80,13 @@ class TapWalletManager { FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.CARD_IS_SCANNED, data.card, data.walletData?.blockchain) } store.state.globalState.feedbackManager?.infoHolder?.setCardInfo(data.card) - val configManager = store.state.globalState.configManager - updateConfigManager(configManager, data) + updateConfigManager(data) withContext(Dispatchers.Main) { store.dispatch(WalletAction.ResetState) store.dispatch(GlobalAction.SaveScanNoteResponse(data)) store.dispatch(WalletAction.SetIfTestnetCard(data.card.isTestCard)) store.dispatch(WalletAction.MultiWallet.SetIsMultiwalletAllowed(data.card.isMultiwalletAllowed)) - if (data.isTangemTwins()) { - data.card.getTwinCardNumber()?.let { - val isCreatingTwinsAllowed = configManager?.config?.isCreatingTwinCardsAllowed ?: false - store.dispatch(TwinCardsAction.SetTwinCard(it, null, isCreatingTwinsAllowed)) - } - } val blockchain = data.getBlockchain() if (blockchain == Blockchain.Ethereum || @@ -106,7 +97,8 @@ class TapWalletManager { } } - private fun updateConfigManager(configManager: ConfigManager?, data: ScanResponse) { + private fun updateConfigManager(data: ScanResponse) { + val configManager = store.state.globalState.configManager val blockchain = data.getBlockchain() if (data.card.isStart2Coin) { configManager?.turnOff(ConfigManager.isSendingToPayIdEnabled) 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 df48e76971..39c203e744 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 @@ -1,6 +1,5 @@ package com.tangem.tap.domain.twins -import android.content.Context import com.squareup.moshi.JsonAdapter import com.squareup.moshi.Types import com.tangem.Message @@ -12,24 +11,21 @@ import com.tangem.common.core.TangemSdkError import com.tangem.common.extensions.hexToBytes import com.tangem.common.extensions.toHexString import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler -import com.tangem.tap.common.extensions.readAssetAsString import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.network.createMoshi import com.tangem.tap.tangemSdkManager -class TwinCardsManager(private val scanResponse: ScanResponse, context: Context) { +class TwinCardsManager(private val scanResponse: ScanResponse, assetReader: AssetReader) { private val currentCardId: String = scanResponse.card.cardId - // private val secondCardId: String? = TwinsHelper.getTwinsCardId(currentCardId) + // private val secondCardId: Strings? = TwinsHelper.getTwinsCardId(currentCardId) private val secondCardId: String? = null private var currentCardPublicKey: String? = null private var secondCardPublicKey: String? = null - private val issuerKeyPair: KeyPair = getIssuerKeys( - context, scanResponse.card.issuer.publicKey.toHexString() - ) + private val issuerKeyPair: KeyPair = getIssuerKeys(assetReader, scanResponse.card.issuer.publicKey.toHexString()) suspend fun createFirstWallet(message: Message): SimpleResult { val response = tangemSdkManager.runTaskAsync( @@ -108,8 +104,8 @@ class TwinCardsManager(private val scanResponse: ScanResponse, context: Context) } companion object { - private fun getIssuerKeys(context: Context, publicKey: String): KeyPair { - val issuer = getIssuers(context).first { it.publicKey == publicKey } + private fun getIssuerKeys(reader: AssetReader, publicKey: String): KeyPair { + val issuer = getIssuers(reader).first { it.publicKey == publicKey } return KeyPair( publicKey = issuer.publicKey.hexToBytes(), privateKey = issuer.privateKey.hexToBytes() @@ -122,13 +118,17 @@ class TwinCardsManager(private val scanResponse: ScanResponse, context: Context) ) } - private fun getIssuers(context: Context): List { - val file = context.readAssetAsString("tangem-app-config/issuers") + private fun getIssuers(reader: AssetReader): List { + val file = reader.readAssetAsString("tangem-app-config/issuers") return getAdapter().fromJson(file)!! } } } +interface AssetReader { + fun readAssetAsString(name: String): String +} + private class Issuer( val id: String, val privateKey: String, diff --git a/app/src/main/java/com/tangem/tap/domain/twins/TwinsCardWidget.kt b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsWidget.kt similarity index 100% rename from app/src/main/java/com/tangem/tap/domain/twins/TwinsCardWidget.kt rename to app/src/main/java/com/tangem/tap/domain/twins/TwinCardsWidget.kt 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 efa81149b7..31d11dbc31 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 @@ -95,7 +95,7 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber + if (scanResponse.isTangemTwins()) { + scanResponse.card.getTwinCardNumber()?.let { + val configManager = store.state.globalState.configManager + val isCreatingTwinsAllowed = configManager?.config?.isCreatingTwinCardsAllowed ?: false + store.dispatch(TwinCardsAction.SetTwinCard(it, null, isCreatingTwinsAllowed)) + } + } + if (OnboardingHelper.isOnboardingCase(scanResponse)) { val navigateTo = OnboardingHelper.whereToNavigate(scanResponse) store.dispatch(GlobalAction.Onboarding.Start(scanResponse)) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt index 6bf7ca96bf..173662c65e 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt @@ -19,7 +19,10 @@ import com.tangem.tap.common.postUi import com.tangem.tap.common.toggleWidget.RefreshBalanceWidget import com.tangem.tap.common.transitions.InternalNoteLayoutTransition import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment -import com.tangem.tap.features.onboarding.products.note.redux.* +import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteAction +import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteResources +import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteState +import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteStep import com.tangem.tap.store import com.tangem.wallet.R import kotlinx.android.synthetic.main.fragment_onboarding_main.* @@ -43,14 +46,14 @@ class OnboardingNoteFragment : BaseOnboardingFragment() { toolbar.setTitle(R.string.onboarding_title) btnRefreshBalanceWidget = RefreshBalanceWidget(onboarding_main_container) - val resources = AndroidResources( - OnboardingStringResources( + + val resources = OnboardingNoteResources( + OnboardingNoteResources.Strings( R.string.copy_toast_msg ) ) - - store.dispatch(OnboardingNoteAction.LoadCardArtwork) store.dispatch(OnboardingNoteAction.SetResources(resources)) + store.dispatch(OnboardingNoteAction.LoadCardArtwork) store.dispatch(OnboardingNoteAction.DetermineStepOfScreen) } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt index 798f8f4136..1948bfe98c 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt @@ -10,7 +10,7 @@ import org.rekotlin.Action */ sealed class OnboardingNoteAction : Action { // from user, ui - data class SetResources(val resources: AndroidResources) : OnboardingNoteAction() + data class SetResources(val resources: OnboardingNoteResources) : OnboardingNoteAction() object LoadCardArtwork : OnboardingNoteAction() object CreateWallet : OnboardingNoteAction() object TopUp : OnboardingNoteAction() diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt index d4f1c2bcb7..33c5cbdd7d 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt @@ -10,7 +10,7 @@ import org.rekotlin.StateType */ data class OnboardingNoteState( val walletManager: WalletManager? = null, - val resources: AndroidResources = AndroidResources(), + val resources: OnboardingNoteResources = OnboardingNoteResources(), // UI val cardArtwork: Artwork? = null, val walletBalance: OnboardingWalletBalance = OnboardingWalletBalance.loading(), @@ -27,10 +27,10 @@ enum class OnboardingNoteStep { None, CreateWallet, TopUpWallet, Done } -data class AndroidResources( - val strings: OnboardingStringResources = OnboardingStringResources() -) - -data class OnboardingStringResources( - val addressWasCopied: Int = -1 -) \ No newline at end of file +data class OnboardingNoteResources( + val strings: Strings = Strings() +) { + data class Strings( + val addressWasCopied: Int = -1 + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsAction.kt b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsAction.kt index cf7d2433dd..383481a905 100644 --- a/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsAction.kt +++ b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsAction.kt @@ -1,62 +1,62 @@ package com.tangem.tap.features.twins.redux -import android.content.Context import com.tangem.Message -import com.tangem.tap.common.redux.NotificationAction +import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.domain.tasks.product.ScanResponse +import com.tangem.tap.domain.twins.AssetReader import com.tangem.tap.domain.twins.TwinCardNumber -import com.tangem.wallet.R +import com.tangem.tap.domain.twins.TwinCardsManager import org.rekotlin.Action /** [REDACTED_AUTHOR] */ sealed class TwinCardsAction : Action { - object ShowOnboarding : TwinCardsAction() - object SetOnboardingShown : TwinCardsAction() + + data class SetResources(val resources: TwinCardsResources) : TwinCardsAction() + data class SetTwinCard( val number: TwinCardNumber, val secondCardId: String?, val isCreatingTwinCardsAllowed: Boolean, ) : TwinCardsAction() - sealed class CreateWallet : TwinCardsAction() { + sealed class CardsManager { + data class Set(val manager: TwinCardsManager) : TwinCardsAction() + object Release : TwinCardsAction() + } + + object ShowOnboarding : TwinCardsAction() + object SetOnboardingShown : TwinCardsAction() + object ProceedToCreateWallet : TwinCardsAction() + + sealed class Wallet : TwinCardsAction() { + object HandleOnBackPressed : TwinCardsAction() + data class Create( val number: TwinCardNumber, val createTwinWalletMode: CreateTwinWalletMode - ) : CreateWallet() + ) : TwinCardsAction() - object NotEmpty : CreateWallet(), NotificationAction { - override val messageResource = R.string.details_notification_erase_wallet_not_possible - } - - object ShowAlert : CreateWallet() - object HideAlert : CreateWallet() - object Proceed : CreateWallet() - - object Cancel : CreateWallet() { - object Confirm : CreateWallet() - } + data class InterruptDialog(val interrupt: () -> Unit) : TwinCardsAction(), StateDialog data class LaunchFirstStep( - val message: Message, val context: Context - ) : CreateWallet() { - object Success : CreateWallet() - object Failure : CreateWallet() + val initialMessage: Message, + val reader: AssetReader + ) : TwinCardsAction() { + object Success : TwinCardsAction() } data class LaunchSecondStep( val initialMessage: Message, val preparingMessage: Message, val creatingWalletMessage: Message, - ) : CreateWallet() { - object Success : CreateWallet() - object Failure : CreateWallet() + ) : TwinCardsAction() { + object Success : TwinCardsAction() } - data class LaunchThirdStep(val message: Message) : CreateWallet() { - data class Success(val scanResponse: ScanResponse) : CreateWallet() - object Failure : CreateWallet() + data class LaunchThirdStep(val message: Message) : TwinCardsAction() { + data class Success(val scanResponse: ScanResponse) : TwinCardsAction() } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsMiddleware.kt index 673e0616d5..84c837d778 100644 --- a/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsMiddleware.kt @@ -2,6 +2,8 @@ package com.tangem.tap.features.twins.redux import com.tangem.blockchain.extensions.Result import com.tangem.blockchain.extensions.SimpleResult +import com.tangem.tap.common.extensions.dispatchDialogShow +import com.tangem.tap.common.extensions.dispatchNotification import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction @@ -32,12 +34,11 @@ private val twinsWalletMiddleware: Middleware = { dispatch, state -> } } -private var twinsManager: TwinCardsManager? = null - private fun handle(action: Action, dispatch: DispatchFunction) { val action = action as? TwinCardsAction ?: return val twinCardsState = store.state.twinCardsState + when (action) { is TwinCardsAction.SetTwinCard -> { val showOnboarding = !preferencesStorage.wasTwinsOnboardingShown() @@ -46,7 +47,22 @@ private fun handle(action: Action, dispatch: DispatchFunction) { TwinCardsAction.SetOnboardingShown -> { preferencesStorage.saveTwinsOnboardingShown() } - is TwinCardsAction.CreateWallet.Create -> { + is TwinCardsAction.ProceedToCreateWallet -> { + store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWallet)) + } + is TwinCardsAction.Wallet.HandleOnBackPressed -> { + if (twinCardsState.createWalletState?.showAlert == true) { + val stateDialog = TwinCardsAction.Wallet.InterruptDialog { + store.dispatch(TwinCardsAction.CardsManager.Release) + store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) + } + store.dispatchDialogShow(stateDialog) + } else { + store.dispatch(TwinCardsAction.CardsManager.Release) + store.dispatch(NavigationAction.PopBackTo()) + } + } + is TwinCardsAction.Wallet.Create -> { val wallet = store.state.walletState.walletManagers.map { it.wallet }.firstOrNull() if (wallet == null) { store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWalletWarning)) @@ -54,90 +70,70 @@ private fun handle(action: Action, dispatch: DispatchFunction) { } val notEmpty = wallet.recentTransactions.isNotEmpty() || wallet.amounts.toSendableAmounts().isNotEmpty() if (notEmpty) { - store.dispatch(TwinCardsAction.CreateWallet.NotEmpty) + store.dispatchNotification(twinCardsState.resources.strings.walletIsNotEmpty) } else { store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWalletWarning)) } } - is TwinCardsAction.CreateWallet.Proceed -> { - store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWallet)) - } - is TwinCardsAction.CreateWallet.Cancel -> { - val step = twinCardsState.createWalletState?.step - if (step != null && step != CreateTwinWalletStep.FirstStep) { - store.dispatch(TwinCardsAction.CreateWallet.ShowAlert) - } else { - twinsManager = null - store.dispatch(NavigationAction.PopBackTo()) - } - } - is TwinCardsAction.CreateWallet.Cancel.Confirm -> { - twinsManager = null - store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) - } - is TwinCardsAction.CreateWallet.LaunchFirstStep -> { - store.state.globalState.scanResponse?.let { - twinsManager = TwinCardsManager(it, action.context) - } + is TwinCardsAction.Wallet.LaunchFirstStep -> { + val scanResponse = store.state.globalState.scanResponse ?: return + + val manager = TwinCardsManager(scanResponse, action.reader) + store.dispatch(TwinCardsAction.CardsManager.Set(manager)) + scope.launch { - val result = twinsManager?.createFirstWallet(action.message) + val result = manager.createFirstWallet(action.initialMessage) withContext(Dispatchers.Main) { when (result) { - SimpleResult.Success -> - store.dispatch(TwinCardsAction.CreateWallet.LaunchFirstStep.Success) - is SimpleResult.Failure -> - store.dispatch(TwinCardsAction.CreateWallet.LaunchFirstStep.Failure) + SimpleResult.Success -> { + store.dispatch(TwinCardsAction.Wallet.LaunchFirstStep.Success) + } + is SimpleResult.Failure -> { + } } } } } - TwinCardsAction.CreateWallet.LaunchFirstStep.Success -> { + is TwinCardsAction.Wallet.LaunchSecondStep -> { + val manager = twinCardsState.twinCardsManager ?: return - } - TwinCardsAction.CreateWallet.LaunchFirstStep.Failure -> { - - } - is TwinCardsAction.CreateWallet.LaunchSecondStep -> scope.launch { - val result = twinsManager?.createSecondWallet(action.initialMessage, + val result = manager.createSecondWallet(action.initialMessage, action.preparingMessage, action.creatingWalletMessage) withContext(Dispatchers.Main) { when (result) { - SimpleResult.Success -> - store.dispatch(TwinCardsAction.CreateWallet.LaunchSecondStep.Success) - is SimpleResult.Failure -> - store.dispatch(TwinCardsAction.CreateWallet.LaunchSecondStep.Failure) + SimpleResult.Success -> { + store.dispatch(TwinCardsAction.Wallet.LaunchSecondStep.Success) + } + is SimpleResult.Failure -> { + } } } } - TwinCardsAction.CreateWallet.LaunchSecondStep.Success -> { - } - TwinCardsAction.CreateWallet.LaunchSecondStep.Failure -> { + is TwinCardsAction.Wallet.LaunchThirdStep -> { + val manager = twinCardsState.twinCardsManager ?: return - } - is TwinCardsAction.CreateWallet.LaunchThirdStep -> { scope.launch { - val result = twinsManager?.complete(action.message) + val result = manager.complete(action.message) withContext(Dispatchers.Main) { when (result) { - is Result.Success -> - store.dispatch(TwinCardsAction.CreateWallet.LaunchThirdStep.Success(result.data)) - is Result.Failure -> - store.dispatch(TwinCardsAction.CreateWallet.LaunchThirdStep.Failure) + is Result.Success -> { + store.dispatch(TwinCardsAction.Wallet.LaunchThirdStep.Success(result.data)) + } + is Result.Failure -> { + + } } } } } - is TwinCardsAction.CreateWallet.LaunchThirdStep.Success -> { + is TwinCardsAction.Wallet.LaunchThirdStep.Success -> { scope.launch { store.state.globalState.tapWalletManager.onCardScanned(action.scanResponse) } store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) } - TwinCardsAction.CreateWallet.LaunchThirdStep.Failure -> { - - } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsReducer.kt b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsReducer.kt index 6f9e3a6f57..89978790a1 100644 --- a/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsReducer.kt @@ -12,52 +12,49 @@ class TwinCardsReducer { private fun internalReduce(action: Action, state: AppState): TwinCardsState { if (action !is TwinCardsAction) return state.twinCardsState - val twinCardsState = state.twinCardsState + val state = state.twinCardsState return when (action) { + is TwinCardsAction.SetResources -> { + state.copy(resources = action.resources) + } is TwinCardsAction.SetTwinCard -> { - twinCardsState.copy( + state.copy( cardNumber = action.number, secondCardId = action.secondCardId, - showTwinOnboarding = state.twinCardsState.showTwinOnboarding, + showTwinOnboarding = state.showTwinOnboarding, isCreatingTwinCardsAllowed = action.isCreatingTwinCardsAllowed, ) } + is TwinCardsAction.CardsManager.Set -> { + state.copy(twinCardsManager = action.manager) + } + TwinCardsAction.CardsManager.Release -> { + state.copy(twinCardsManager = null) + } is TwinCardsAction.ShowOnboarding -> { - state.twinCardsState.copy(showTwinOnboarding = true) + state.copy(showTwinOnboarding = true) } is TwinCardsAction.SetOnboardingShown -> { - twinCardsState.copy(showTwinOnboarding = false) + state.copy(showTwinOnboarding = false) } - is TwinCardsAction.CreateWallet.Create -> { - val prevState = twinCardsState.createWalletState - twinCardsState.copy(createWalletState = CreateTwinWalletState( + is TwinCardsAction.Wallet.Create -> { + val prevState = state.createWalletState + state.copy(createWalletState = CreateTwinWalletState( prevState?.scanResponse, prevState?.number ?: action.number, prevState?.mode ?: action.createTwinWalletMode, )) } - TwinCardsAction.CreateWallet.LaunchFirstStep.Success -> { - twinCardsState.copy(createWalletState = twinCardsState.createWalletState?.copy( + TwinCardsAction.Wallet.LaunchFirstStep.Success -> { + state.copy(createWalletState = state.createWalletState?.copy( step = CreateTwinWalletStep.SecondStep )) } - TwinCardsAction.CreateWallet.LaunchSecondStep.Success -> - twinCardsState.copy(createWalletState = twinCardsState.createWalletState?.copy( + TwinCardsAction.Wallet.LaunchSecondStep.Success -> + state.copy(createWalletState = state.createWalletState?.copy( step = CreateTwinWalletStep.ThirdStep )) - TwinCardsAction.CreateWallet.ShowAlert -> twinCardsState - TwinCardsAction.CreateWallet.HideAlert -> twinCardsState - is TwinCardsAction.CreateWallet.Proceed -> twinCardsState - TwinCardsAction.CreateWallet.NotEmpty -> twinCardsState - TwinCardsAction.CreateWallet.Cancel -> twinCardsState - TwinCardsAction.CreateWallet.Cancel.Confirm -> twinCardsState - is TwinCardsAction.CreateWallet.LaunchFirstStep -> twinCardsState - TwinCardsAction.CreateWallet.LaunchFirstStep.Failure -> twinCardsState - is TwinCardsAction.CreateWallet.LaunchSecondStep -> twinCardsState - TwinCardsAction.CreateWallet.LaunchSecondStep.Failure -> twinCardsState - is TwinCardsAction.CreateWallet.LaunchThirdStep -> twinCardsState - is TwinCardsAction.CreateWallet.LaunchThirdStep.Success -> twinCardsState - TwinCardsAction.CreateWallet.LaunchThirdStep.Failure -> twinCardsState + else -> state } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsState.kt b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsState.kt index 49a43b9d93..f23ac31930 100644 --- a/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsState.kt +++ b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsState.kt @@ -2,22 +2,22 @@ package com.tangem.tap.features.twins.redux import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.domain.twins.TwinCardNumber +import com.tangem.tap.domain.twins.TwinCardsManager import org.rekotlin.StateType /** [REDACTED_AUTHOR] */ data class TwinCardsState( + val twinCardsManager: TwinCardsManager? = null, // if cardNumber is not set -> the scanned card is not Twin val cardNumber: TwinCardNumber? = null, val secondCardId: String? = null, val showTwinOnboarding: Boolean = false, val isCreatingTwinCardsAllowed: Boolean = false, - val createWalletState: CreateTwinWalletState? = null -) : StateType { - val isReadyToUse: Boolean - get() = cardNumber != null -} + val createWalletState: CreateTwinWalletState? = null, + val resources: TwinCardsResources = TwinCardsResources(), +) : StateType data class CreateTwinWalletState( val scanResponse: ScanResponse?, @@ -26,9 +26,18 @@ data class CreateTwinWalletState( val step: CreateTwinWalletStep = CreateTwinWalletStep.FirstStep, ) { val showAlert: Boolean - get() = mode != CreateTwinWalletMode.RecreateWallet + get() = step != CreateTwinWalletStep.FirstStep } enum class CreateTwinWalletStep { FirstStep, SecondStep, ThirdStep } enum class CreateTwinWalletMode { CreateWallet, RecreateWallet } + +data class TwinCardsResources( + val strings: Strings = Strings() +) { + //TODO: create global structure for StringRes. Set it from the MainActivity. + data class Strings( + val walletIsNotEmpty: Int = -1 + ) +} diff --git a/app/src/main/java/com/tangem/tap/features/twins/ui/CreateTwinWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/twins/ui/CreateTwinWalletFragment.kt index 0c23906aaf..94c5a099bc 100644 --- a/app/src/main/java/com/tangem/tap/features/twins/ui/CreateTwinWalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/twins/ui/CreateTwinWalletFragment.kt @@ -1,20 +1,17 @@ package com.tangem.tap.features.twins.ui -import android.app.Dialog import android.os.Bundle import android.view.View import androidx.activity.OnBackPressedCallback import androidx.core.content.ContextCompat.getColor import androidx.fragment.app.Fragment import androidx.transition.TransitionInflater -import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.squareup.picasso.Picasso import com.tangem.Message +import com.tangem.tap.common.extensions.readAssetAsString +import com.tangem.tap.domain.twins.AssetReader import com.tangem.tap.domain.twins.TwinCardNumber -import com.tangem.tap.features.twins.redux.CreateTwinWalletMode -import com.tangem.tap.features.twins.redux.CreateTwinWalletStep -import com.tangem.tap.features.twins.redux.TwinCardsAction -import com.tangem.tap.features.twins.redux.TwinCardsState +import com.tangem.tap.features.twins.redux.* import com.tangem.tap.features.wallet.redux.Artwork import com.tangem.tap.store import com.tangem.wallet.R @@ -24,20 +21,35 @@ import org.rekotlin.StoreSubscriber class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create), StoreSubscriber { - private var dialog: Dialog? = null + private val assetReader: AssetReader by lazy { + object : AssetReader { + override fun readAssetAsString(name: String): String = requireContext().readAssetAsString(name) + } + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { - store.dispatch(TwinCardsAction.CreateWallet.Cancel) + store.dispatch(TwinCardsAction.Wallet.HandleOnBackPressed) } }) + + initResources() val inflater = TransitionInflater.from(requireContext()) enterTransition = inflater.inflateTransition(R.transition.slide_right) exitTransition = inflater.inflateTransition(R.transition.fade) } + private fun initResources() { + val resources = TwinCardsResources( + TwinCardsResources.Strings( + R.string.details_notification_erase_wallet_not_possible + ) + ) + store.dispatch(TwinCardsAction.SetResources(resources)) + } + override fun onStart() { super.onStart() store.subscribe(this) { state -> @@ -55,9 +67,7 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create), override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - toolbar.setNavigationOnClickListener { - store.dispatch(TwinCardsAction.CreateWallet.Cancel) - } + toolbar.setNavigationOnClickListener { store.dispatch(TwinCardsAction.Wallet.HandleOnBackPressed) } Picasso.get() .load(Artwork.TWIN_CARD_1) @@ -96,12 +106,12 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create), v_step_2.setBackgroundColor(defaultColor) v_step_3.setBackgroundColor(defaultColor) btn_tap.setOnClickListener { - store.dispatch(TwinCardsAction.CreateWallet.LaunchFirstStep( + store.dispatch(TwinCardsAction.Wallet.LaunchFirstStep( Message(getString( R.string.details_twins_recreate_title_format, twinCardNumberString )), - requireContext() + assetReader )) } btn_tap.text = getString(R.string.details_twins_recreate_button_format, @@ -117,7 +127,7 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create), v_step_2.setBackgroundColor(selectedColor) v_step_3.setBackgroundColor(defaultColor) btn_tap.setOnClickListener { - store.dispatch(TwinCardsAction.CreateWallet.LaunchSecondStep( + store.dispatch(TwinCardsAction.Wallet.LaunchSecondStep( Message(getString(R.string.details_twins_recreate_title_format, twinCardNumberString)), Message(getString(R.string.details_twins_recreate_title_preparing)), Message(getString(R.string.details_twins_recreate_title_creating_wallet)), @@ -135,7 +145,7 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create), v_step_2.setBackgroundColor(selectedColor) v_step_3.setBackgroundColor(selectedColor) btn_tap.setOnClickListener { - store.dispatch(TwinCardsAction.CreateWallet.LaunchThirdStep( + store.dispatch(TwinCardsAction.Wallet.LaunchThirdStep( Message(getString( R.string.details_twins_recreate_title_format, twinCardNumberString) ) @@ -148,27 +158,5 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create), } btn_tap.text = getString(R.string.details_twins_recreate_button_format, cardNumber) tv_twin_title.text = getString(R.string.details_twins_recreate_title_format, cardNumber) - - if (state.createWalletState?.showAlert == true) { - if (dialog == null) { - dialog = MaterialAlertDialogBuilder(requireContext()) - .setMessage(R.string.details_twins_recreate_alert) - .setPositiveButton(R.string.common_ok) { _, _ -> - store.dispatch(TwinCardsAction.CreateWallet.Cancel.Confirm) - } - .setNegativeButton(R.string.common_cancel) { _, _ -> - dialog?.cancel() - } - .setOnCancelListener { - store.dispatch(TwinCardsAction.CreateWallet.HideAlert) - } - .create() - dialog?.show() - } - } else { - dialog?.cancel() - dialog = null - } } - } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/twins/ui/TwinWalletWarningFragment.kt b/app/src/main/java/com/tangem/tap/features/twins/ui/TwinWalletWarningFragment.kt index fefff8729c..18f779a400 100644 --- a/app/src/main/java/com/tangem/tap/features/twins/ui/TwinWalletWarningFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/twins/ui/TwinWalletWarningFragment.kt @@ -39,7 +39,7 @@ class TwinWalletWarningFragment : Fragment(R.layout.fragment_twin_cards_warning) } btn_cancel.setOnClickListener { store.dispatch(NavigationAction.PopBackTo()) } - btn_start.setOnClickListener { store.dispatch(TwinCardsAction.CreateWallet.Proceed) } + btn_start.setOnClickListener { store.dispatch(TwinCardsAction.ProceedToCreateWallet) } Picasso.get() .load(Artwork.TWIN_CARD_1) .placeholder(R.drawable.card_placeholder) diff --git a/app/src/main/java/com/tangem/tap/features/twins/ui/dialog/CreateWalletInterruptDialog.kt b/app/src/main/java/com/tangem/tap/features/twins/ui/dialog/CreateWalletInterruptDialog.kt new file mode 100644 index 0000000000..ab34b78039 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/twins/ui/dialog/CreateWalletInterruptDialog.kt @@ -0,0 +1,25 @@ +package com.tangem.tap.features.twins.ui.dialog + +import android.content.Context +import androidx.appcompat.app.AlertDialog +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.tangem.tap.common.extensions.dispatchDialogHide +import com.tangem.tap.features.twins.redux.TwinCardsAction +import com.tangem.tap.store +import com.tangem.wallet.R + +/** +[REDACTED_AUTHOR] + */ +class CreateWalletInterruptDialog { + companion object { + fun create(state: TwinCardsAction.Wallet.InterruptDialog, context: Context): AlertDialog { + return MaterialAlertDialogBuilder(context) + .setMessage(R.string.details_twins_recreate_alert) + .setPositiveButton(R.string.common_ok) { _, _ -> state.interrupt() } + .setNegativeButton(R.string.common_cancel) { _, _ -> } + .setOnDismissListener { store.dispatchDialogHide() } + .create() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/models/PendingTransaction.kt b/app/src/main/java/com/tangem/tap/features/wallet/models/PendingTransaction.kt index 492513e02c..29fe72df99 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/models/PendingTransaction.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/models/PendingTransaction.kt @@ -65,5 +65,5 @@ fun Wallet.getPendingTransactions(): List { } fun Wallet.hasPendingTransactions(): Boolean { - return recentTransactions.toPendingTransactions(address).isNotEmpty() + return getPendingTransactions().isNotEmpty() } \ No newline at end of file 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 61b4423e33..bdf79de67a 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 @@ -97,7 +97,7 @@ class WalletMiddleware { } is WalletAction.CreateWallet -> { if (walletState.isTangemTwins) { - store.dispatch(TwinCardsAction.CreateWallet.Create( + store.dispatch(TwinCardsAction.Wallet.Create( walletState.twinCardsState.cardNumber!!, CreateTwinWalletMode.CreateWallet )) From 0acf130492776f0dd187c314e656c860b9c42e3c Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 22 Oct 2021 12:19:51 +0300 Subject: [PATCH 085/189] Updated on 2026-08-14 --- app/src/main/assets/tangem-app-config | 2 +- .../domain/twins/CreateSecondTwinWalletTask.kt | 1 + .../tap/features/details/redux/DetailsReducer.kt | 16 ++++++++-------- .../wallet/redux/middlewares/WalletMiddleware.kt | 8 +------- 4 files changed, 11 insertions(+), 16 deletions(-) diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index 0cf3af21e6..e80938f7d9 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit 0cf3af21e6f441e6323a1ba127ee7518168797df +Subproject commit e80938f7d9ba378a91ac544a9170597e64afa696 diff --git a/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt index bd406430fe..966fe4f1d1 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt @@ -6,6 +6,7 @@ import com.tangem.common.KeyPair import com.tangem.common.card.EllipticCurve import com.tangem.common.core.CardSession import com.tangem.common.core.CardSessionRunnable +import com.tangem.common.core.TangemSdkError import com.tangem.common.extensions.hexToBytes import com.tangem.operations.wallet.CreateWalletCommand import com.tangem.operations.wallet.CreateWalletResponse diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt index cfd00ae031..91f7a75152 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt @@ -64,7 +64,6 @@ private fun handlePrepareScreen(action: DetailsAction.PrepareScreen, state: Deta appCurrencyState = AppCurrencyState( action.fiatCurrencyName ), - securityScreenState = SecurityScreenState(currentOption = securityOption), createTwinWalletState = twinsState, cardTermsOfUseUrl = action.cardTou.getUrl(action.card) ) @@ -174,14 +173,15 @@ private fun handleSecurityAction( } is DetailsAction.ManageSecurity.SaveChanges.Success -> { // Setting options to show only LongTap from now on for non-twins - val currentOption = state.securityScreenState?.selectedOption state.copy( - securityScreenState = state.securityScreenState?.copy( - currentOption = currentOption, - allowedOptions = state.card?.let { - prepareAllowedSecurityOptions(it, currentOption) } - ?: EnumSet.of(SecurityOption.LongTap) - )) + securityScreenState = state.securityScreenState?.copy( + currentOption = state.securityScreenState.selectedOption, + allowedOptions = state.card?.let { + prepareAllowedSecurityOptions( + it, state.securityScreenState.selectedOption + ) + } ?: EnumSet.of(SecurityOption.LongTap) + )) } else -> state 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 153078f584..8a680fcf1c 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 @@ -108,14 +108,8 @@ class WalletMiddleware { ) when (result) { is CompletionResult.Success -> { - val card = globalState?.scanNoteResponse?.card?.let { - result.data.copy( - isPin1Default = it.isPin1Default, - isPin2Default = it.isPin2Default, - ) - } ?: result.data val scanNoteResponse = - globalState?.scanNoteResponse?.copy(card = card) + globalState?.scanNoteResponse?.copy(card = result.data) scanNoteResponse?.let { globalState.tapWalletManager.onCardScanned(scanNoteResponse) } From 7aea15323a8caea17fb633bd4f55a2f81e047ba8 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 22 Oct 2021 15:59:39 +0300 Subject: [PATCH 086/189] Updated on 2026-08-14 --- .../tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 5ebfb1cb14..e2ac447999 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 @@ -61,7 +61,7 @@ class WalletConnectSdkHelper { val decimals = blockchain.decimals() - val value = transaction.value?.hexToBigDecimal() + val value = (transaction.value ?: "0").hexToBigDecimal() ?.movePointLeft(decimals) ?: return null val gasPrice = transaction.gasPrice?.hexToBigDecimal() From 167dae1f7598c53a962b80045814c74619a3e39f Mon Sep 17 00:00:00 2001 From: Tangem Date: Sun, 24 Oct 2021 18:06:07 +0300 Subject: [PATCH 087/189] Updated on 2026-08-14 --- app/src/main/assets/tangem-app-config | 2 +- .../java/com/tangem/tap/domain/tasks/ScanNoteTask.kt | 4 +++- .../com/tangem/tap/domain/twins/TwinCardsManager.kt | 12 +----------- .../tap/features/details/redux/DetailsReducer.kt | 3 +-- 4 files changed, 6 insertions(+), 15 deletions(-) diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index e80938f7d9..0cf3af21e6 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit e80938f7d9ba378a91ac544a9170597e64afa696 +Subproject commit 0cf3af21e6f441e6323a1ba127ee7518168797df diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt index 274f14bfa5..43425eab48 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt @@ -1,6 +1,8 @@ package com.tangem.tap.domain.tasks import com.tangem.* +import com.tangem.blockchain.common.BlockchainSdkConfig +import com.tangem.blockchain.common.WalletManagerFactory import com.tangem.common.CompletionResult import com.tangem.common.card.Card import com.tangem.common.card.FirmwareVersion @@ -18,6 +20,7 @@ import com.tangem.tap.domain.extensions.getSingleWallet import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.domain.twins.TwinCardsManager import com.tangem.tap.domain.twins.isTangemTwin +import com.tangem.tap.store class ScanNoteTask(val card: Card? = null) : CardSessionRunnable { @@ -118,7 +121,6 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable { private fun getErrorIfExcludedCard(card: Card): TangemError? { if (card.isExcluded()) return TapSdkError.CardForDifferentApp // Disable new cards on the old version of the app // TODO: remove when cards are supported - if (card.isMultiCurrencyWallet() || card.isNote()) return UpdateAppToUseThisCard() // Disable new multi-currency HD wallet cards on the old version of the app // if (card.isMultiCurrencyWallet()) return UpdateAppToUseThisCard() return null 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 b951b725bf..faa839cbe6 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 @@ -65,9 +65,7 @@ class TwinCardsManager(private val scanResponse: ScanResponse, context: Context) preparingMessage = preparingMessage, creatingWalletMessage = creatingWalletMessage ) - val response = tangemSdkManager.runTaskAsync( - task, null, initialMessage - ) + val response = tangemSdkManager.runTaskAsync(task, null, initialMessage) when (response) { is CompletionResult.Success -> { secondCardPublicKey = response.data.wallet.publicKey.toHexString() @@ -116,14 +114,6 @@ class TwinCardsManager(private val scanResponse: ScanResponse, context: Context) return CryptoUtils.verify(cardWalletPublicKey, publicKey, signedKey) } - fun verifyTwinPublicKey(issuerData: ByteArray, cardWalletPublicKey: ByteArray?): Boolean { - if (issuerData.size < 65) return false - val publicKey = issuerData.sliceArray(0 until 65) - val signedKey = issuerData.sliceArray(65 until issuerData.size) - return (cardWalletPublicKey != null && - CryptoUtils.verify(cardWalletPublicKey, publicKey, signedKey)) - } - private fun getIssuerKeys(context: Context, publicKey: String): KeyPair { val issuer = getIssuers(context).first { it.publicKey == publicKey } return KeyPair( diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt index b22f25959e..d2bb330e40 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt @@ -191,10 +191,9 @@ private fun prepareAllowedSecurityOptions( ): EnumSet { val prohibitDefaultPin = card?.settings?.isResettingUserCodesAllowed != true - val prohibitDefaultPin = card?.settings?.isRemovingAccessCodeAllowed != true val allowedSecurityOptions = EnumSet.noneOf(SecurityOption::class.java) - if (card?.isTwinCard() == true) { + if (card?.isTangemTwin() == true) { allowedSecurityOptions.add(SecurityOption.PassCode) } if ((currentSecurityOption == SecurityOption.LongTap) || !prohibitDefaultPin) { From 73fe2a87afb707ee7a6b556e253663e0da1d5697 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sun, 24 Oct 2021 18:35:16 +0300 Subject: [PATCH 088/189] Updated on 2026-08-14 --- .../java/com/tangem/tap/domain/tasks/ScanNoteTask.kt | 9 --------- .../tangem/tap/features/details/redux/DetailsReducer.kt | 1 + .../onboarding/products/twins/OnboardingTwinsFragment.kt | 6 ++---- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt index 820b159123..22a7e5ca87 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt @@ -1,8 +1,6 @@ package com.tangem.tap.domain.tasks import com.tangem.* -import com.tangem.blockchain.common.BlockchainSdkConfig -import com.tangem.blockchain.common.WalletManagerFactory import com.tangem.common.CompletionResult import com.tangem.common.card.Card import com.tangem.common.card.FirmwareVersion @@ -123,11 +121,4 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable { // if (card.isMultiCurrencyWallet()) return UpdateAppToUseThisCard() return null } - - private fun getWalletManagerFactory(): WalletManagerFactory { - val blockchainSdkConfig = store.state.globalState.configManager?.config - ?.blockchainSdkConfig ?: BlockchainSdkConfig() - return WalletManagerFactory(blockchainSdkConfig) - } - } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt index 14884b13f6..d2622417f7 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt @@ -7,6 +7,7 @@ import com.tangem.tap.domain.TapWorkarounds.isStart2Coin import com.tangem.tap.domain.extensions.isWalletDataSupported import com.tangem.tap.domain.extensions.signedHashesCount import com.tangem.tap.domain.extensions.toSendableAmounts +import com.tangem.tap.domain.twins.isTangemTwin import com.tangem.tap.features.wallet.models.hasPendingTransactions import org.rekotlin.Action import java.util.* diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/OnboardingTwinsFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/OnboardingTwinsFragment.kt index 791c31589c..684d05324e 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/OnboardingTwinsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/OnboardingTwinsFragment.kt @@ -10,7 +10,6 @@ import com.tangem.tap.common.extensions.dispatchDebugErrorNotification import com.tangem.tap.common.extensions.withMainContext import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction -import com.tangem.tap.domain.twins.TwinsHelper import com.tangem.tap.features.twins.redux.TwinCardsAction import com.tangem.tap.features.wallet.redux.Artwork import com.tangem.tap.scope @@ -37,9 +36,8 @@ class OnboardingTwinsFragment : Fragment(R.layout.fragment_twin_cards_welcome) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - store.dispatch(TwinCardsAction.SetOnboardingShown)) - val secondTwinNumber = - store.state.twinCardsState?.cardNumber?.pairNumber()?.number ?: "" + store.dispatch(TwinCardsAction.SetOnboardingShown) + val secondTwinNumber = store.state.twinCardsState.cardNumber?.pairNumber()?.number ?: "" val text = getString(R.string.twins_onboarding_description_format, secondTwinNumber) tv_twin_cards_description_1.text = text From cf97a1778d5ed953a8483b76bda0eb3c7443a9ba Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 25 Oct 2021 01:35:57 +0300 Subject: [PATCH 089/189] Updated on 2026-08-14 --- .../onboarding/products/note/redux/OnboardingNoteAction.kt | 2 +- .../onboarding/products/note/redux/OnboardingNoteMiddleware.kt | 2 +- .../onboarding/products/note/redux/OnboardingNoteReducer.kt | 2 +- .../products/otherCards/OnboardingOtherCardsFragment.kt | 1 - .../products/otherCards/redux/OnboardingOtherCardsAction.kt | 2 +- .../products/otherCards/redux/OnboardingOtherCardsMiddleware.kt | 2 +- .../products/otherCards/redux/OnboardingOtherCardsReducer.kt | 2 +- 7 files changed, 6 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt index 1948bfe98c..859e1104fa 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt @@ -17,7 +17,7 @@ sealed class OnboardingNoteAction : Action { object ShowAddressInfoDialog : OnboardingNoteAction() // from redux - class SetArtworkUrl(val artwork: Artwork) : OnboardingNoteAction() + class SetArtwork(val artwork: Artwork) : OnboardingNoteAction() data class SetWalletManager(val walletManager: WalletManager) : OnboardingNoteAction() object DetermineStepOfScreen : OnboardingNoteAction() object Done : OnboardingNoteAction() diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt index 0a925dc7ee..8267514eb0 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt @@ -50,7 +50,7 @@ private fun handleNoteAction(action: Action, dispatch: DispatchFunction) { is OnboardingNoteAction.LoadCardArtwork -> { scope.launch { val artwork = onboardingManager.loadArtwork() - withMainContext { store.dispatch(OnboardingNoteAction.SetArtworkUrl(artwork)) } + withMainContext { store.dispatch(OnboardingNoteAction.SetArtwork(artwork)) } } } is OnboardingNoteAction.DetermineStepOfScreen -> { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt index 03415eec84..c88906cb20 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt @@ -17,7 +17,7 @@ private fun internalReduce(action: Action, appState: AppState): OnboardingNoteSt is GlobalAction.Onboarding.Start -> { state = OnboardingNoteState() } - is OnboardingNoteAction.SetArtworkUrl -> { + is OnboardingNoteAction.SetArtwork -> { state = state.copy(cardArtwork = action.artwork) } is OnboardingNoteAction.SetWalletManager -> { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt index ebdbe7d4c1..abfb32f6d5 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt @@ -10,7 +10,6 @@ import androidx.transition.TransitionManager import com.tangem.tap.common.extensions.getDrawableCompat import com.tangem.tap.common.transitions.InternalNoteLayoutTransition import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment -import com.tangem.tap.features.onboarding.products.note.swapToBitmapDrawable import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsAction import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsState import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsStep diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsAction.kt index 44bdec53c8..b48570b6c9 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsAction.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsAction.kt @@ -12,7 +12,7 @@ sealed class OnboardingOtherCardsAction : Action { object CreateWallet : OnboardingOtherCardsAction() // from redux - class SetArtworkUrl(val artwork: Artwork) : OnboardingOtherCardsAction() + class SetArtwork(val artwork: Artwork) : OnboardingOtherCardsAction() object DetermineStepOfScreen : OnboardingOtherCardsAction() object Done : OnboardingOtherCardsAction() diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt index e775176f10..6fea776c74 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt @@ -44,7 +44,7 @@ private fun handleOtherCardsAction(action: Action, dispatch: DispatchFunction) { is OnboardingOtherCardsAction.LoadCardArtwork -> { scope.launch { val artwork = onboardingManager.loadArtwork() - withMainContext { store.dispatch(OnboardingOtherCardsAction.SetArtworkUrl(artwork)) } + withMainContext { store.dispatch(OnboardingOtherCardsAction.SetArtwork(artwork)) } } } is OnboardingOtherCardsAction.DetermineStepOfScreen -> { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsReducer.kt index a6313b9199..129b7ea280 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsReducer.kt @@ -17,7 +17,7 @@ private fun internalReduce(action: Action, appState: AppState): OnboardingOtherC is GlobalAction.Onboarding.Start -> { state = OnboardingOtherCardsState() } - is OnboardingOtherCardsAction.SetArtworkUrl -> { + is OnboardingOtherCardsAction.SetArtwork -> { state = state.copy(cardArtwork = action.artwork) } is OnboardingOtherCardsAction.SetStepOfScreen -> { From 61cf84a62650ee7e308806b8309e59a0e241e7ea Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 25 Oct 2021 15:44:05 +0300 Subject: [PATCH 090/189] Updated on 2026-08-14 --- app/build.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 2d9e53e7e6..0b53a8df5e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -69,16 +69,16 @@ dependencies { implementation 'androidx.core:core-ktx:1.6.0' implementation 'androidx.appcompat:appcompat:1.3.1' implementation 'androidx.fragment:fragment-ktx:1.3.6' - implementation 'androidx.constraintlayout:constraintlayout:2.1.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.1' implementation 'com.google.android.material:material:1.4.0' implementation "androidx.core:core-ktx:1.6.0" - implementation 'com.google.android.play:core:1.10.0' + implementation 'com.google.android.play:core:1.10.2' 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-42' - implementation 'com.tangem.tangem-sdk-kotlin:core:develop-88' - implementation 'com.tangem.tangem-sdk-kotlin:android:develop-88' + implementation 'com.tangem.tangem-sdk-kotlin:core:develop-91' + implementation 'com.tangem.tangem-sdk-kotlin:android:develop-91' // WebView implementation "androidx.browser:browser:1.3.0" From df6fe358986ce3be1f594a4fe585429cc02ee61f Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 26 Oct 2021 18:04:55 +0300 Subject: [PATCH 091/189] Updated on 2026-08-14 --- Gemfile.lock | 184 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 Gemfile.lock diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000000..9dd60618bd --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,184 @@ +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.4) + rexml + addressable (2.8.0) + public_suffix (>= 2.0.2, < 5.0) + atomos (0.1.3) + aws-eventstream (1.2.0) + aws-partitions (1.519.0) + aws-sdk-core (3.121.3) + aws-eventstream (~> 1, >= 1.0.2) + aws-partitions (~> 1, >= 1.239.0) + aws-sigv4 (~> 1.1) + jmespath (~> 1.0) + aws-sdk-kms (1.50.0) + aws-sdk-core (~> 3, >= 3.121.2) + aws-sigv4 (~> 1.1) + aws-sdk-s3 (1.104.0) + aws-sdk-core (~> 3, >= 3.121.2) + aws-sdk-kms (~> 1) + aws-sigv4 (~> 1.4) + aws-sigv4 (1.4.0) + aws-eventstream (~> 1, >= 1.0.2) + babosa (1.0.4) + claide (1.0.3) + colored (1.2) + colored2 (3.1.2) + commander-fastlane (4.4.6) + highline (~> 1.7.2) + declarative (0.0.20) + declarative-option (0.1.0) + digest-crc (0.6.4) + rake (>= 12.0.0, < 14.0.0) + domain_name (0.5.20190701) + unf (>= 0.0.5, < 1.0.0) + dotenv (2.7.6) + emoji_regex (1.0.1) + excon (0.87.0) + faraday (0.17.4) + multipart-post (>= 1.2, < 3) + faraday-cookie_jar (0.0.7) + faraday (>= 0.8.0) + http-cookie (~> 1.0.0) + faraday_middleware (0.13.1) + faraday (>= 0.7.4, < 1.0) + fastimage (2.2.5) + fastlane (2.146.1) + CFPropertyList (>= 2.3, < 4.0.0) + addressable (>= 2.3, < 3.0.0) + aws-sdk-s3 (~> 1.0) + babosa (>= 1.0.2, < 2.0.0) + bundler (>= 1.12.0, < 3.0.0) + colored + commander-fastlane (>= 4.4.6, < 5.0.0) + dotenv (>= 2.1.1, < 3.0.0) + emoji_regex (>= 0.1, < 2.0) + excon (>= 0.71.0, < 1.0.0) + faraday (~> 0.17) + faraday-cookie_jar (~> 0.0.6) + faraday_middleware (~> 0.13.1) + fastimage (>= 2.1.0, < 3.0.0) + gh_inspector (>= 1.1.2, < 2.0.0) + google-api-client (>= 0.29.2, < 0.37.0) + google-cloud-storage (>= 1.15.0, < 2.0.0) + highline (>= 1.7.2, < 2.0.0) + json (< 3.0.0) + jwt (~> 2.1.0) + mini_magick (>= 4.9.4, < 5.0.0) + multi_xml (~> 0.5) + multipart-post (~> 2.0.0) + plist (>= 3.1.0, < 4.0.0) + public_suffix (~> 2.0.0) + rubyzip (>= 1.3.0, < 2.0.0) + security (= 0.1.3) + simctl (~> 1.6.3) + slack-notifier (>= 2.0.0, < 3.0.0) + terminal-notifier (>= 2.0.0, < 3.0.0) + terminal-table (>= 1.4.5, < 2.0.0) + tty-screen (>= 0.6.3, < 1.0.0) + tty-spinner (>= 0.8.0, < 1.0.0) + word_wrap (~> 1.0.0) + xcodeproj (>= 1.13.0, < 2.0.0) + xcpretty (~> 0.3.0) + xcpretty-travis-formatter (>= 0.0.3) + fastlane-plugin-firebase_app_distribution (0.3.1) + gh_inspector (1.1.3) + google-api-client (0.32.1) + addressable (~> 2.5, >= 2.5.1) + googleauth (>= 0.5, < 0.10.0) + httpclient (>= 2.8.1, < 3.0) + mini_mime (~> 1.0) + representable (~> 3.0) + retriable (>= 2.0, < 4.0) + signet (~> 0.10) + google-cloud-core (1.3.2) + google-cloud-env (~> 1.0) + google-cloud-env (1.2.1) + faraday (~> 0.11) + google-cloud-storage (1.21.1) + addressable (~> 2.5) + digest-crc (~> 0.4) + google-api-client (~> 0.26) + google-cloud-core (~> 1.2) + googleauth (>= 0.6.2, < 0.10.0) + mini_mime (~> 1.0) + googleauth (0.9.0) + faraday (~> 0.12) + jwt (>= 1.4, < 3.0) + memoist (~> 0.16) + multi_json (~> 1.11) + os (>= 0.9, < 2.0) + signet (~> 0.7) + highline (1.7.10) + http-cookie (1.0.4) + domain_name (~> 0.5) + httpclient (2.8.3) + jmespath (1.4.0) + json (2.6.1) + jwt (2.1.0) + memoist (0.16.2) + mini_magick (4.11.0) + mini_mime (1.1.2) + multi_json (1.15.0) + multi_xml (0.6.0) + multipart-post (2.0.0) + nanaimo (0.3.0) + naturally (2.2.1) + os (1.1.1) + plist (3.6.0) + public_suffix (2.0.5) + rake (13.0.6) + representable (3.0.4) + declarative (< 0.1.0) + declarative-option (< 0.2.0) + uber (< 0.2.0) + retriable (3.1.2) + rexml (3.2.5) + rouge (2.0.7) + rubyzip (1.3.0) + security (0.1.3) + signet (0.11.0) + addressable (~> 2.3) + faraday (~> 0.9) + jwt (>= 1.5, < 3.0) + multi_json (~> 1.10) + simctl (1.6.8) + CFPropertyList + naturally + slack-notifier (2.4.0) + terminal-notifier (2.0.0) + terminal-table (1.8.0) + unicode-display_width (~> 1.1, >= 1.1.1) + tty-cursor (0.7.1) + tty-screen (0.8.1) + tty-spinner (0.9.3) + tty-cursor (~> 0.7) + uber (0.1.0) + unf (0.1.4) + unf_ext + unf_ext (0.0.8) + unicode-display_width (1.8.0) + word_wrap (1.0.0) + xcodeproj (1.21.0) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.3.0) + rexml (~> 3.2.4) + xcpretty (0.3.0) + rouge (~> 2.0.7) + xcpretty-travis-formatter (1.0.1) + xcpretty (~> 0.2, >= 0.0.7) + +PLATFORMS + universal-darwin-18 + +DEPENDENCIES + fastlane + fastlane-plugin-firebase_app_distribution + +BUNDLED WITH + 2.2.16 From e6569a72585a039f6674d4bde759eb5e804608c6 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 27 Oct 2021 23:05:29 +0300 Subject: [PATCH 092/189] Updated on 2026-08-14 --- .../java/com/tangem/tap/common/extensions/Store.kt | 5 +++++ .../com/tangem/tap/common/redux/global/GlobalAction.kt | 3 +++ .../tangem/tap/common/redux/global/GlobalReducer.kt | 3 +++ .../com/tangem/tap/common/redux/global/GlobalState.kt | 10 ++++++++++ 4 files changed, 21 insertions(+) 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 f5fd832f2b..17db11e102 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 @@ -4,6 +4,7 @@ import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.TapError +import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.scope import com.tangem.tap.store import kotlinx.coroutines.Dispatchers @@ -17,6 +18,10 @@ fun Store<*>.dispatchOnMain(action: Action) { } } +suspend fun Store<*>.onCardScanned(scanResponse: ScanResponse) { + store.state.globalState.tapWalletManager.onCardScanned(scanResponse) +} + fun Store<*>.dispatchOpenUrl(url: String) { store.dispatch(NavigationAction.OpenUrl(url)) } 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 297ba9fd40..278672bff1 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 @@ -23,6 +23,9 @@ sealed class GlobalAction : Action { data class ShowErrorNotification(override val error: TapError) : GlobalAction(), ErrorAction data class DebugShowErrorNotification(override val error: TapError) : GlobalAction(), DebugErrorAction + // android resources + data class SetResources(val resources: AndroidResources) : GlobalAction() + // dialogs data class ShowDialog(val stateDialog: StateDialog) : GlobalAction() object HideDialog : GlobalAction() 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 29ec84876e..f5ce207390 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 @@ -12,6 +12,9 @@ fun globalReducer(action: Action, state: AppState): GlobalState { val globalState = state.globalState return when (action) { + is GlobalAction.SetResources -> { + globalState.copy(resources = action.resources) + } is GlobalAction.Onboarding.Start -> { val usedCardsPrefStorage = preferencesStorage.usedCardsPrefStorage val onboardingState = OnboardingManager(action.scanResponse, usedCardsPrefStorage) 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 e27641b854..e0166e90e8 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 @@ -27,7 +27,17 @@ data class GlobalState( val scanCardFailsCounter: Int = 0, val dialog: StateDialog? = null, val moonPayUserStatus: MoonPayUserStatus? = null, + val resources: AndroidResources = AndroidResources(), ) : StateType + +data class AndroidResources( + val strings: RString = RString() +) { + data class RString( + val addressWasCopied: Int = -1, + val walletIsNotEmpty: Int = -1 + ) +} typealias CryptoCurrencyName = String typealias FiatCurrencyName = String \ No newline at end of file From c02e5b93f69135c851b254e132ad1050f0c9dd0a Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 27 Oct 2021 23:11:06 +0300 Subject: [PATCH 093/189] Updated on 2026-08-14 --- .../features/details/redux/DetailsAction.kt | 3 ++- .../details/redux/DetailsMiddleware.kt | 19 +++++++++++++++++++ .../features/details/redux/DetailsState.kt | 2 +- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt index 3f8fbfccea..29143f79b9 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt @@ -6,6 +6,7 @@ import com.tangem.tap.common.redux.NotificationAction import com.tangem.tap.common.redux.global.FiatCurrencyName import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.domain.termsOfUse.CardTou +import com.tangem.tap.domain.twins.TwinCardNumber import com.tangem.tap.network.coinmarketcap.FiatCurrency import com.tangem.wallet.R import org.rekotlin.Action @@ -21,7 +22,7 @@ sealed class DetailsAction : Action { ) : DetailsAction() object ShowDisclaimer : DetailsAction() - + data class ReCreateTwinsWallet(val number: TwinCardNumber) : DetailsAction() sealed class EraseWallet : DetailsAction() { object Check : EraseWallet() 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 b539946faa..9192ef3043 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 @@ -4,12 +4,16 @@ import com.tangem.common.CompletionResult import com.tangem.common.core.TangemSdkError import com.tangem.common.services.Result import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler +import com.tangem.tap.common.extensions.dispatchNotification import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.features.disclaimer.redux.DisclaimerAction +import com.tangem.tap.features.onboarding.twins.redux.CreateTwinWalletMode +import com.tangem.tap.features.onboarding.twins.redux.TwinCardsAction +import com.tangem.tap.features.wallet.models.hasSendableAmountsOrPendingTransactions import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.network.coinmarketcap.CoinMarketCapService import com.tangem.tap.preferencesStorage @@ -37,6 +41,21 @@ class DetailsMiddleware { store.dispatch(DisclaimerAction.ShowAcceptedDisclaimer) store.dispatch(NavigationAction.NavigateTo(AppScreen.Disclaimer)) } + is DetailsAction.ReCreateTwinsWallet -> { + val wallet = store.state.walletState.walletManagers.map { it.wallet }.firstOrNull() + if (wallet == null) { + store.dispatch(TwinCardsAction.SetMode(CreateTwinWalletMode.RecreateWallet)) + store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingTwins)) + } else { + if (wallet.hasSendableAmountsOrPendingTransactions()) { + val walletIsNotEmpty = store.state.globalState.resources.strings.walletIsNotEmpty + store.dispatchNotification(walletIsNotEmpty) + } else { + store.dispatch(TwinCardsAction.SetMode(CreateTwinWalletMode.RecreateWallet)) + store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingTwins)) + } + } + } } next(action) } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt index 40a6a6df31..271f51cc97 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt @@ -6,7 +6,7 @@ import com.tangem.common.card.Card import com.tangem.tap.common.entities.Button import com.tangem.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY import com.tangem.tap.common.redux.global.FiatCurrencyName -import com.tangem.tap.features.twins.redux.TwinCardsState +import com.tangem.tap.features.onboarding.twins.redux.TwinCardsState import com.tangem.tap.network.coinmarketcap.FiatCurrency import com.tangem.tap.store import org.rekotlin.StateType From 6dcce60fb4f69531021fe03f5807c40379c4b884 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 27 Oct 2021 23:21:34 +0300 Subject: [PATCH 094/189] Updated on 2026-08-14 --- .../main/java/com/tangem/tap/MainActivity.kt | 12 + .../com/tangem/tap/common/DialogManager.kt | 6 +- .../tap/common/extensions/Navigation.kt | 11 +- .../common/leapfrogWidget/LeapfrogWidget.kt | 65 ++-- .../com/tangem/tap/common/redux/AppReducer.kt | 28 +- .../com/tangem/tap/common/redux/AppState.kt | 4 +- .../tangem/tap/common/redux/StateDialog.kt | 5 +- .../navigation/FragmentShareTransition.kt | 35 ++ .../redux/navigation/NavigationAction.kt | 12 +- .../redux/navigation/NavigationState.kt | 5 +- .../configurable/config/ConfigManager.kt | 5 +- .../tangem/tap/domain/tasks/ScanNoteTask.kt | 124 ------ .../tap/domain/twins/TwinCardsManager.kt | 27 +- .../tap/domain/twins/TwinCardsWidget.kt | 64 +-- .../BaseStoreFragment.kt => BaseFragment.kt} | 44 ++- .../tangem/tap/features/BaseStoreFragment.kt | 24 ++ .../details/redux/DetailsMiddleware.kt | 4 +- .../features/details/redux/DetailsState.kt | 2 +- .../features/details/ui/DetailsFragment.kt | 18 +- .../tangem/tap/features/home/HomeFragment.kt | 13 +- .../tap/features/home/redux/HomeMiddleware.kt | 27 +- .../products/BaseOnboardingFragment.kt | 7 +- .../products/note/OnboardingNoteFragment.kt | 31 +- .../note/redux/OnboardingNoteAction.kt | 1 - .../note/redux/OnboardingNoteMiddleware.kt | 6 +- .../note/redux/OnboardingNoteReducer.kt | 3 - .../note/redux/OnboardingNoteState.kt | 9 - .../OnboardingOtherCardsFragment.kt | 16 +- .../redux/OnboardingOtherCardsMiddleware.kt | 3 +- .../products/twins/OnboardingTwinsFragment.kt | 72 ---- .../products/twins/redux/TwinCardsAction.kt | 64 +++ .../twins/redux/TwinCardsMiddleware.kt | 275 +++++++++++++ .../products/twins/redux/TwinCardsReducer.kt | 64 +++ .../products/twins/redux/TwinCardsState.kt | 61 +++ .../products/twins/ui/TwinsCardsFragment.kt | 366 ++++++++++++++++++ .../ui/dialog/CreateWalletInterruptDialog.kt | 10 +- .../wallet/OnboardingWalletFragment.kt | 2 +- .../tap/features/send/ui/SendFragment.kt | 37 +- .../FragmentStateSubscriber.kt | 2 +- .../stateSubscribers/SendStateSubscriber.kt | 16 +- .../features/twins/redux/TwinCardsAction.kt | 62 --- .../twins/redux/TwinCardsMiddleware.kt | 139 ------- .../features/twins/redux/TwinCardsReducer.kt | 60 --- .../features/twins/redux/TwinCardsState.kt | 43 -- .../twins/ui/CreateTwinWalletFragment.kt | 162 -------- .../twins/ui/TwinWalletWarningFragment.kt | 57 --- .../wallet/models/PendingTransaction.kt | 21 +- .../tap/features/wallet/redux/WalletState.kt | 2 +- .../redux/middlewares/WalletMiddleware.kt | 43 +- .../wallet/redux/reducers/WalletReducer.kt | 3 +- .../tap/features/wallet/ui/WalletFragment.kt | 4 +- .../wallet/ui/wallet/SingleWalletView.kt | 11 +- .../main/res/drawable/card_placeholder.xml | 22 -- .../res/drawable/card_placeholder_black.xml | 126 ++++++ .../res/drawable/card_placeholder_white.xml | 108 ++++++ .../main/res/drawable/ic_home_back_card.xml | 102 ----- .../main/res/drawable/ic_home_front_card.xml | 118 ------ ...=> layout_onboarding_container_bottom.xml} | 16 + .../res/layout/fragment_onboarding_main.xml | 2 +- .../res/layout/fragment_twin_cards_create.xml | 160 -------- .../layout/fragment_twin_cards_warning.xml | 103 ----- .../layout/fragment_twin_cards_welcome.xml | 97 ----- app/src/main/res/layout/fragment_wallet.xml | 2 +- ...=> layout_onboarding_container_bottom.xml} | 18 +- ...ml => layout_onboarding_container_top.xml} | 43 +- ...ome.xml => layout_onboarding_home_top.xml} | 5 +- app/src/main/res/layout/layout_twin_cards.xml | 38 -- .../res/layout/layout_twin_cards_orange.xml | 38 -- .../layout/lp_onboarding_create_wallet.xml | 40 +- .../main/res/layout/lp_onboarding_done.xml | 40 +- .../layout/lp_onboarding_done_activation.xml | 40 +- .../lp_onboarding_done_activation_twins.xml | 116 ++++++ .../res/layout/lp_onboarding_topup_wallet.xml | 42 +- .../lp_onboarding_topup_wallet_twins.xml | 116 ++++++ .../main/res/layout/test_leapfrog_widget.xml | 7 +- .../main/res/layout/view_bg_twins_welcome.xml | 46 +++ .../res/layout/view_onboarding_progress.xml | 21 +- .../res/layout/view_onboarding_tv_balance.xml | 1 + .../main/res/values/strings_untranslated.xml | 27 +- app/src/main/res/values/styles.xml | 3 +- 80 files changed, 1946 insertions(+), 1738 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/common/redux/navigation/FragmentShareTransition.kt delete mode 100644 app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt rename app/src/main/java/com/tangem/tap/features/{send/BaseStoreFragment.kt => BaseFragment.kt} (56%) create mode 100644 app/src/main/java/com/tangem/tap/features/BaseStoreFragment.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/twins/OnboardingTwinsFragment.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsAction.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsReducer.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsState.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/TwinsCardsFragment.kt rename app/src/main/java/com/tangem/tap/features/{ => onboarding/products}/twins/ui/dialog/CreateWalletInterruptDialog.kt (66%) delete mode 100644 app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsAction.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsMiddleware.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsReducer.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsState.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/twins/ui/CreateTwinWalletFragment.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/twins/ui/TwinWalletWarningFragment.kt delete mode 100644 app/src/main/res/drawable/card_placeholder.xml create mode 100644 app/src/main/res/drawable/card_placeholder_black.xml create mode 100644 app/src/main/res/drawable/card_placeholder_white.xml delete mode 100644 app/src/main/res/drawable/ic_home_back_card.xml delete mode 100644 app/src/main/res/drawable/ic_home_front_card.xml rename app/src/main/res/layout-h680dp/{layout_onboarding_bottom_action_views.xml => layout_onboarding_container_bottom.xml} (83%) delete mode 100644 app/src/main/res/layout/fragment_twin_cards_create.xml delete mode 100644 app/src/main/res/layout/fragment_twin_cards_warning.xml delete mode 100644 app/src/main/res/layout/fragment_twin_cards_welcome.xml rename app/src/main/res/layout/{layout_onboarding_bottom_action_views.xml => layout_onboarding_container_bottom.xml} (82%) rename app/src/main/res/layout/{layout_onboarding_note.xml => layout_onboarding_container_top.xml} (69%) rename app/src/main/res/layout/{layout_onboarding_home.xml => layout_onboarding_home_top.xml} (90%) delete mode 100644 app/src/main/res/layout/layout_twin_cards.xml delete mode 100644 app/src/main/res/layout/layout_twin_cards_orange.xml create mode 100644 app/src/main/res/layout/lp_onboarding_done_activation_twins.xml create mode 100644 app/src/main/res/layout/lp_onboarding_topup_wallet_twins.xml create mode 100644 app/src/main/res/layout/view_bg_twins_welcome.xml diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index 6f99403572..a7307e4e74 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -13,6 +13,8 @@ import com.tangem.tap.common.DialogManager import com.tangem.tap.common.IntentHandler import com.tangem.tap.common.SnackbarHandler import com.tangem.tap.common.redux.NotificationsHandler +import com.tangem.tap.common.redux.global.AndroidResources +import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.TangemSdkManager @@ -66,9 +68,19 @@ class MainActivity : AppCompatActivity(), SnackbarHandler { tangemSdk = TangemSdk.init(this, TangemSdkManager.config) tangemSdkManager = TangemSdkManager(tangemSdk, this) + store.dispatch(GlobalAction.SetResources(getAndroidResources())) store.dispatch(WalletConnectAction.RestoreSessions) } + private fun getAndroidResources(): AndroidResources { + return AndroidResources( + AndroidResources.RString( + R.string.copy_toast_msg, + R.string.details_notification_erase_wallet_not_possible + ) + ) + } + private fun systemActions() { // makes the status bar text dark window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR 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 a604cbd643..b3a0c345e2 100644 --- a/app/src/main/java/com/tangem/tap/common/DialogManager.kt +++ b/app/src/main/java/com/tangem/tap/common/DialogManager.kt @@ -7,8 +7,8 @@ import com.tangem.tap.common.redux.global.GlobalState import com.tangem.tap.features.details.redux.walletconnect.WalletConnectDialog import com.tangem.tap.features.details.ui.walletconnect.dialogs.* import com.tangem.tap.features.onboarding.AddressInfoBottomSheetDialog -import com.tangem.tap.features.twins.redux.TwinCardsAction -import com.tangem.tap.features.twins.ui.dialog.CreateWalletInterruptDialog +import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction +import com.tangem.tap.features.onboarding.products.twins.ui.dialog.CreateWalletInterruptDialog import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog import com.tangem.tap.store import com.tangem.wallet.R @@ -45,7 +45,7 @@ class DialogManager : StoreSubscriber { dialog = when (state.dialog) { is AppDialog.ScanFailsDialog -> ScanFailsDialog.create(context) is AppDialog.AddressInfoDialog -> AddressInfoBottomSheetDialog(state.dialog, context) - is TwinCardsAction.Wallet.InterruptDialog -> CreateWalletInterruptDialog.create(state.dialog, context) + is TwinCardsAction.Wallet.ShowInterruptDialog -> CreateWalletInterruptDialog.create(state.dialog, context) is WalletConnectDialog.UnsupportedCard -> SimpleAlertDialog.create( titleRes = R.string.wallet_connect, diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt index 135e0cbd9c..f96f16f5ee 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt @@ -16,12 +16,10 @@ import com.tangem.tap.features.disclaimer.ui.DisclaimerFragment import com.tangem.tap.features.home.HomeFragment import com.tangem.tap.features.onboarding.products.note.OnboardingNoteFragment import com.tangem.tap.features.onboarding.products.otherCards.OnboardingOtherCardsFragment -import com.tangem.tap.features.onboarding.products.twins.OnboardingTwinsFragment +import com.tangem.tap.features.onboarding.products.twins.ui.TwinsCardsFragment import com.tangem.tap.features.onboarding.products.wallet.OnboardingWalletFragment import com.tangem.tap.features.send.ui.SendFragment import com.tangem.tap.features.tokens.ui.AddTokensFragment -import com.tangem.tap.features.twins.ui.CreateTwinWalletFragment -import com.tangem.tap.features.twins.ui.TwinWalletWarningFragment import com.tangem.tap.features.wallet.ui.WalletDetailsFragment import com.tangem.tap.features.wallet.ui.WalletFragment import com.tangem.wallet.R @@ -39,7 +37,7 @@ fun FragmentActivity.openFragment( transaction.setReorderingAllowed(true) shareElements.forEach { shareElement -> shareElement.wView.get()?.let { view -> - transaction.addSharedElement(view, shareElement.name) + transaction.addSharedElement(view, shareElement.elementName) } } } @@ -70,10 +68,11 @@ fun FragmentActivity.addOnBackPressedDispatcher( private fun fragmentFactory(screen: AppScreen): Fragment { return when (screen) { +// AppScreen.Home -> TestLeapfrogFragment() AppScreen.Home -> HomeFragment() AppScreen.OnboardingNote -> OnboardingNoteFragment() AppScreen.OnboardingWallet -> OnboardingWalletFragment() - AppScreen.OnboardingTwins -> OnboardingTwinsFragment() + AppScreen.OnboardingTwins -> TwinsCardsFragment() AppScreen.OnboardingOther -> OnboardingOtherCardsFragment() AppScreen.Wallet -> WalletFragment() AppScreen.Send -> SendFragment() @@ -81,8 +80,6 @@ private fun fragmentFactory(screen: AppScreen): Fragment { AppScreen.DetailsConfirm -> DetailsConfirmFragment() AppScreen.DetailsSecurity -> DetailsSecurityFragment() AppScreen.Disclaimer -> DisclaimerFragment() - AppScreen.CreateTwinWalletWarning -> TwinWalletWarningFragment() - AppScreen.CreateTwinWallet -> CreateTwinWalletFragment() AppScreen.AddTokens -> AddTokensFragment() AppScreen.WalletDetails -> WalletDetailsFragment() AppScreen.WalletConnectSessions -> WalletConnectSessionsFragment() 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 index 884cef3f94..e83f7b8b92 100644 --- a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapfrogWidget.kt +++ b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapfrogWidget.kt @@ -6,6 +6,7 @@ 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 /** @@ -15,9 +16,9 @@ class LeapfrogWidget( private val parentContainer: FrameLayout, private val calculator: PropertyCalculator = PropertyCalculator(), ) { - private val foldAnimationDuration = 500L - private val leapAnimationDuration = 800L - private val leapBackAnimationDuration = 800L + private val foldAnimationDuration = 300L + private val leapAnimationDuration = 500L + private val leapBackAnimationDuration = 500L private val leapViews = mutableListOf() @@ -25,9 +26,6 @@ class LeapfrogWidget( private var leapProgress: Int = 100 private var leapBackProgress: Int = 100 - var isFolded: Boolean = true - private set - init { calculator.setTranslationConverter { parentContainer.dpToPx(it) } if (viewIsFullFledged()) { @@ -43,12 +41,11 @@ class LeapfrogWidget( * Apply initial properties to the views */ fun initViews() { - isFolded = true leapViews.forEach { it.initView() } } - fun fold(onEnd: () -> Unit = {}) { - val animator = foldAnimator() + fun fold(animate: Boolean = true, onEnd: VoidCallback = {}) { + val animator = foldAnimator(animate) if (animator == null) { onEnd() } else { @@ -57,8 +54,8 @@ class LeapfrogWidget( } } - fun unfold(onEnd: () -> Unit = {}) { - val animator = unfoldAnimator() + fun unfold(animate: Boolean = true, onEnd: VoidCallback = {}) { + val animator = unfoldAnimator(animate) if (animator == null) { onEnd() } else { @@ -67,33 +64,31 @@ class LeapfrogWidget( } } - fun foldAnimator(): Animator? { - if (!canFoldUnfold() || isFolded) return null + fun foldAnimator(animate: Boolean = true): Animator? { + if (!canFoldUnfold()) return null - return createFoldUnfoldAnimator(true, foldAnimationDuration) + return createFoldUnfoldAnimator(true, animate, foldAnimationDuration) } - fun unfoldAnimator(): Animator? { - if (!canFoldUnfold() || !isFolded) return null + fun unfoldAnimator(animate: Boolean = true): Animator? { + if (!canFoldUnfold()) return null - return createFoldUnfoldAnimator(false, foldAnimationDuration) + return createFoldUnfoldAnimator(false, animate, foldAnimationDuration) } - private fun createFoldUnfoldAnimator(isFoldAnimation: Boolean, duration: Long): Animator { + 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(duration, it.state.properties)) + animatorsList.add(it.view.foldAnimation(animateDuration, it.state.properties)) } else { it.unfold() - animatorsList.add(it.view.unfoldAnimation(duration, it.state.properties)) + animatorsList.add(it.view.unfoldAnimation(animateDuration, it.state.properties)) } } - animatorsList.add(createProgressListener(duration) { - foldUnfoldProgress = it - if (it == 100) isFolded = isFoldAnimation - }) + animatorsList.add(createProgressListener(animateDuration) { foldUnfoldProgress = it }) val animator = AnimatorSet() animator.interpolator = AccelerateDecelerateInterpolator() @@ -101,13 +96,13 @@ class LeapfrogWidget( return animator } - fun leap(onEnd: () -> Unit = {}) { + fun leap(animate: Boolean = true, onEnd: VoidCallback = {}) { if (!canLeap()) { onEnd() return } - val duration = leapAnimationDuration + val duration = if (animate) leapAnimationDuration else 0 val animatorsList = mutableListOf() leapViews.forEach { when (it.leap()) { @@ -128,13 +123,13 @@ class LeapfrogWidget( animator.start() } - fun leapBack(onEnd: () -> Unit = {}) { + fun leapBack(animate: Boolean = true, onEnd: VoidCallback = {}) { if (!canLeapBack()) { onEnd() return } - val duration = leapBackAnimationDuration + val duration = if (animate) leapBackAnimationDuration else 0 val animatorsList = mutableListOf() leapViews.forEach { when (it.leapBack()) { @@ -160,12 +155,11 @@ class LeapfrogWidget( fun getViewByPosition(position: Int): LeapView = leapViews.first { it.state.currentPosition == position } - fun getState(): LeapfrogWidgetState = LeapfrogWidgetState(isFolded, leapViews.map { it.state }) + fun getState(): LeapfrogWidgetState = LeapfrogWidgetState(leapViews.map { it.state }) fun applyState(state: LeapfrogWidgetState) { - isFolded = state.isFolded - state.leapViewStates.forEach { state -> - leapViews.firstOrNull { it.index == state.index }?.applyState(state) + state.leapViewStates.forEach { viewState -> + leapViews.firstOrNull { it.index == viewState.index }?.applyState(viewState) } } @@ -183,7 +177,6 @@ class LeapfrogWidget( private fun canLeap(): Boolean { return when { !viewIsFullFledged() -> false - isFolded -> false leapBackInProgress() -> false leapProgress < 70 -> false else -> true @@ -193,7 +186,6 @@ class LeapfrogWidget( private fun canLeapBack(): Boolean { return when { !viewIsFullFledged() -> false - isFolded -> false leapInProgress() -> false leapBackProgress < 70 -> false else -> true @@ -206,14 +198,13 @@ class LeapfrogWidget( } data class LeapfrogWidgetState( - val isFolded: Boolean, val leapViewStates: List ) class PropertyCalculator( val elevationFactor: Float = 1f, - val decreaseScaleFactor: Float = 0.1f, - val yTranslationFactor: Float = 20f, + 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 diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt index ed4dbe69aa..d797ff9010 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt @@ -8,10 +8,10 @@ import com.tangem.tap.features.disclaimer.redux.DisclaimerReducer import com.tangem.tap.features.home.redux.HomeReducer import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteReducer import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsReducer +import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsReducer import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletReducer import com.tangem.tap.features.send.redux.reducers.SendScreenReducer import com.tangem.tap.features.tokens.redux.TokensReducer -import com.tangem.tap.features.twins.redux.TwinCardsReducer import com.tangem.tap.features.wallet.redux.reducers.WalletReducer import org.rekotlin.Action @@ -20,19 +20,19 @@ fun appReducer(action: Action, state: AppState?): AppState { if (action is AppAction.RestoreState) return action.state return AppState( - navigationState = NavigationReducer.reduce(action, state), - globalState = globalReducer(action, state), - homeState = HomeReducer.reduce(action, state), - onboardingNoteState = OnboardingNoteReducer.reduce(action, state), - onboardingWalletState = OnboardingWalletReducer.reduce(action, state), - onboardingOtherCardsState = OnboardingOtherCardsReducer.reduce(action, state), - walletState = WalletReducer.reduce(action, state), - twinCardsState = TwinCardsReducer.reduce(action, state), - sendState = SendScreenReducer.reduce(action, state.sendState), - detailsState = DetailsReducer.reduce(action, state), - disclaimerState = DisclaimerReducer.reduce(action, state), - tokensState = TokensReducer.reduce(action, state), - walletConnectState = WalletConnectReducer.reduce(action, state.walletConnectState) + navigationState = NavigationReducer.reduce(action, state), + globalState = globalReducer(action, state), + homeState = HomeReducer.reduce(action, state), + onboardingNoteState = OnboardingNoteReducer.reduce(action, state), + onboardingWalletState = OnboardingWalletReducer.reduce(action, state), + onboardingOtherCardsState = OnboardingOtherCardsReducer.reduce(action, state), + walletState = WalletReducer.reduce(action, state), + twinCardsState = TwinCardsReducer.reduce(action, state), + sendState = SendScreenReducer.reduce(action, state.sendState), + detailsState = DetailsReducer.reduce(action, state), + disclaimerState = DisclaimerReducer.reduce(action, state), + tokensState = TokensReducer.reduce(action, state), + walletConnectState = WalletConnectReducer.reduce(action, state.walletConnectState) ) } diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt index 98827674a0..39b221f517 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt @@ -16,14 +16,14 @@ import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteMidd import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteState import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsMiddleware import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsState +import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsMiddleware +import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletMiddleware import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletState import com.tangem.tap.features.send.redux.middlewares.SendMiddleware import com.tangem.tap.features.send.redux.states.SendState import com.tangem.tap.features.tokens.redux.TokensMiddleware import com.tangem.tap.features.tokens.redux.TokensState -import com.tangem.tap.features.twins.redux.TwinCardsMiddleware -import com.tangem.tap.features.twins.redux.TwinCardsState import com.tangem.tap.features.wallet.redux.WalletState import com.tangem.tap.features.wallet.redux.middlewares.WalletMiddleware import org.rekotlin.Middleware diff --git a/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt b/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt index 36e1a749a5..4334d55f59 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt @@ -1,5 +1,6 @@ package com.tangem.tap.common.redux +import com.tangem.common.extensions.VoidCallback import com.tangem.tap.features.wallet.redux.AddressData /** @@ -11,7 +12,7 @@ sealed class AppDialog : StateDialog { object ScanFailsDialog : AppDialog() data class AddressInfoDialog( val addressData: AddressData, - val onCopyAddress: () -> Unit, - val onExploreAddress: () -> Unit + val onCopyAddress: VoidCallback, + val onExploreAddress: VoidCallback ) : AppDialog() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/FragmentShareTransition.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/FragmentShareTransition.kt new file mode 100644 index 0000000000..69547d020f --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/FragmentShareTransition.kt @@ -0,0 +1,35 @@ +package com.tangem.tap.common.redux.navigation + +import android.view.View +import androidx.transition.TransitionSet +import java.lang.ref.WeakReference + +/** +[REDACTED_AUTHOR] + */ +data class FragmentShareTransition( + val shareElements: List, + val enterTransitionSet: TransitionSet, + val exitTransitionSet: TransitionSet +) + +/** + * For ease of use, the name is used as transitionName\name into the FragmentTransaction.addSharedElement + */ +class ShareElement(view: View, name: String? = null) { + + val wView: WeakReference + val elementName: String + + init { + name?.let { view.transitionName = it } + elementName = view.transitionName + ?: throw UnsupportedOperationException("ShareElement require the name") + wView = WeakReference(view) + } + + companion object { + val imvFrontCard = "imv_front_card" + val imvBackCard = "imv_back_card" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt index 331dc07e1a..d2147ad3c6 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt @@ -1,8 +1,6 @@ package com.tangem.tap.common.redux.navigation -import android.view.View import androidx.fragment.app.FragmentActivity -import androidx.transition.TransitionSet import org.rekotlin.Action import java.lang.ref.WeakReference @@ -19,12 +17,4 @@ sealed class NavigationAction : Action { data class ActivityCreated(val activity: WeakReference) : NavigationAction() object ActivityDestroyed : NavigationAction() -} - -data class FragmentShareTransition( - val shareElements: List, - val enterTransitionSet: TransitionSet, - val exitTransitionSet: TransitionSet -) - -data class ShareElement(val wView: WeakReference, val name: String) \ No newline at end of file +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt index 4fa02150f0..61e3d263da 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt @@ -5,8 +5,8 @@ import org.rekotlin.StateType import java.lang.ref.WeakReference data class NavigationState( - val backStack: List = listOf(AppScreen.Home), - val activity: WeakReference? = null + val backStack: List = listOf(AppScreen.Home), + val activity: WeakReference? = null ) : StateType enum class AppScreen { @@ -16,7 +16,6 @@ enum class AppScreen { Wallet, WalletDetails, Send, Details, DetailsConfirm, DetailsSecurity, - CreateTwinWalletWarning, CreateTwinWallet, AddTokens, WalletConnectSessions, QrScan 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 e08ba924b5..33aa1e1b4d 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 @@ -14,6 +14,7 @@ data class Config( val blockchainSdkConfig: BlockchainSdkConfig = BlockchainSdkConfig(), val isSendingToPayIdEnabled: Boolean = true, val isTopUpEnabled: Boolean = false, + @Deprecated("Not relevant since version 3.23") val isCreatingTwinCardsAllowed: Boolean = false ) @@ -50,10 +51,10 @@ class ConfigManager( fun resetToDefault(name: String) { when (name) { isSendingToPayIdEnabled -> config = - config.copy(isSendingToPayIdEnabled = defaultConfig.isSendingToPayIdEnabled) + config.copy(isSendingToPayIdEnabled = defaultConfig.isSendingToPayIdEnabled) isTopUpEnabled -> config = config.copy(isTopUpEnabled = defaultConfig.isTopUpEnabled) isCreatingTwinCardsAllowed -> config = - config.copy(isCreatingTwinCardsAllowed = defaultConfig.isCreatingTwinCardsAllowed) + config.copy(isCreatingTwinCardsAllowed = defaultConfig.isCreatingTwinCardsAllowed) } } diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt deleted file mode 100644 index 22a7e5ca87..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt +++ /dev/null @@ -1,124 +0,0 @@ -package com.tangem.tap.domain.tasks - -import com.tangem.* -import com.tangem.common.CompletionResult -import com.tangem.common.card.Card -import com.tangem.common.card.FirmwareVersion -import com.tangem.common.core.CardSession -import com.tangem.common.core.CardSessionRunnable -import com.tangem.common.core.TangemError -import com.tangem.common.extensions.toHexString -import com.tangem.operations.ScanTask -import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand -import com.tangem.tap.domain.ProductType -import com.tangem.tap.domain.TapSdkError -import com.tangem.tap.domain.TapWorkarounds.isExcluded -import com.tangem.tap.domain.TapWorkarounds.isTangemNote -import com.tangem.tap.domain.extensions.getSingleWallet -import com.tangem.tap.domain.tasks.product.ScanResponse -import com.tangem.tap.domain.twins.TwinsHelper - -@Deprecated("Use ScanProductTask instead") -class ScanNoteTask(val card: Card? = null) : CardSessionRunnable { - - override fun run( - session: CardSession, - callback: (result: CompletionResult) -> Unit, - ) { - ScanTask().run(session) { result -> - when (result) { - is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) - - is CompletionResult.Success -> { - val card = this.card ?: result.data - - val error = getErrorIfExcludedCard(card) - if (error != null) { - callback(CompletionResult.Failure(error)) - return@run - } - - if (TwinsHelper.getTwinCardNumber(card.cardId) != null) { - dealWithTwinCard(card, session, callback) - } else if (!isTangemNote(card) && card.firmwareVersion >= FirmwareVersion.MultiWalletAvailable) { - createMissingWalletsIfNeeded(card, session, callback) - } else { - callback(CompletionResult.Success( - ScanResponse(card, ProductType.Other, session.environment.walletData))) - } - } - } - } - } - - private fun createMissingWalletsIfNeeded( - card: Card, session: CardSession, - callback: (result: CompletionResult) -> Unit, - ) { - val walletData = session.environment.walletData - if (card.wallets.isEmpty()) { - callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, walletData))) - return - } - - val curvesPresent = card.wallets.map { it.curve } -// val curvesToCreate = EllipticCurve.values().subtract(curvesPresent) - val curvesToCreate = card.supportedCurves.subtract(curvesPresent) - - if (curvesToCreate.isEmpty()) { - callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, walletData))) - return - } - - CreateWalletsTask(curvesToCreate.toList()).run(session) { result -> - when (result) { - is CompletionResult.Success -> - callback(CompletionResult.Success(ScanResponse(result.data, ProductType.Other, walletData))) - is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) - } - } - - } - - private fun dealWithTwinCard( - card: Card, session: CardSession, - callback: (result: CompletionResult) -> Unit, - ) { - ReadIssuerDataCommand().run(session) { readDataResult -> - when (readDataResult) { - is CompletionResult.Success -> { - val publicKey = card.getSingleWallet()?.publicKey - if (publicKey == null) { - callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, null))) - return@run - } - val verified = TwinsHelper.verifyTwinPublicKey( - readDataResult.data.issuerData, publicKey - ) - if (verified) { - val twinPublicKey = readDataResult.data.issuerData.sliceArray(0 until 65) - callback(CompletionResult.Success( - ScanResponse( - card = card, - ProductType.Other, - walletData = session.environment.walletData, - secondTwinPublicKey = twinPublicKey.toHexString()) - )) - return@run - } else { - callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, null))) - } - } - is CompletionResult.Failure -> - callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, null))) - } - } - } - - private fun getErrorIfExcludedCard(card: Card): TangemError? { - if (card.isExcluded()) return TapSdkError.CardForDifferentApp - // Disable new multi-currency HD wallet cards on the old version of the app -// if (card.isMultiCurrencyWallet()) return UpdateAppToUseThisCard() - return null - } -} \ No newline at end of file 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 0024a37cd8..e16ccd79cb 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 @@ -4,44 +4,45 @@ import com.squareup.moshi.JsonAdapter import com.squareup.moshi.Types import com.tangem.Message import com.tangem.blockchain.extensions.Result -import com.tangem.blockchain.extensions.SimpleResult import com.tangem.common.CompletionResult import com.tangem.common.KeyPair +import com.tangem.common.card.Card 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.domain.tasks.product.ScanResponse import com.tangem.tap.network.createMoshi import com.tangem.tap.tangemSdkManager -class TwinCardsManager(private val scanResponse: ScanResponse, assetReader: AssetReader) { +class TwinCardsManager(private val card: Card, assetReader: AssetReader) { - private val currentCardId: String = scanResponse.card.cardId + private val currentCardId: String = card.cardId private var currentCardPublicKey: String? = null private var secondCardPublicKey: String? = null - private val issuerKeyPair: KeyPair = getIssuerKeys(assetReader, scanResponse.card.issuer.publicKey.toHexString()) + private val issuerKeyPair: KeyPair = getIssuerKeys(assetReader, card.issuer.publicKey.toHexString()) - suspend fun createFirstWallet(message: Message): SimpleResult { + suspend fun createFirstWallet(message: Message): CompletionResult { val response = tangemSdkManager.runTaskAsync( CreateFirstTwinWalletTask(), currentCardId, message ) when (response) { is CompletionResult.Success -> { currentCardPublicKey = response.data.wallet.publicKey.toHexString() - return SimpleResult.Success + return response } is CompletionResult.Failure -> { (response.error as? TangemSdkError)?.let { error -> FirebaseAnalyticsHandler.logCardSdkError( error, FirebaseAnalyticsHandler.ActionToLog.CreateWallet, - card = scanResponse.card + card = card ) } - return SimpleResult.failure(response.error) + return response } } @@ -52,7 +53,7 @@ class TwinCardsManager(private val scanResponse: ScanResponse, assetReader: Asse initialMessage: Message, preparingMessage: Message, creatingWalletMessage: Message, - ): SimpleResult { + ): CompletionResult { val task = CreateSecondTwinWalletTask( firstPublicKey = currentCardPublicKey!!, firstCardId = currentCardId, @@ -64,20 +65,18 @@ class TwinCardsManager(private val scanResponse: ScanResponse, assetReader: Asse when (response) { is CompletionResult.Success -> { secondCardPublicKey = response.data.wallet.publicKey.toHexString() - return SimpleResult.Success } is CompletionResult.Failure -> { (response.error as? TangemSdkError)?.let { error -> FirebaseAnalyticsHandler.logCardSdkError( error, FirebaseAnalyticsHandler.ActionToLog.CreateWallet, - card = scanResponse.card + card = card ) } - return SimpleResult.failure(response.error) } } - + return response } suspend fun complete(message: Message): Result { @@ -92,7 +91,7 @@ class TwinCardsManager(private val scanResponse: ScanResponse, assetReader: Asse FirebaseAnalyticsHandler.logCardSdkError( error, FirebaseAnalyticsHandler.ActionToLog.WriteIssuerData, - card = scanResponse.card + card = card ) } Result.failure(response.error) 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 63f891eb4d..c8f3c02952 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 @@ -5,6 +5,7 @@ import android.animation.ObjectAnimator 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 @@ -17,47 +18,60 @@ class TwinsCardWidget( val getTopOfAnchorViewForActivateState: () -> Float ) { + var state: TwinCardsWidgetState? = null + private set + + private val animationDuration = 300L + init { if (leapfrogWidget.getViewsCount() != 2) throw UnsupportedOperationException() } - fun toWelcome(animate: Boolean = true, onEnd: () -> Unit = {}) { - val animator = createAnimator(animate, onEnd) + fun toWelcome(animate: Boolean = true, onEnd: VoidCallback = {}) { + if (state == TwinCardsWidgetState.Welcome) return + + state = TwinCardsWidgetState.Welcome + val animator = createAnimator(animate) animator.playTogether( createAnimator(TwinCardNumber.First, createWelcomeProperties(TwinCardNumber.First)), createAnimator(TwinCardNumber.Second, createWelcomeProperties(TwinCardNumber.Second)) ) - leapfrogWidget.fold { animator.start() } + animator.doOnEnd { onEnd() } + leapfrogWidget.fold(animate) { animator.start() } } - fun toLeapfrog(animate: Boolean = true, onEnd: () -> Unit = {}) { - val animator = createAnimator(animate, onEnd) + fun toLeapfrog(animate: Boolean = true, onEnd: VoidCallback = {}) { + if (state == TwinCardsWidgetState.Leapfrog) return + + state = TwinCardsWidgetState.Leapfrog + val animator = createAnimator(animate) animator.playTogether( createAnimator(TwinCardNumber.First, createLeapfrogProperties(TwinCardNumber.First)), createAnimator(TwinCardNumber.Second, createLeapfrogProperties(TwinCardNumber.Second)) ) - leapfrogWidget.fold { - animator.doOnEnd { - leapfrogWidget.initViews() - leapfrogWidget.unfold() - } - animator.start() + animator.doOnEnd { + leapfrogWidget.initViews() + leapfrogWidget.unfold(animate, onEnd) } + animator.start() } - fun toActivate(animate: Boolean = true, onEnd: () -> Unit = {}) { - val animator = createAnimator(animate, onEnd) + fun toActivate(animate: Boolean = true, onEnd: VoidCallback = {}) { + if (state == TwinCardsWidgetState.Activate) return + + state = TwinCardsWidgetState.Activate + val animator = createAnimator(animate) animator.playTogether( createAnimator(TwinCardNumber.First, createActivateProperties(TwinCardNumber.First)), createAnimator(TwinCardNumber.Second, createActivateProperties(TwinCardNumber.Second)) ) + animator.doOnEnd { onEnd() } animator.start() } - private fun createAnimator(animate: Boolean, onEnd: () -> Unit): AnimatorSet { + private fun createAnimator(animate: Boolean): AnimatorSet { return AnimatorSet().apply { - duration = if (animate) 400 else 0 - doOnEnd { onEnd() } + duration = if (animate) animationDuration else 0 } } @@ -72,20 +86,20 @@ class TwinsCardWidget( return when (cardNumber) { TwinCardNumber.First -> { TwinsCardProperties( - xTranslation = -180f, - yTranslation = -350f, + xTranslation = -120f, + yTranslation = -220f, rotation = -3f, elevation = 1f, - scale = 1.1f, + scale = 1f, ) } TwinCardNumber.Second -> { TwinsCardProperties( - xTranslation = 180f, - yTranslation = 10f, + xTranslation = 170f, + yTranslation = 170f, rotation = -3f, elevation = 0f, - scale = 1.1f, + scale = 1f, ) } } @@ -108,7 +122,7 @@ class TwinsCardWidget( xTranslation = twinProperties.xTranslation, yTranslation = twinProperties.yTranslation - topOfAnchorView, rotation = 0f, - scale = twinProperties.scale - 0.4f, + scale = twinProperties.scale - 0.5f, ) } @@ -120,6 +134,10 @@ class TwinsCardWidget( } } +enum class TwinCardsWidgetState { + Welcome, Leapfrog, Activate +} + private data class TwinsCardProperties( val xTranslation: Float = 0f, val yTranslation: Float = 0f, diff --git a/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt b/app/src/main/java/com/tangem/tap/features/BaseFragment.kt similarity index 56% rename from app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt rename to app/src/main/java/com/tangem/tap/features/BaseFragment.kt index bb3046db5e..8e1c2e098a 100644 --- a/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/BaseFragment.kt @@ -1,26 +1,26 @@ -package com.tangem.tap.features.send +package com.tangem.tap.features +import android.annotation.SuppressLint import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.activity.OnBackPressedCallback +import androidx.appcompat.widget.Toolbar import androidx.fragment.app.Fragment import androidx.transition.TransitionInflater import com.google.android.material.snackbar.Snackbar +import com.tangem.common.extensions.VoidCallback +import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.store import com.tangem.wallet.R -import kotlinx.android.synthetic.main.fragment_wallet.* -import org.rekotlin.StoreSubscriber /** [REDACTED_AUTHOR] */ -abstract class BaseStoreFragment(layoutId: Int) : Fragment(layoutId) { - - abstract fun subscribeToStore() +abstract class BaseFragment(layoutId: Int) : Fragment(layoutId), FragmentOnBackPressedHandler { protected lateinit var mainView: View - protected val storeSubscribersList = mutableListOf>() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -40,26 +40,32 @@ abstract class BaseStoreFragment(layoutId: Int) : Fragment(layoutId) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - toolbar?.setNavigationOnClickListener { activity?.onBackPressed() } + addBackPressHandler(this) } - override fun onStart() { - super.onStart() - subscribeToStore() + override fun handleOnBackPressed() { + store.dispatch(NavigationAction.PopBackTo()) } - - override fun onStop() { - storeSubscribersList.forEach { store.unsubscribe(it) } - storeSubscribersList.clear() - super.onStop() - } - - fun showRetrySnackbar(message: String, action: () -> Unit) { + fun showRetrySnackbar(message: String, action: VoidCallback) { val snackbar = Snackbar.make(mainView, message, Snackbar.LENGTH_INDEFINITE) snackbar.setAction(getString(R.string.common_retry)) { snackbar.dismiss() action() }.show() } +} + +interface FragmentOnBackPressedHandler { + fun handleOnBackPressed() +} + +@SuppressLint("FragmentBackPressedCallback") +fun Fragment.addBackPressHandler(handler: FragmentOnBackPressedHandler) { + activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + handler.handleOnBackPressed() + } + }) + view?.findViewById(R.id.toolbar)?.setNavigationOnClickListener { activity?.onBackPressed() } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/BaseStoreFragment.kt b/app/src/main/java/com/tangem/tap/features/BaseStoreFragment.kt new file mode 100644 index 0000000000..b4f486a1a2 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/BaseStoreFragment.kt @@ -0,0 +1,24 @@ +package com.tangem.tap.features + +import com.tangem.tap.store +import org.rekotlin.StoreSubscriber + +/** +[REDACTED_AUTHOR] + */ +abstract class BaseStoreFragment(layoutId: Int) : BaseFragment(layoutId) { + + abstract fun subscribeToStore() + protected val storeSubscribersList = mutableListOf>() + + override fun onStart() { + super.onStart() + subscribeToStore() + } + + override fun onStop() { + storeSubscribersList.forEach { store.unsubscribe(it) } + storeSubscribersList.clear() + super.onStop() + } +} \ No newline at end of file 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 9192ef3043..22bbd11511 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 @@ -11,8 +11,8 @@ import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.features.disclaimer.redux.DisclaimerAction -import com.tangem.tap.features.onboarding.twins.redux.CreateTwinWalletMode -import com.tangem.tap.features.onboarding.twins.redux.TwinCardsAction +import com.tangem.tap.features.onboarding.products.twins.redux.CreateTwinWalletMode +import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction import com.tangem.tap.features.wallet.models.hasSendableAmountsOrPendingTransactions import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.network.coinmarketcap.CoinMarketCapService diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt index 271f51cc97..40f409c680 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt @@ -6,7 +6,7 @@ import com.tangem.common.card.Card import com.tangem.tap.common.entities.Button import com.tangem.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY import com.tangem.tap.common.redux.global.FiatCurrencyName -import com.tangem.tap.features.onboarding.twins.redux.TwinCardsState +import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState import com.tangem.tap.network.coinmarketcap.FiatCurrency import com.tangem.tap.store import org.rekotlin.StateType 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 31d11dbc31..cb51e326dd 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 @@ -6,7 +6,6 @@ import android.view.View import androidx.activity.OnBackPressedCallback import androidx.fragment.app.Fragment import androidx.transition.TransitionInflater -import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.show import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.AppScreen @@ -16,8 +15,6 @@ import com.tangem.tap.features.details.redux.DetailsAction import com.tangem.tap.features.details.redux.DetailsState import com.tangem.tap.features.details.redux.SecurityOption import com.tangem.tap.features.feedback.FeedbackEmail -import com.tangem.tap.features.twins.redux.CreateTwinWalletMode -import com.tangem.tap.features.twins.redux.TwinCardsAction import com.tangem.tap.store import com.tangem.wallet.R import kotlinx.android.synthetic.main.fragment_details.* @@ -91,17 +88,10 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber() { private lateinit var btnScanCard: ViewStateWidget - override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_home + override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_home_top override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -38,7 +37,10 @@ class HomeFragment : BaseOnboardingFragment() { toolbar.hide() val shareTransition = FragmentShareTransition( - listOf(ShareElement(WeakReference(imv_front_card), imv_front_card.transitionName)), + listOf( + ShareElement(imv_front_card, ShareElement.imvFrontCard), + ShareElement(imv_back_card, ShareElement.imvBackCard) + ), FrontCardEnterTransition(), FrontCardExitTransition() ) @@ -62,6 +64,7 @@ class HomeFragment : BaseOnboardingFragment() { oldState.homeState == newState.homeState }.select { it.homeState } } + storeSubscribersList.add(this) } override fun newState(state: HomeState) { 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 4de7177c34..bd5cdcffff 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 @@ -2,6 +2,8 @@ package com.tangem.tap.features.home.redux import com.tangem.tap.common.entities.IndeterminateProgressButton import com.tangem.tap.common.extensions.dispatchOpenUrl +import com.tangem.tap.common.extensions.onCardScanned +import com.tangem.tap.common.extensions.withMainContext import com.tangem.tap.common.post import com.tangem.tap.common.postUi import com.tangem.tap.common.redux.AppState @@ -10,10 +12,10 @@ import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.FragmentShareTransition import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.DELAY_SDK_DIALOG_CLOSE -import com.tangem.tap.domain.twins.getTwinCardNumber import com.tangem.tap.features.onboarding.OnboardingHelper +import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction +import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsStep import com.tangem.tap.features.send.redux.states.ButtonState -import com.tangem.tap.features.twins.redux.TwinCardsAction import com.tangem.tap.preferencesStorage import com.tangem.tap.scope import com.tangem.tap.store @@ -58,21 +60,24 @@ private fun handleReadCard() { } else { changeButtonState(ButtonState.PROGRESS) store.dispatch(GlobalAction.ScanCard({ scanResponse -> - if (scanResponse.isTangemTwins()) { - scanResponse.card.getTwinCardNumber()?.let { - val configManager = store.state.globalState.configManager - val isCreatingTwinsAllowed = configManager?.config?.isCreatingTwinCardsAllowed ?: false - store.dispatch(TwinCardsAction.SetTwinCard(it, null, isCreatingTwinsAllowed)) - } - } + store.dispatch(TwinCardsAction.IfTwinsPrepareState(scanResponse)) if (OnboardingHelper.isOnboardingCase(scanResponse)) { val navigateTo = OnboardingHelper.whereToNavigate(scanResponse) store.dispatch(GlobalAction.Onboarding.Start(scanResponse)) navigateTo(navigateTo, store.state.homeState.shareTransition) } else { - scope.launch { store.state.globalState.tapWalletManager.onCardScanned(scanResponse) } - navigateTo(AppScreen.Wallet, null) + scope.launch { + store.onCardScanned(scanResponse) + withMainContext { + if (scanResponse.twinsIsTwinned() && !preferencesStorage.wasTwinsOnboardingShown()) { + store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.WelcomeOnly)) + navigateTo(AppScreen.OnboardingTwins, store.state.homeState.shareTransition) + } else { + navigateTo(AppScreen.Wallet, null) + } + } + } } }, { changeButtonState(ButtonState.ENABLED) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/BaseOnboardingFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/BaseOnboardingFragment.kt index 7cdbff7733..b33b3118f5 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/BaseOnboardingFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/BaseOnboardingFragment.kt @@ -5,7 +5,7 @@ import android.view.View import com.tangem.tap.common.extensions.inflate import com.tangem.tap.common.extensions.show import com.tangem.tap.common.transitions.HomeToOnboardingTransition -import com.tangem.tap.features.send.BaseStoreFragment +import com.tangem.tap.features.BaseStoreFragment import com.tangem.wallet.R import kotlinx.android.synthetic.main.fragment_onboarding_main.* import kotlinx.android.synthetic.main.view_confetti.* @@ -16,11 +16,6 @@ import org.rekotlin.StoreSubscriber */ abstract class BaseOnboardingFragment : BaseStoreFragment(R.layout.fragment_onboarding_main), StoreSubscriber { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - configureTransitions() - } - override fun configureTransitions() { enterTransition = HomeToOnboardingTransition() exitTransition = HomeToOnboardingTransition() diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt index 173662c65e..80b671393f 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt @@ -16,18 +16,18 @@ import com.tangem.tangem_sdk_new.extensions.fadeOut import com.tangem.tap.common.extensions.getDrawableCompat import com.tangem.tap.common.extensions.stripZeroPlainString import com.tangem.tap.common.postUi +import com.tangem.tap.common.redux.navigation.ShareElement import com.tangem.tap.common.toggleWidget.RefreshBalanceWidget import com.tangem.tap.common.transitions.InternalNoteLayoutTransition import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteAction -import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteResources import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteState import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteStep import com.tangem.tap.store import com.tangem.wallet.R import kotlinx.android.synthetic.main.fragment_onboarding_main.* -import kotlinx.android.synthetic.main.layout_onboarding_bottom_action_views.* -import kotlinx.android.synthetic.main.layout_onboarding_note.* +import kotlinx.android.synthetic.main.layout_onboarding_container_bottom.* +import kotlinx.android.synthetic.main.layout_onboarding_container_top.* import kotlinx.android.synthetic.main.view_onboarding_progress.* import kotlinx.android.synthetic.main.view_onboarding_tv_balance.* @@ -36,23 +36,26 @@ import kotlinx.android.synthetic.main.view_onboarding_tv_balance.* */ class OnboardingNoteFragment : BaseOnboardingFragment() { - private lateinit var btnRefreshBalanceWidget: RefreshBalanceWidget - private var currentStep: OnboardingNoteStep = OnboardingNoteStep.None + private var previousStep: OnboardingNoteStep = OnboardingNoteStep.None - override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_note + private lateinit var btnRefreshBalanceWidget: RefreshBalanceWidget + + override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_container_top + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + postponeEnterTransition() + } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + imv_front_card.transitionName = ShareElement.imvFrontCard + startPostponedEnterTransition() + toolbar.setTitle(R.string.onboarding_title) btnRefreshBalanceWidget = RefreshBalanceWidget(onboarding_main_container) - val resources = OnboardingNoteResources( - OnboardingNoteResources.Strings( - R.string.copy_toast_msg - ) - ) - store.dispatch(OnboardingNoteAction.SetResources(resources)) store.dispatch(OnboardingNoteAction.LoadCardArtwork) store.dispatch(OnboardingNoteAction.DetermineStepOfScreen) } @@ -81,7 +84,7 @@ class OnboardingNoteFragment : BaseOnboardingFragment() { } setBalance(state) showConfetti(state.showConfetti) - currentStep = state.currentStep + previousStep = state.currentStep } private fun setBalance(state: OnboardingNoteState) { @@ -166,7 +169,7 @@ class OnboardingNoteFragment : BaseOnboardingFragment() { } private fun updateConstraints(currentStep: OnboardingNoteStep, @LayoutRes layoutId: Int) { - if (this.currentStep == currentStep) return + if (this.previousStep == currentStep) return val constraintSet = ConstraintSet() constraintSet.clone(requireContext(), layoutId) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt index 859e1104fa..73b588ee69 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt @@ -10,7 +10,6 @@ import org.rekotlin.Action */ sealed class OnboardingNoteAction : Action { // from user, ui - data class SetResources(val resources: OnboardingNoteResources) : OnboardingNoteAction() object LoadCardArtwork : OnboardingNoteAction() object CreateWallet : OnboardingNoteAction() object TopUp : OnboardingNoteAction() diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt index 8267514eb0..0ef12f9c8f 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt @@ -12,6 +12,7 @@ import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.TapError import com.tangem.tap.domain.extensions.hasWallets import com.tangem.tap.domain.extensions.makePrimaryWalletManager +import com.tangem.tap.features.wallet.redux.Currency import com.tangem.tap.features.wallet.redux.ProgressState import com.tangem.tap.scope import com.tangem.tap.store @@ -107,6 +108,7 @@ private fun handleNoteAction(action: Action, dispatch: DispatchFunction) { val isLoadedBefore = noteState.walletBalance.state != ProgressState.Loading val balanceIsLoading = noteState.walletBalance.copy( + currency = Currency.Blockchain(walletManager.wallet.blockchain), state = ProgressState.Loading, error = null, criticalError = null @@ -128,7 +130,7 @@ private fun handleNoteAction(action: Action, dispatch: DispatchFunction) { } is OnboardingNoteAction.ShowAddressInfoDialog -> { val addressData = noteState.walletManager?.getAddressData() ?: return - val addressWasCopied = noteState.resources.strings.addressWasCopied + val addressWasCopied = globalState.resources.strings.addressWasCopied val appDialog = AppDialog.AddressInfoDialog( addressData, onCopyAddress = { store.dispatchToastNotification(addressWasCopied) }, @@ -143,7 +145,7 @@ private fun handleNoteAction(action: Action, dispatch: DispatchFunction) { OnboardingNoteAction.Done -> { store.dispatch(GlobalAction.Onboarding.Stop) scope.launch { - globalState.tapWalletManager.onCardScanned(scanResponse) + store.onCardScanned(scanResponse) store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt index c88906cb20..206a4893ab 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt @@ -23,9 +23,6 @@ private fun internalReduce(action: Action, appState: AppState): OnboardingNoteSt is OnboardingNoteAction.SetWalletManager -> { state = state.copy(walletManager = action.walletManager) } - is OnboardingNoteAction.SetResources -> { - state = state.copy(resources = action.resources) - } is OnboardingNoteAction.Balance.Set -> { state = state.copy(walletBalance = action.balance) } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt index 33c5cbdd7d..23def7368f 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt @@ -10,7 +10,6 @@ import org.rekotlin.StateType */ data class OnboardingNoteState( val walletManager: WalletManager? = null, - val resources: OnboardingNoteResources = OnboardingNoteResources(), // UI val cardArtwork: Artwork? = null, val walletBalance: OnboardingWalletBalance = OnboardingWalletBalance.loading(), @@ -25,12 +24,4 @@ data class OnboardingNoteState( enum class OnboardingNoteStep { None, CreateWallet, TopUpWallet, Done -} - -data class OnboardingNoteResources( - val strings: Strings = Strings() -) { - data class Strings( - val addressWasCopied: Int = -1 - ) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt index abfb32f6d5..f57e92fe03 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt @@ -8,16 +8,18 @@ import androidx.constraintlayout.widget.ConstraintSet import androidx.core.view.isVisible import androidx.transition.TransitionManager import com.tangem.tap.common.extensions.getDrawableCompat +import com.tangem.tap.common.redux.navigation.ShareElement import com.tangem.tap.common.transitions.InternalNoteLayoutTransition import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment +import com.tangem.tap.features.onboarding.products.note.swapToBitmapDrawable import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsAction import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsState import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsStep import com.tangem.tap.store import com.tangem.wallet.R import kotlinx.android.synthetic.main.fragment_onboarding_main.* -import kotlinx.android.synthetic.main.layout_onboarding_bottom_action_views.* -import kotlinx.android.synthetic.main.layout_onboarding_note.* +import kotlinx.android.synthetic.main.layout_onboarding_container_bottom.* +import kotlinx.android.synthetic.main.layout_onboarding_container_top.* import kotlinx.android.synthetic.main.view_onboarding_progress.* /** @@ -25,11 +27,19 @@ import kotlinx.android.synthetic.main.view_onboarding_progress.* */ class OnboardingOtherCardsFragment : BaseOnboardingFragment() { - override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_note + override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_container_top + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + postponeEnterTransition() + } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + imv_front_card.transitionName = ShareElement.imvFrontCard + startPostponedEnterTransition() + toolbar.setTitle(R.string.onboarding_title) store.dispatch(OnboardingOtherCardsAction.LoadCardArtwork) store.dispatch(OnboardingOtherCardsAction.DetermineStepOfScreen) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt index 6fea776c74..fd2dbee87d 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt @@ -1,6 +1,7 @@ package com.tangem.tap.features.onboarding.products.otherCards.redux import com.tangem.common.CompletionResult +import com.tangem.tap.common.extensions.onCardScanned import com.tangem.tap.common.extensions.withMainContext import com.tangem.tap.common.postUi import com.tangem.tap.common.redux.AppState @@ -83,7 +84,7 @@ private fun handleOtherCardsAction(action: Action, dispatch: DispatchFunction) { OnboardingOtherCardsAction.Done -> { store.dispatch(GlobalAction.Onboarding.Stop) scope.launch { - globalState.tapWalletManager.onCardScanned(onboardingManager.scanResponse) + store.onCardScanned(onboardingManager.scanResponse) store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/OnboardingTwinsFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/OnboardingTwinsFragment.kt deleted file mode 100644 index 684d05324e..0000000000 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/OnboardingTwinsFragment.kt +++ /dev/null @@ -1,72 +0,0 @@ -package com.tangem.tap.features.onboarding.products.twins - -import android.os.Bundle -import android.view.View -import androidx.activity.OnBackPressedCallback -import androidx.fragment.app.Fragment -import androidx.transition.TransitionInflater -import com.squareup.picasso.Picasso -import com.tangem.tap.common.extensions.dispatchDebugErrorNotification -import com.tangem.tap.common.extensions.withMainContext -import com.tangem.tap.common.redux.navigation.AppScreen -import com.tangem.tap.common.redux.navigation.NavigationAction -import com.tangem.tap.features.twins.redux.TwinCardsAction -import com.tangem.tap.features.wallet.redux.Artwork -import com.tangem.tap.scope -import com.tangem.tap.store -import com.tangem.wallet.R -import kotlinx.android.synthetic.main.fragment_twin_cards_welcome.* -import kotlinx.android.synthetic.main.layout_twin_cards.* -import kotlinx.coroutines.launch - -class OnboardingTwinsFragment : Fragment(R.layout.fragment_twin_cards_welcome) { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) - } - }) - val inflater = TransitionInflater.from(requireContext()) - enterTransition = inflater.inflateTransition(R.transition.slide_right) - exitTransition = inflater.inflateTransition(R.transition.fade) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - store.dispatch(TwinCardsAction.SetOnboardingShown) - val secondTwinNumber = store.state.twinCardsState.cardNumber?.pairNumber()?.number ?: "" - val text = getString(R.string.twins_onboarding_description_format, secondTwinNumber) - tv_twin_cards_description_1.text = text - - setOnClickListeners() - - Picasso.get() - .load(Artwork.TWIN_CARD_1) - .placeholder(R.drawable.card_placeholder) - ?.error(R.drawable.card_placeholder) - ?.into(iv_twin_card_1) - - Picasso.get() - .load(Artwork.TWIN_CARD_2) - .placeholder(R.drawable.card_placeholder) - ?.error(R.drawable.card_placeholder) - ?.into(iv_twin_card_2) - } - - private fun setOnClickListeners() { - btn_continue.setOnClickListener { - scope.launch { - val global = store.state.globalState - val scanResponse = global.onboardingManager?.scanResponse ?: return@launch - global.tapWalletManager.onCardScanned(scanResponse) - withMainContext { - store.dispatchDebugErrorNotification("Делаем онбоардинг") - store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) - } - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsAction.kt new file mode 100644 index 0000000000..7d48937c17 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsAction.kt @@ -0,0 +1,64 @@ +package com.tangem.tap.features.onboarding.products.twins.redux + +import com.tangem.Message +import com.tangem.blockchain.common.WalletManager +import com.tangem.common.extensions.VoidCallback +import com.tangem.tap.common.redux.StateDialog +import com.tangem.tap.domain.tasks.product.ScanResponse +import com.tangem.tap.domain.twins.AssetReader +import com.tangem.tap.domain.twins.TwinCardsManager +import com.tangem.tap.features.onboarding.OnboardingWalletBalance +import org.rekotlin.Action + +/** +[REDACTED_AUTHOR] + */ +sealed class TwinCardsAction : Action { + data class IfTwinsPrepareState(val scanResponse: ScanResponse) : TwinCardsAction() + + object Init : TwinCardsAction() + data class SetMode(val mode: CreateTwinWalletMode) : TwinCardsAction() + data class SetStepOfScreen(val step: TwinCardsStep) : TwinCardsAction() + data class SetUserUnderstand(val isUnderstand: Boolean) : TwinCardsAction() + + sealed class CardsManager { + data class Set(val manager: TwinCardsManager) : TwinCardsAction() + object Release : TwinCardsAction() + } + + sealed class Wallet : TwinCardsAction() { + data class HandleOnBackPressed( + // this is necessary for the correct animation of the return of cards to the Home screen + val shouldResetTwinCardsWidget: (should: Boolean, popAction: VoidCallback) -> Unit + ) : TwinCardsAction() + + data class ShowInterruptDialog(val onOk: VoidCallback) : TwinCardsAction(), StateDialog + + data class LaunchFirstStep(val initialMessage: Message, val reader: AssetReader) : TwinCardsAction() + data class LaunchSecondStep( + val initialMessage: Message, + val preparingMessage: Message, + val creatingWalletMessage: Message, + ) : TwinCardsAction() + + data class LaunchThirdStep(val message: Message) : TwinCardsAction() + } + + + // for the onboarding + data class SetPairCardId(val cardId: String) : TwinCardsAction() + object TopUp : TwinCardsAction() + object ShowAddressInfoDialog : TwinCardsAction() + data class SetWalletManager(val walletManager: WalletManager) : TwinCardsAction() + object Done : TwinCardsAction() + + sealed class Balance { + object Update : TwinCardsAction() + data class Set(val balance: OnboardingWalletBalance) : TwinCardsAction() + } + + sealed class Confetti { + object Show : TwinCardsAction() + object Hide : TwinCardsAction() + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..f56502e209 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt @@ -0,0 +1,275 @@ +package com.tangem.tap.features.onboarding.products.twins.redux + +import com.tangem.blockchain.extensions.Result +import com.tangem.common.CompletionResult +import com.tangem.common.extensions.guard +import com.tangem.tap.common.extensions.* +import com.tangem.tap.common.postUi +import com.tangem.tap.common.redux.AppDialog +import com.tangem.tap.common.redux.AppState +import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.common.redux.navigation.NavigationAction +import com.tangem.tap.domain.DELAY_SDK_DIALOG_CLOSE +import com.tangem.tap.domain.TapError +import com.tangem.tap.domain.extensions.makePrimaryWalletManager +import com.tangem.tap.domain.tasks.product.ScanResponse +import com.tangem.tap.domain.twins.TwinCardsManager +import com.tangem.tap.features.wallet.redux.Currency +import com.tangem.tap.features.wallet.redux.ProgressState +import com.tangem.tap.preferencesStorage +import com.tangem.tap.scope +import com.tangem.tap.store +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import org.rekotlin.Action +import org.rekotlin.DispatchFunction +import org.rekotlin.Middleware + +class TwinCardsMiddleware { + companion object { + val handler = twinsWalletMiddleware + } +} + +private val twinsWalletMiddleware: Middleware = { dispatch, state -> + { next -> + { action -> + handle(action, dispatch) + next(action) + } + } +} + +private fun handle(action: Action, dispatch: DispatchFunction) { + val action = action as? TwinCardsAction ?: return + + val globalState = store.state.globalState + val onboardingManager = globalState.onboardingManager + val twinCardsState = store.state.twinCardsState + + fun getScanResponse(): ScanResponse { + return when (twinCardsState.mode) { + CreateTwinWalletMode.CreateWallet -> onboardingManager?.scanResponse + CreateTwinWalletMode.RecreateWallet -> globalState.scanResponse + } ?: throw NullPointerException("ScanResponse can't be NULL") + } + + fun updateScanResponse(response: ScanResponse) { + when (twinCardsState.mode) { + CreateTwinWalletMode.CreateWallet -> onboardingManager?.scanResponse = response + CreateTwinWalletMode.RecreateWallet -> store.dispatch(GlobalAction.SaveScanNoteResponse(response)) + } + } + + fun startCardActivation(cardId: String) { + if (twinCardsState.mode == CreateTwinWalletMode.CreateWallet) { + onboardingManager?.activationStarted(cardId) + } + } + + fun finishCardActivation() { + if (twinCardsState.mode == CreateTwinWalletMode.CreateWallet) { + onboardingManager?.activationFinished(getScanResponse().card.cardId) + twinCardsState.pairCardId?.let { onboardingManager?.activationFinished(it) } + + } + } + + when (action) { + is TwinCardsAction.Init -> { + if (twinCardsState.currentStep == TwinCardsStep.WelcomeOnly) return + + when (twinCardsState.mode) { + CreateTwinWalletMode.CreateWallet -> { + if (preferencesStorage.wasTwinsOnboardingShown()) { + val step = when { + !getScanResponse().twinsIsTwinned() -> TwinCardsStep.CreateFirstWallet + twinCardsState.walletBalance.balanceIsToppedUp() -> TwinCardsStep.Done + else -> TwinCardsStep.TopUpWallet + } + store.dispatch((TwinCardsAction.SetStepOfScreen(step))) + } else { + store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.Welcome)) + } + } + CreateTwinWalletMode.RecreateWallet -> { + store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.Warning)) + } + } + } + is TwinCardsAction.Wallet.HandleOnBackPressed -> { + val shouldReturnCardBack = twinCardsState.mode == CreateTwinWalletMode.CreateWallet + && twinCardsState.currentStep != TwinCardsStep.TopUpWallet + && twinCardsState.currentStep != TwinCardsStep.Done + + if (twinCardsState.showAlert) { + val onInterruptPrompt = { + store.dispatch(TwinCardsAction.CardsManager.Release) + action.shouldResetTwinCardsWidget(shouldReturnCardBack) { + store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) + } + } + val stateDialog = TwinCardsAction.Wallet.ShowInterruptDialog(onInterruptPrompt) + store.dispatchDialogShow(stateDialog) + } else { + store.dispatch(TwinCardsAction.CardsManager.Release) + action.shouldResetTwinCardsWidget(shouldReturnCardBack) { + store.dispatch(NavigationAction.PopBackTo()) + } + } + } + is TwinCardsAction.SetStepOfScreen -> { + when (action.step) { + TwinCardsStep.WelcomeOnly -> { + preferencesStorage.saveTwinsOnboardingShown() + } + TwinCardsStep.Welcome -> { + preferencesStorage.saveTwinsOnboardingShown() + } + TwinCardsStep.Warning -> { + } + TwinCardsStep.CreateFirstWallet -> { + } + TwinCardsStep.CreateSecondWallet -> { + } + TwinCardsStep.CreateThirdWallet -> { + } + TwinCardsStep.TopUpWallet -> { + store.dispatch(TwinCardsAction.Balance.Update) + } + TwinCardsStep.Done -> { + finishCardActivation() + postUi(500) { store.dispatch(TwinCardsAction.Confetti.Show) } + } + } + } + is TwinCardsAction.Wallet.LaunchFirstStep -> { + val manager = TwinCardsManager(getScanResponse().card, action.reader) + store.dispatch(TwinCardsAction.CardsManager.Set(manager)) + + scope.launch { + when (val result = manager.createFirstWallet(action.initialMessage)) { + is CompletionResult.Success -> { + startCardActivation(result.data.cardId) + delay(DELAY_SDK_DIALOG_CLOSE) + withMainContext { + store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.CreateSecondWallet)) + } + } + is CompletionResult.Failure -> { + } + } + } + } + is TwinCardsAction.Wallet.LaunchSecondStep -> { + val manager = twinCardsState.twinCardsManager ?: return + + scope.launch { + val result = manager.createSecondWallet( + action.initialMessage, + action.preparingMessage, + action.creatingWalletMessage + ) + when (result) { + is CompletionResult.Success -> { + startCardActivation(result.data.cardId) + delay(DELAY_SDK_DIALOG_CLOSE) + withMainContext { + store.dispatch(TwinCardsAction.SetPairCardId(result.data.cardId)) + store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.CreateThirdWallet)) + } + } + is CompletionResult.Failure -> { + } + } + } + } + is TwinCardsAction.Wallet.LaunchThirdStep -> { + val manager = twinCardsState.twinCardsManager ?: return + + scope.launch { + when (val result = manager.complete(action.message)) { + is Result.Success -> { + updateScanResponse(result.data) + delay(DELAY_SDK_DIALOG_CLOSE) + withMainContext { store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.TopUpWallet)) } + } + is Result.Failure -> { + } + } + } + } + + is TwinCardsAction.Balance.Update -> { + val walletManager = if (twinCardsState.walletManager != null) { + twinCardsState.walletManager + } else { + val wmFactory = globalState.tapWalletManager.walletManagerFactory + val walletManager = wmFactory.makePrimaryWalletManager(getScanResponse()).guard { + val message = "Loading cancelled. Cause: wallet manager didn't created" + val customError = TapError.CustomError(message) + store.dispatchErrorNotification(customError) + return + } + dispatch(TwinCardsAction.SetWalletManager(walletManager)) + walletManager + } + val isLoadedBefore = twinCardsState.walletBalance.state != ProgressState.Loading + val balanceIsLoading = twinCardsState.walletBalance.copy( + currency = Currency.Blockchain(walletManager.wallet.blockchain), + state = ProgressState.Loading, + error = null, + criticalError = null + ) + store.dispatch(TwinCardsAction.Balance.Set(balanceIsLoading)) + + scope.launch { + val loadedBalance = onboardingManager?.updateBalance(walletManager) ?: return@launch + loadedBalance.criticalError?.let { store.dispatchErrorNotification(it) } + delay(if (isLoadedBefore) 0 else 300) + withMainContext { store.dispatch(TwinCardsAction.Balance.Set(loadedBalance)) } + } + + } + is TwinCardsAction.Balance.Set -> { + if (action.balance.balanceIsToppedUp()) { + scope.launch { + withMainContext { store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.Done)) } + } + } + } + is TwinCardsAction.ShowAddressInfoDialog -> { + val addressData = twinCardsState.walletManager?.getAddressData() ?: return + + val addressWasCopied = globalState.resources.strings.addressWasCopied + val appDialog = AppDialog.AddressInfoDialog( + addressData, + onCopyAddress = { store.dispatchToastNotification(addressWasCopied) }, + onExploreAddress = { store.dispatchOpenUrl(addressData.exploreUrl) } + ) + store.dispatchDialogShow(appDialog) + } + is TwinCardsAction.TopUp -> { + val topUpUrl = twinCardsState.walletManager?.getToUpUrl() ?: return + store.dispatchOpenUrl(topUpUrl) + } + TwinCardsAction.Done -> { + scope.launch { + val scanResponse = getScanResponse() + store.onCardScanned(scanResponse) + withMainContext { + when (twinCardsState.mode) { + CreateTwinWalletMode.CreateWallet -> { + store.dispatch(GlobalAction.Onboarding.Stop) + store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) + } + CreateTwinWalletMode.RecreateWallet -> { + store.dispatch(NavigationAction.PopBackTo()) + } + } + } + } + } + } +} diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsReducer.kt new file mode 100644 index 0000000000..1d18eb4dec --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsReducer.kt @@ -0,0 +1,64 @@ +package com.tangem.tap.features.onboarding.products.twins.redux + +import com.tangem.tap.common.redux.AppState +import com.tangem.tap.domain.twins.getTwinCardNumber +import org.rekotlin.Action + +class TwinCardsReducer { + companion object { + fun reduce(action: Action, state: AppState): TwinCardsState = internalReduce(action, state) + } +} + +private fun internalReduce(action: Action, state: AppState): TwinCardsState { + if (action !is TwinCardsAction) return state.twinCardsState + + var state = state.twinCardsState + + when (action) { + is TwinCardsAction.IfTwinsPrepareState -> { + state = if (action.scanResponse.isTangemTwins()) { + val cardNumber = action.scanResponse.card.getTwinCardNumber() ?: return state + + TwinCardsState( + cardNumber = cardNumber, + mode = CreateTwinWalletMode.CreateWallet, + ) + } else { + TwinCardsState() + } + } + is TwinCardsAction.SetMode -> { + state = state.copy(mode = action.mode) + } + is TwinCardsAction.SetStepOfScreen -> { + state = state.copy(currentStep = action.step) + } + is TwinCardsAction.SetUserUnderstand -> { + state = state.copy(userWasUnderstandIfWalletRecreate = action.isUnderstand) + } + is TwinCardsAction.CardsManager.Set -> { + state = state.copy(twinCardsManager = action.manager) + } + TwinCardsAction.CardsManager.Release -> { + state = state.copy(twinCardsManager = null) + } + is TwinCardsAction.SetPairCardId -> { + state = state.copy(pairCardId = action.cardId) + } + is TwinCardsAction.SetWalletManager -> { + state = state.copy(walletManager = action.walletManager) + } + is TwinCardsAction.Balance.Set -> { + state = state.copy(walletBalance = action.balance) + } + is TwinCardsAction.Confetti.Show -> { + state = state.copy(showConfetti = true) + } + is TwinCardsAction.Confetti.Hide -> { + state = state.copy(showConfetti = false) + } + } + + return state +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsState.kt new file mode 100644 index 0000000000..ac731df22a --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsState.kt @@ -0,0 +1,61 @@ +package com.tangem.tap.features.onboarding.products.twins.redux + +import com.tangem.blockchain.common.WalletManager +import com.tangem.tap.domain.twins.TwinCardNumber +import com.tangem.tap.domain.twins.TwinCardsManager +import com.tangem.tap.features.onboarding.OnboardingWalletBalance +import org.rekotlin.StateType + +/** +[REDACTED_AUTHOR] + */ +data class TwinCardsState( + // if cardNumber = null -> the scanned card is not Twin + val cardNumber: TwinCardNumber? = null, + val mode: CreateTwinWalletMode = CreateTwinWalletMode.CreateWallet, + val currentStep: TwinCardsStep = TwinCardsStep.Welcome, + val userWasUnderstandIfWalletRecreate: Boolean = false, + // available only when creating or recreating wallets + val twinCardsManager: TwinCardsManager? = null, + + // for the onboarding + val pairCardId: String? = null, // available after create\recreate wallets + val walletManager: WalletManager? = null, + val walletBalance: OnboardingWalletBalance = OnboardingWalletBalance.loading(), + val showConfetti: Boolean = false, +) : StateType { + + val steps: List + get() = when (mode) { + CreateTwinWalletMode.CreateWallet -> listOf( + TwinCardsStep.None, + TwinCardsStep.CreateFirstWallet, + TwinCardsStep.CreateSecondWallet, + TwinCardsStep.CreateThirdWallet, + TwinCardsStep.TopUpWallet, + TwinCardsStep.Done + ) + CreateTwinWalletMode.RecreateWallet -> listOf( + TwinCardsStep.None, + TwinCardsStep.CreateFirstWallet, + TwinCardsStep.CreateSecondWallet, + TwinCardsStep.CreateThirdWallet, + TwinCardsStep.Done + ) + } + + val progress: Int + get() = steps.indexOf(currentStep) + + val showAlert: Boolean + get() = currentStep == TwinCardsStep.CreateSecondWallet || currentStep == TwinCardsStep.CreateThirdWallet +} + +enum class CreateTwinWalletMode { CreateWallet, RecreateWallet } + +enum class TwinCardsStep { + None, WelcomeOnly, Welcome, Warning, CreateFirstWallet, CreateSecondWallet, CreateThirdWallet, + + // for the onboarding + TopUpWallet, Done +} \ No newline at end of file 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 new file mode 100644 index 0000000000..3a1f683cca --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/TwinsCardsFragment.kt @@ -0,0 +1,366 @@ +package com.tangem.tap.features.onboarding.products.twins.ui + +import android.os.Bundle +import android.view.View +import android.view.animation.OvershootInterpolator +import androidx.annotation.LayoutRes +import androidx.constraintlayout.widget.ConstraintSet +import androidx.core.view.isVisible +import androidx.transition.TransitionInflater +import androidx.transition.TransitionManager +import com.squareup.picasso.Picasso +import com.tangem.Message +import com.tangem.blockchain.common.Blockchain +import com.tangem.common.extensions.VoidCallback +import com.tangem.tap.common.extensions.* +import com.tangem.tap.common.leapfrogWidget.LeapfrogWidget +import com.tangem.tap.common.postUi +import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.common.redux.navigation.NavigationAction +import com.tangem.tap.common.redux.navigation.ShareElement +import com.tangem.tap.common.toggleWidget.RefreshBalanceWidget +import com.tangem.tap.common.transitions.InternalNoteLayoutTransition +import com.tangem.tap.domain.twins.AssetReader +import com.tangem.tap.domain.twins.TwinCardNumber +import com.tangem.tap.domain.twins.TwinsCardWidget +import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment +import com.tangem.tap.features.onboarding.products.twins.redux.CreateTwinWalletMode +import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction +import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState +import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsStep +import com.tangem.tap.features.wallet.redux.Artwork +import com.tangem.tap.store +import com.tangem.wallet.R +import kotlinx.android.synthetic.main.fragment_onboarding_main.* +import kotlinx.android.synthetic.main.layout_onboarding_container_bottom.* +import kotlinx.android.synthetic.main.layout_onboarding_container_top.* +import kotlinx.android.synthetic.main.view_bg_twins_welcome.* +import kotlinx.android.synthetic.main.view_onboarding_progress.* +import kotlinx.android.synthetic.main.view_onboarding_tv_balance.* +import org.rekotlin.Action + +class TwinsCardsFragment : BaseOnboardingFragment() { + + private var previousStep = TwinCardsStep.None + + private lateinit var twinsWidget: TwinsCardWidget + private lateinit var btnRefreshBalanceWidget: RefreshBalanceWidget + + private val assetReader: AssetReader by lazy { + object : AssetReader { + override fun readAssetAsString(name: String): String = requireContext().readAssetAsString(name) + } + } + + override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_container_top + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + postponeEnterTransition() + store.dispatch(TwinCardsAction.Init) + } + + override fun configureTransitions() { + when (store.state.twinCardsState.mode) { + CreateTwinWalletMode.CreateWallet -> super.configureTransitions() + CreateTwinWalletMode.RecreateWallet -> { + val inflater = TransitionInflater.from(requireContext()) + enterTransition = inflater.inflateTransition(R.transition.slide_right) + exitTransition = inflater.inflateTransition(R.transition.fade) + } + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + reconfigureLayoutForTwins() + twinsWidget = TwinsCardWidget(LeapfrogWidget(cards_container)) { 250f } + btnRefreshBalanceWidget = RefreshBalanceWidget(onboarding_main_container) + + toolbar.title = getText(R.string.twins_recreate_toolbar) + + Picasso.get() + .load(Artwork.TWIN_CARD_1) + .placeholder(R.drawable.card_placeholder_black) + ?.into(imv_twin_front_card) + + Picasso.get() + .load(Artwork.TWIN_CARD_2) + .placeholder(R.drawable.card_placeholder_white) + ?.into(imv_twin_back_card) + } + + private fun reconfigureLayoutForTwins() { + imv_front_card.hide() + imv_card_background.hide() + cards_container.show() + + imv_twin_front_card.transitionName = ShareElement.imvFrontCard + imv_twin_back_card.transitionName = ShareElement.imvBackCard + } + + override fun subscribeToStore() { + store.subscribe(this) { state -> + state.skipRepeats { oldState, newState -> + oldState.twinCardsState == newState.twinCardsState + }.select { it.twinCardsState } + } + storeSubscribersList.add(this) + } + + override fun newState(state: TwinCardsState) { + if (activity == null) return + +// toolbar.title = when (state.mode) { +// CreateTwinWalletMode.CreateWallet -> getText(R.string.wallet_button_create_wallet) +// CreateTwinWalletMode.RecreateWallet -> getText(R.string.twins_recreate_toolbar) +// } + + pb_state.max = state.steps.size - 1 + pb_state.progress = state.progress + + when (state.currentStep) { + TwinCardsStep.WelcomeOnly -> setupWelcomeOnlyState(state) + TwinCardsStep.Welcome -> setupWelcomeState(state) + TwinCardsStep.Warning -> setupWarningState(state) + TwinCardsStep.CreateFirstWallet -> setupCreateFirstWalletState(state) + TwinCardsStep.CreateSecondWallet -> setupCreateSecondWalletState(state) + TwinCardsStep.CreateThirdWallet -> setupCreateThirdWalletState(state) + TwinCardsStep.TopUpWallet -> setupTopUpWalletState(state) + TwinCardsStep.Done -> setupDoneState(state) + } + + setBalance(state) + showConfetti(state.showConfetti) + previousStep = state.currentStep + } + + private fun setBalance(state: TwinCardsState) { + if (state.walletBalance.currency.blockchain == Blockchain.Unknown) return + + val balanceValue = state.walletBalance.value.stripZeroPlainString() + val currency = state.walletBalance.currency.currencySymbol + tv_balance_value.text = "$balanceValue $currency" + } + + private fun setupWelcomeOnlyState(state: TwinCardsState) { + setupWelcomeState(state, NavigationAction.NavigateTo(AppScreen.Wallet)) + } + + private fun setupWelcomeState(state: TwinCardsState) { + setupWelcomeState(state, TwinCardsAction.SetStepOfScreen(TwinCardsStep.CreateFirstWallet)) + } + + private fun setupWelcomeState(state: TwinCardsState, mainAction: Action) { + twinsWidget.toWelcome(false) { startPostponedEnterTransition() } + + pb_state.hide() + + tv_header.setText(R.string.twins_onboarding_subtitle) + tv_body.text = getString(R.string.twins_onboarding_description_format, state.cardNumber?.pairIndexNumber()) + + btn_main_action.setText(R.string.common_continue) + btn_main_action.setOnClickListener { store.dispatch(mainAction) } + } + + private fun setupWarningState(state: TwinCardsState) { + twinsWidget.toWelcome(false) { startPostponedEnterTransition() } + pb_state.hide() + chb_understand.show() + + tv_header.setText(R.string.common_warning) + tv_body.setText(R.string.twins_recreate_warning) + + chb_understand.setOnCheckedChangeListener { buttonView, isChecked -> + store.dispatch(TwinCardsAction.SetUserUnderstand(isChecked)) + } + btn_main_action.isEnabled = state.userWasUnderstandIfWalletRecreate + btn_main_action.setText(R.string.common_continue) + btn_main_action.setOnClickListener { + store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.CreateFirstWallet)) + } + } + + private fun setupCreateFirstWalletState(state: TwinCardsState) { + bg_circle_large.hide() + bg_circle_medium.hide() + bg_circle_min.hide() + chb_understand.hide() + pb_state.show() + + onboarding_main_container.beginDelayedTransition() + + when (previousStep) { + TwinCardsStep.None -> { + twinsWidget.toLeapfrog(false) { + when (state.cardNumber) { + TwinCardNumber.First -> startPostponedEnterTransition() + TwinCardNumber.Second -> { + switchToCard(state.cardNumber, false) { startPostponedEnterTransition() } + } + } + } + } + TwinCardsStep.Welcome, TwinCardsStep.Warning -> { + twinsWidget.toLeapfrog(onEnd = { switchToCard(state.cardNumber) }) + } + } + + val twinIndexNumber = state.cardNumber?.indexNumber() + tv_header.text = getString(R.string.twins_recreate_title_format, twinIndexNumber) + tv_body.setText(R.string.onboarding_twins_interrupt_warning) + + btn_main_action.text = getString(R.string.twins_recreate_button_format, twinIndexNumber) + btn_main_action.setOnClickListener { + store.dispatch(TwinCardsAction.Wallet.LaunchFirstStep( + Message(getString(R.string.twins_recreate_title_format, twinIndexNumber)), + assetReader + )) + } + + btn_alternative_action.isVisible = false + btn_alternative_action.isClickable = false + } + + private fun setupCreateSecondWalletState(state: TwinCardsState) { + switchToCard(state.cardNumber?.pairNumber()) + + val twinPairIndexNumber = state.cardNumber?.pairIndexNumber() + tv_header.text = getString(R.string.twins_recreate_title_format, twinPairIndexNumber) + tv_body.setText(R.string.onboarding_twins_interrupt_warning) + + btn_main_action.text = getString(R.string.twins_recreate_button_format, twinPairIndexNumber) + btn_main_action.setOnClickListener { + store.dispatch(TwinCardsAction.Wallet.LaunchSecondStep( + Message(getString(R.string.twins_recreate_title_format, twinPairIndexNumber)), + Message(getString(R.string.twins_recreate_title_preparing)), + Message(getString(R.string.twins_recreate_title_creating_wallet)), + )) + } + } + + private fun setupCreateThirdWalletState(state: TwinCardsState) { + switchToCard(state.cardNumber) + + val twinIndexNumber = state.cardNumber?.indexNumber() + tv_header.text = getString(R.string.twins_recreate_title_format, twinIndexNumber) + tv_body.setText(R.string.onboarding_twins_interrupt_warning) + + btn_main_action.text = getString(R.string.twins_recreate_button_format, twinIndexNumber) + btn_main_action.setOnClickListener { + store.dispatch(TwinCardsAction.Wallet.LaunchThirdStep( + Message(getString(R.string.twins_recreate_title_format, twinIndexNumber)) + )) + } + } + + + private fun setupTopUpWalletState(state: TwinCardsState) { + when (previousStep) { + TwinCardsStep.None -> { + when (state.cardNumber) { + TwinCardNumber.First -> { + twinsWidget.leapfrogWidget.unfold(false) { + twinsWidget.toActivate(false) { + startPostponedEnterTransition() + } + } + } + TwinCardNumber.Second -> { + switchToCard(state.cardNumber, false) { + twinsWidget.toActivate(false) + startPostponedEnterTransition() + } + } + } + } + TwinCardsStep.CreateThirdWallet -> { + twinsWidget.toActivate() + } + } + + btn_main_action.setText(R.string.onboarding_top_up_button_but_crypto) + btn_main_action.setOnClickListener { + store.dispatch(TwinCardsAction.TopUp) + } + + btn_alternative_action.setText(R.string.onboarding_top_up_button_show_wallet_address) + btn_alternative_action.setOnClickListener { + store.dispatch(TwinCardsAction.ShowAddressInfoDialog) + } + + tv_header.setText(R.string.onboarding_top_up_header) + tv_body.setText(R.string.onboarding_top_up_body) + + btnRefreshBalanceWidget.changeState(state.walletBalance.state) + if (btnRefreshBalanceWidget.isShowing != true) { + postUi(300) { + btnRefreshBalanceWidget.mainView.setOnClickListener { + store.dispatch(TwinCardsAction.Balance.Update) + } + } + } + + imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_rectangle_rounded_8)) + updateConstraints(state.currentStep, R.layout.lp_onboarding_topup_wallet_twins) + } + + private fun setupDoneState(state: TwinCardsState) { + btn_main_action.setText(R.string.onboarding_done_button_continue) + btn_main_action.setOnClickListener { + store.dispatch(TwinCardsAction.Confetti.Hide) + store.dispatch(TwinCardsAction.Done) + } + + btn_alternative_action.isVisible = false + btn_alternative_action.isClickable = false + + tv_header.setText(R.string.onboarding_done_header) + tv_body.setText(R.string.onboarding_done_body) + + val layout = when (state.mode) { + CreateTwinWalletMode.CreateWallet -> R.layout.lp_onboarding_done_activation_twins + CreateTwinWalletMode.RecreateWallet -> R.layout.lp_onboarding_done + } + updateConstraints(state.currentStep, layout) + } + + private fun updateConstraints(currentStep: TwinCardsStep, @LayoutRes layoutId: Int) { + if (this.previousStep == currentStep) return + + val constraintSet = ConstraintSet() + constraintSet.clone(requireContext(), layoutId) + constraintSet.applyTo(onboarding_main_container) + val transition = InternalNoteLayoutTransition() + transition.interpolator = OvershootInterpolator() + TransitionManager.beginDelayedTransition(onboarding_main_container, transition) + } + + private fun switchToCard(cardNumber: TwinCardNumber?, animate: Boolean = true, onEnd: VoidCallback = {}) { + val position = cardNumber?.number ?: return + + if (twinsWidget.leapfrogWidget.getViewPositionByIndex(1) != position - 1) { + twinsWidget.leapfrogWidget.leap(animate, onEnd) + } else { + onEnd() + } + } + + override fun handleOnBackPressed() { + store.dispatch(TwinCardsAction.Wallet.HandleOnBackPressed { should, popAction -> + store.dispatch(TwinCardsAction.Confetti.Hide) + showConfetti(false) + if (should) switchToCard(TwinCardNumber.First, true, popAction) + else popAction() + }) + } +} + +private fun TwinCardNumber?.indexNumber(): String { + return this?.number?.toString() ?: "" +} + +private fun TwinCardNumber?.pairIndexNumber(): String { + return this?.pairNumber()?.number?.toString() ?: "" +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/twins/ui/dialog/CreateWalletInterruptDialog.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/dialog/CreateWalletInterruptDialog.kt similarity index 66% rename from app/src/main/java/com/tangem/tap/features/twins/ui/dialog/CreateWalletInterruptDialog.kt rename to app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/dialog/CreateWalletInterruptDialog.kt index ab34b78039..0020137a82 100644 --- a/app/src/main/java/com/tangem/tap/features/twins/ui/dialog/CreateWalletInterruptDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/dialog/CreateWalletInterruptDialog.kt @@ -1,10 +1,10 @@ -package com.tangem.tap.features.twins.ui.dialog +package com.tangem.tap.features.onboarding.products.twins.ui.dialog import android.content.Context import androidx.appcompat.app.AlertDialog import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.tangem.tap.common.extensions.dispatchDialogHide -import com.tangem.tap.features.twins.redux.TwinCardsAction +import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction import com.tangem.tap.store import com.tangem.wallet.R @@ -13,10 +13,10 @@ import com.tangem.wallet.R */ class CreateWalletInterruptDialog { companion object { - fun create(state: TwinCardsAction.Wallet.InterruptDialog, context: Context): AlertDialog { + fun create(state: TwinCardsAction.Wallet.ShowInterruptDialog, context: Context): AlertDialog { return MaterialAlertDialogBuilder(context) - .setMessage(R.string.details_twins_recreate_alert) - .setPositiveButton(R.string.common_ok) { _, _ -> state.interrupt() } + .setMessage(R.string.twins_recreate_alert) + .setPositiveButton(R.string.common_ok) { _, _ -> state.onOk() } .setNegativeButton(R.string.common_cancel) { _, _ -> } .setOnDismissListener { store.dispatchDialogHide() } .create() diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/OnboardingWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/OnboardingWalletFragment.kt index d0463040bd..69c66581a6 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/OnboardingWalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/OnboardingWalletFragment.kt @@ -3,8 +3,8 @@ package com.tangem.tap.features.onboarding.products.wallet import android.os.Bundle import android.view.View import androidx.transition.TransitionInflater +import com.tangem.tap.features.BaseStoreFragment import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletState -import com.tangem.tap.features.send.BaseStoreFragment import com.tangem.tap.store import com.tangem.wallet.R import org.rekotlin.StoreSubscriber diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt index bb675cf3fe..4c35c25b24 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt @@ -7,7 +7,6 @@ import android.text.method.DigitsKeyListener import android.view.View import android.view.inputmethod.EditorInfo import android.widget.EditText -import androidx.activity.OnBackPressedCallback import androidx.core.view.postDelayed import androidx.core.widget.addTextChangedListener import androidx.recyclerview.widget.LinearLayoutManager @@ -25,7 +24,7 @@ import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.common.snackBar.MaxAmountSnackbar import com.tangem.tap.common.text.truncateMiddleWith import com.tangem.tap.common.toggleWidget.* -import com.tangem.tap.features.send.BaseStoreFragment +import com.tangem.tap.features.BaseStoreFragment import com.tangem.tap.features.send.redux.* import com.tangem.tap.features.send.redux.AddressPayIdActionUi.* import com.tangem.tap.features.send.redux.AmountActionUi.* @@ -63,23 +62,6 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { private val sendSubscriber = SendStateSubscriber(this) private lateinit var keyboardObserver: KeyboardObserver - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - val externalTransactionData = store.state.sendState.externalTransactionData - if (externalTransactionData == null) { - store.dispatch(NavigationAction.PopBackTo()) - } else { - store.dispatch( - WalletAction.TradeCryptoAction.FinishSelling(externalTransactionData.transactionId) - ) - } - } - }) - } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -97,7 +79,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { private fun initSendButtonStates() { btnSend.setOnClickListener { store.dispatch(SendActionUi.SendAmountToRecipient( - Message(getString(R.string.initial_message_sign_header)) + Message(getString(R.string.initial_message_sign_header)) )) } sendBtn = IndeterminateProgressButtonWidget(btnSend, progress) @@ -124,8 +106,8 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { } imvQrCode.setOnClickListener { startActivityForResult( - Intent(requireContext(), ScanQrCodeActivity::class.java), - ScanQrCodeActivity.SCAN_QR_REQUEST_CODE + Intent(requireContext(), ScanQrCodeActivity::class.java), + ScanQrCodeActivity.SCAN_QR_REQUEST_CODE ) } } @@ -281,6 +263,17 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { sp.edit().putString("mainCurrency", type.name).apply() } + override fun handleOnBackPressed() { + val externalTransactionData = store.state.sendState.externalTransactionData + if (externalTransactionData == null) { + store.dispatch(NavigationAction.PopBackTo()) + } else { + store.dispatch( + WalletAction.TradeCryptoAction.FinishSelling(externalTransactionData.transactionId) + ) + } + } + override fun onDestroy() { keyboardObserver.unregisterListener() store.dispatch(ReleaseSendState) diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/FragmentStateSubscriber.kt b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/FragmentStateSubscriber.kt index aa129ebbb5..17dfb7a5fc 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/FragmentStateSubscriber.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/FragmentStateSubscriber.kt @@ -1,6 +1,6 @@ package com.tangem.tap.features.send.ui.stateSubscribers -import com.tangem.tap.features.send.BaseStoreFragment +import com.tangem.tap.features.BaseStoreFragment import org.rekotlin.StateType import org.rekotlin.StoreSubscriber import java.lang.ref.WeakReference diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt index 2471b257de..a8987dcd0e 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt @@ -12,7 +12,7 @@ import com.tangem.tap.common.redux.getMessageString import com.tangem.tap.common.text.DecimalDigitsInputFilter import com.tangem.tap.domain.MultiMessageError import com.tangem.tap.domain.assembleErrors -import com.tangem.tap.features.send.BaseStoreFragment +import com.tangem.tap.features.BaseStoreFragment import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.Error import com.tangem.tap.features.send.redux.FeeAction import com.tangem.tap.features.send.redux.SendAction @@ -188,15 +188,15 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber (fg as? SendFragment)?.saveMainCurrency(state.mainCurrency.type) val balanceText = fg.getString(R.string.send_balance_subtitle_format, - state.mainCurrency.currencySymbol, - state.viewBalanceValue) + state.mainCurrency.currencySymbol, + state.viewBalanceValue) fg.tvBalance.update(balanceText) fg.tilAmountToSend.isEnabled = state.inputIsEnabled val imageRes = if (state.inputIsEnabled) R.drawable.ic_arrows_up_down else 0 fg.tvAmountCurrency.setCompoundDrawablesWithIntrinsicBounds(0, 0, imageRes, 0) - val textColor = if (state.inputIsEnabled) R.color.blue else R.color.textGray + val textColor = if (state.inputIsEnabled) R.color.blue else R.color.textGray fg.tvAmountCurrency.setTextColor(fg.getColor(textColor)) } @@ -245,7 +245,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber totalLayout.tvTotalValue.update("${roughOrEmpty(receipt.totalFiat)} ${receipt.symbols.fiat}") val willSent = getString(R.string.send_total_subtitle_format, - receipt.willSentCrypto, receipt.symbols.crypto) + receipt.willSentCrypto, receipt.symbols.crypto) totalLayout.tvWillBeSentValue.update(willSent) } @@ -277,9 +277,9 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber totalLayout.tvTotalValue.update("${roughOrEmpty(receipt.totalFiat)} ${receipt.symbols.fiat}") val willSent = getString( - R.string.send_total_subtitle_asset_format, - receipt.symbols.token ?: "", receipt.willSentToken, - receipt.symbols.crypto, receipt.willSentFeeCoin + R.string.send_total_subtitle_asset_format, + receipt.symbols.token ?: "", receipt.willSentToken, + receipt.symbols.crypto, receipt.willSentFeeCoin ) totalLayout.tvWillBeSentValue.update(willSent) } diff --git a/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsAction.kt b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsAction.kt deleted file mode 100644 index 383481a905..0000000000 --- a/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsAction.kt +++ /dev/null @@ -1,62 +0,0 @@ -package com.tangem.tap.features.twins.redux - -import com.tangem.Message -import com.tangem.tap.common.redux.StateDialog -import com.tangem.tap.domain.tasks.product.ScanResponse -import com.tangem.tap.domain.twins.AssetReader -import com.tangem.tap.domain.twins.TwinCardNumber -import com.tangem.tap.domain.twins.TwinCardsManager -import org.rekotlin.Action - -/** -[REDACTED_AUTHOR] - */ -sealed class TwinCardsAction : Action { - - data class SetResources(val resources: TwinCardsResources) : TwinCardsAction() - - data class SetTwinCard( - val number: TwinCardNumber, - val secondCardId: String?, - val isCreatingTwinCardsAllowed: Boolean, - ) : TwinCardsAction() - - sealed class CardsManager { - data class Set(val manager: TwinCardsManager) : TwinCardsAction() - object Release : TwinCardsAction() - } - - object ShowOnboarding : TwinCardsAction() - object SetOnboardingShown : TwinCardsAction() - object ProceedToCreateWallet : TwinCardsAction() - - sealed class Wallet : TwinCardsAction() { - object HandleOnBackPressed : TwinCardsAction() - - data class Create( - val number: TwinCardNumber, - val createTwinWalletMode: CreateTwinWalletMode - ) : TwinCardsAction() - - data class InterruptDialog(val interrupt: () -> Unit) : TwinCardsAction(), StateDialog - - data class LaunchFirstStep( - val initialMessage: Message, - val reader: AssetReader - ) : TwinCardsAction() { - object Success : TwinCardsAction() - } - - data class LaunchSecondStep( - val initialMessage: Message, - val preparingMessage: Message, - val creatingWalletMessage: Message, - ) : TwinCardsAction() { - object Success : TwinCardsAction() - } - - data class LaunchThirdStep(val message: Message) : TwinCardsAction() { - data class Success(val scanResponse: ScanResponse) : TwinCardsAction() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsMiddleware.kt deleted file mode 100644 index 84c837d778..0000000000 --- a/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsMiddleware.kt +++ /dev/null @@ -1,139 +0,0 @@ -package com.tangem.tap.features.twins.redux - -import com.tangem.blockchain.extensions.Result -import com.tangem.blockchain.extensions.SimpleResult -import com.tangem.tap.common.extensions.dispatchDialogShow -import com.tangem.tap.common.extensions.dispatchNotification -import com.tangem.tap.common.redux.AppState -import com.tangem.tap.common.redux.navigation.AppScreen -import com.tangem.tap.common.redux.navigation.NavigationAction -import com.tangem.tap.domain.extensions.toSendableAmounts -import com.tangem.tap.domain.twins.TwinCardsManager -import com.tangem.tap.preferencesStorage -import com.tangem.tap.scope -import com.tangem.tap.store -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import org.rekotlin.Action -import org.rekotlin.DispatchFunction -import org.rekotlin.Middleware - -class TwinCardsMiddleware { - companion object { - val handler = twinsWalletMiddleware - } -} - -private val twinsWalletMiddleware: Middleware = { dispatch, state -> - { next -> - { action -> - handle(action, dispatch) - next(action) - } - } -} - -private fun handle(action: Action, dispatch: DispatchFunction) { - val action = action as? TwinCardsAction ?: return - - val twinCardsState = store.state.twinCardsState - - when (action) { - is TwinCardsAction.SetTwinCard -> { - val showOnboarding = !preferencesStorage.wasTwinsOnboardingShown() - if (showOnboarding) store.dispatch(TwinCardsAction.ShowOnboarding) - } - TwinCardsAction.SetOnboardingShown -> { - preferencesStorage.saveTwinsOnboardingShown() - } - is TwinCardsAction.ProceedToCreateWallet -> { - store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWallet)) - } - is TwinCardsAction.Wallet.HandleOnBackPressed -> { - if (twinCardsState.createWalletState?.showAlert == true) { - val stateDialog = TwinCardsAction.Wallet.InterruptDialog { - store.dispatch(TwinCardsAction.CardsManager.Release) - store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) - } - store.dispatchDialogShow(stateDialog) - } else { - store.dispatch(TwinCardsAction.CardsManager.Release) - store.dispatch(NavigationAction.PopBackTo()) - } - } - is TwinCardsAction.Wallet.Create -> { - val wallet = store.state.walletState.walletManagers.map { it.wallet }.firstOrNull() - if (wallet == null) { - store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWalletWarning)) - return - } - val notEmpty = wallet.recentTransactions.isNotEmpty() || wallet.amounts.toSendableAmounts().isNotEmpty() - if (notEmpty) { - store.dispatchNotification(twinCardsState.resources.strings.walletIsNotEmpty) - } else { - store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWalletWarning)) - } - } - is TwinCardsAction.Wallet.LaunchFirstStep -> { - val scanResponse = store.state.globalState.scanResponse ?: return - - val manager = TwinCardsManager(scanResponse, action.reader) - store.dispatch(TwinCardsAction.CardsManager.Set(manager)) - - scope.launch { - val result = manager.createFirstWallet(action.initialMessage) - withContext(Dispatchers.Main) { - when (result) { - SimpleResult.Success -> { - store.dispatch(TwinCardsAction.Wallet.LaunchFirstStep.Success) - } - is SimpleResult.Failure -> { - } - } - } - } - } - is TwinCardsAction.Wallet.LaunchSecondStep -> { - val manager = twinCardsState.twinCardsManager ?: return - - scope.launch { - val result = manager.createSecondWallet(action.initialMessage, - action.preparingMessage, action.creatingWalletMessage) - withContext(Dispatchers.Main) { - when (result) { - SimpleResult.Success -> { - store.dispatch(TwinCardsAction.Wallet.LaunchSecondStep.Success) - } - is SimpleResult.Failure -> { - } - } - } - } - } - is TwinCardsAction.Wallet.LaunchThirdStep -> { - val manager = twinCardsState.twinCardsManager ?: return - - scope.launch { - val result = manager.complete(action.message) - withContext(Dispatchers.Main) { - when (result) { - is Result.Success -> { - store.dispatch(TwinCardsAction.Wallet.LaunchThirdStep.Success(result.data)) - } - is Result.Failure -> { - - } - } - } - } - } - is TwinCardsAction.Wallet.LaunchThirdStep.Success -> { - scope.launch { - store.state.globalState.tapWalletManager.onCardScanned(action.scanResponse) - } - store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) - store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsReducer.kt b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsReducer.kt deleted file mode 100644 index 89978790a1..0000000000 --- a/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsReducer.kt +++ /dev/null @@ -1,60 +0,0 @@ -package com.tangem.tap.features.twins.redux - -import com.tangem.tap.common.redux.AppState -import org.rekotlin.Action - -class TwinCardsReducer { - companion object { - fun reduce(action: Action, state: AppState): TwinCardsState = internalReduce(action, state) - } -} - -private fun internalReduce(action: Action, state: AppState): TwinCardsState { - if (action !is TwinCardsAction) return state.twinCardsState - - val state = state.twinCardsState - return when (action) { - is TwinCardsAction.SetResources -> { - state.copy(resources = action.resources) - } - is TwinCardsAction.SetTwinCard -> { - state.copy( - cardNumber = action.number, - secondCardId = action.secondCardId, - showTwinOnboarding = state.showTwinOnboarding, - isCreatingTwinCardsAllowed = action.isCreatingTwinCardsAllowed, - ) - } - is TwinCardsAction.CardsManager.Set -> { - state.copy(twinCardsManager = action.manager) - } - TwinCardsAction.CardsManager.Release -> { - state.copy(twinCardsManager = null) - } - is TwinCardsAction.ShowOnboarding -> { - state.copy(showTwinOnboarding = true) - } - is TwinCardsAction.SetOnboardingShown -> { - state.copy(showTwinOnboarding = false) - } - is TwinCardsAction.Wallet.Create -> { - val prevState = state.createWalletState - state.copy(createWalletState = CreateTwinWalletState( - prevState?.scanResponse, - prevState?.number ?: action.number, - prevState?.mode ?: action.createTwinWalletMode, - )) - } - TwinCardsAction.Wallet.LaunchFirstStep.Success -> { - state.copy(createWalletState = state.createWalletState?.copy( - step = CreateTwinWalletStep.SecondStep - )) - } - TwinCardsAction.Wallet.LaunchSecondStep.Success -> - state.copy(createWalletState = state.createWalletState?.copy( - step = CreateTwinWalletStep.ThirdStep - )) - else -> state - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsState.kt b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsState.kt deleted file mode 100644 index f23ac31930..0000000000 --- a/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsState.kt +++ /dev/null @@ -1,43 +0,0 @@ -package com.tangem.tap.features.twins.redux - -import com.tangem.tap.domain.tasks.product.ScanResponse -import com.tangem.tap.domain.twins.TwinCardNumber -import com.tangem.tap.domain.twins.TwinCardsManager -import org.rekotlin.StateType - -/** -[REDACTED_AUTHOR] - */ -data class TwinCardsState( - val twinCardsManager: TwinCardsManager? = null, - // if cardNumber is not set -> the scanned card is not Twin - val cardNumber: TwinCardNumber? = null, - val secondCardId: String? = null, - val showTwinOnboarding: Boolean = false, - val isCreatingTwinCardsAllowed: Boolean = false, - val createWalletState: CreateTwinWalletState? = null, - val resources: TwinCardsResources = TwinCardsResources(), -) : StateType - -data class CreateTwinWalletState( - val scanResponse: ScanResponse?, - val number: TwinCardNumber = TwinCardNumber.First, - val mode: CreateTwinWalletMode = CreateTwinWalletMode.CreateWallet, - val step: CreateTwinWalletStep = CreateTwinWalletStep.FirstStep, -) { - val showAlert: Boolean - get() = step != CreateTwinWalletStep.FirstStep -} - -enum class CreateTwinWalletStep { FirstStep, SecondStep, ThirdStep } - -enum class CreateTwinWalletMode { CreateWallet, RecreateWallet } - -data class TwinCardsResources( - val strings: Strings = Strings() -) { - //TODO: create global structure for StringRes. Set it from the MainActivity. - data class Strings( - val walletIsNotEmpty: Int = -1 - ) -} diff --git a/app/src/main/java/com/tangem/tap/features/twins/ui/CreateTwinWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/twins/ui/CreateTwinWalletFragment.kt deleted file mode 100644 index 94c5a099bc..0000000000 --- a/app/src/main/java/com/tangem/tap/features/twins/ui/CreateTwinWalletFragment.kt +++ /dev/null @@ -1,162 +0,0 @@ -package com.tangem.tap.features.twins.ui - -import android.os.Bundle -import android.view.View -import androidx.activity.OnBackPressedCallback -import androidx.core.content.ContextCompat.getColor -import androidx.fragment.app.Fragment -import androidx.transition.TransitionInflater -import com.squareup.picasso.Picasso -import com.tangem.Message -import com.tangem.tap.common.extensions.readAssetAsString -import com.tangem.tap.domain.twins.AssetReader -import com.tangem.tap.domain.twins.TwinCardNumber -import com.tangem.tap.features.twins.redux.* -import com.tangem.tap.features.wallet.redux.Artwork -import com.tangem.tap.store -import com.tangem.wallet.R -import kotlinx.android.synthetic.main.fragment_twin_cards_create.* -import org.rekotlin.StoreSubscriber - -class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create), - StoreSubscriber { - - private val assetReader: AssetReader by lazy { - object : AssetReader { - override fun readAssetAsString(name: String): String = requireContext().readAssetAsString(name) - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - store.dispatch(TwinCardsAction.Wallet.HandleOnBackPressed) - } - }) - - initResources() - val inflater = TransitionInflater.from(requireContext()) - enterTransition = inflater.inflateTransition(R.transition.slide_right) - exitTransition = inflater.inflateTransition(R.transition.fade) - } - - private fun initResources() { - val resources = TwinCardsResources( - TwinCardsResources.Strings( - R.string.details_notification_erase_wallet_not_possible - ) - ) - store.dispatch(TwinCardsAction.SetResources(resources)) - } - - override fun onStart() { - super.onStart() - store.subscribe(this) { state -> - state.skipRepeats { oldState, newState -> - oldState.twinCardsState == newState.twinCardsState - }.select { it.twinCardsState } - } - } - - override fun onStop() { - super.onStop() - store.unsubscribe(this) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - toolbar.setNavigationOnClickListener { store.dispatch(TwinCardsAction.Wallet.HandleOnBackPressed) } - - Picasso.get() - .load(Artwork.TWIN_CARD_1) - .placeholder(R.drawable.card_placeholder) - ?.error(R.drawable.card_placeholder) - ?.into(iv_card_1) - - Picasso.get() - .load(Artwork.TWIN_CARD_2) - .placeholder(R.drawable.card_placeholder) - ?.error(R.drawable.card_placeholder) - ?.into(iv_card_2) - } - - - override fun newState(state: TwinCardsState) { - if (activity == null) return - - toolbar.title = when (state.createWalletState?.mode) { - CreateTwinWalletMode.CreateWallet -> getText(R.string.wallet_button_create_wallet) - CreateTwinWalletMode.RecreateWallet, null -> getText(R.string.details_twins_recreate_toolbar) - - } - - val selectedColor = getColor(requireContext(), R.color.colorSecondary) - val defaultColor = getColor(requireContext(), R.color.blue_pale) - - val twinCardNumber = state.createWalletState?.number ?: TwinCardNumber.First - - val cardNumber = when (state.createWalletState?.step) { - CreateTwinWalletStep.FirstStep -> { - val twinCardNumberString = twinCardNumber.number.toString() - tv_step_number.text = - getString(R.string.details_twins_recreate_step_format, "1") - v_step_1.setBackgroundColor(selectedColor) - v_step_2.setBackgroundColor(defaultColor) - v_step_3.setBackgroundColor(defaultColor) - btn_tap.setOnClickListener { - store.dispatch(TwinCardsAction.Wallet.LaunchFirstStep( - Message(getString( - R.string.details_twins_recreate_title_format, - twinCardNumberString - )), - assetReader - )) - } - btn_tap.text = getString(R.string.details_twins_recreate_button_format, - twinCardNumber.number.toString()) - twinCardNumberString - } - CreateTwinWalletStep.SecondStep -> { - val twinCardNumberString = twinCardNumber.pairNumber().number.toString() - - tv_step_number.text = - getString(R.string.details_twins_recreate_step_format, "2") - v_step_1.setBackgroundColor(selectedColor) - v_step_2.setBackgroundColor(selectedColor) - v_step_3.setBackgroundColor(defaultColor) - btn_tap.setOnClickListener { - store.dispatch(TwinCardsAction.Wallet.LaunchSecondStep( - Message(getString(R.string.details_twins_recreate_title_format, twinCardNumberString)), - Message(getString(R.string.details_twins_recreate_title_preparing)), - Message(getString(R.string.details_twins_recreate_title_creating_wallet)), - )) - } - btn_tap.text = getString(R.string.details_twins_recreate_button_format, "2") - twinCardNumberString - } - CreateTwinWalletStep.ThirdStep -> { - val twinCardNumberString = twinCardNumber.number.toString() - - tv_step_number.text = - getString(R.string.details_twins_recreate_step_format, "3") - v_step_1.setBackgroundColor(selectedColor) - v_step_2.setBackgroundColor(selectedColor) - v_step_3.setBackgroundColor(selectedColor) - btn_tap.setOnClickListener { - store.dispatch(TwinCardsAction.Wallet.LaunchThirdStep( - Message(getString( - R.string.details_twins_recreate_title_format, twinCardNumberString) - ) - )) - } - btn_tap.text = getString(R.string.details_twins_recreate_button_format, twinCardNumberString) - twinCardNumberString - } - else -> null - } - btn_tap.text = getString(R.string.details_twins_recreate_button_format, cardNumber) - tv_twin_title.text = getString(R.string.details_twins_recreate_title_format, cardNumber) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/twins/ui/TwinWalletWarningFragment.kt b/app/src/main/java/com/tangem/tap/features/twins/ui/TwinWalletWarningFragment.kt deleted file mode 100644 index 18f779a400..0000000000 --- a/app/src/main/java/com/tangem/tap/features/twins/ui/TwinWalletWarningFragment.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.tangem.tap.features.twins.ui - -import android.os.Bundle -import android.view.View -import androidx.activity.OnBackPressedCallback -import androidx.fragment.app.Fragment -import androidx.transition.TransitionInflater -import com.squareup.picasso.Picasso -import com.tangem.tap.common.redux.navigation.NavigationAction -import com.tangem.tap.features.twins.redux.CreateTwinWalletMode -import com.tangem.tap.features.twins.redux.TwinCardsAction -import com.tangem.tap.features.wallet.redux.Artwork -import com.tangem.tap.store -import com.tangem.wallet.R -import kotlinx.android.synthetic.main.fragment_twin_cards_warning.* -import kotlinx.android.synthetic.main.layout_twin_cards_orange.* - -class TwinWalletWarningFragment : Fragment(R.layout.fragment_twin_cards_warning) { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - store.dispatch(NavigationAction.PopBackTo()) - } - }) - val inflater = TransitionInflater.from(requireContext()) - enterTransition = inflater.inflateTransition(R.transition.slide_right) - exitTransition = inflater.inflateTransition(R.transition.fade) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - val createTwinWallet = store.state.twinCardsState.createWalletState?.mode - if (createTwinWallet == CreateTwinWalletMode.CreateWallet) { - tv_twin_cards_description.text = getText(R.string.details_twins_recreate_subtitle) - } else if (createTwinWallet == CreateTwinWalletMode.RecreateWallet) { - tv_twin_cards_description.text = getText(R.string.details_twins_recreate_warning) - } - - btn_cancel.setOnClickListener { store.dispatch(NavigationAction.PopBackTo()) } - btn_start.setOnClickListener { store.dispatch(TwinCardsAction.ProceedToCreateWallet) } - Picasso.get() - .load(Artwork.TWIN_CARD_1) - .placeholder(R.drawable.card_placeholder) - ?.error(R.drawable.card_placeholder) - ?.into(iv_twin_card_1) - - Picasso.get() - .load(Artwork.TWIN_CARD_2) - .placeholder(R.drawable.card_placeholder) - ?.error(R.drawable.card_placeholder) - ?.into(iv_twin_card_2) - } - - -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/models/PendingTransaction.kt b/app/src/main/java/com/tangem/tap/features/wallet/models/PendingTransaction.kt index 29fe72df99..a449bb17ea 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/models/PendingTransaction.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/models/PendingTransaction.kt @@ -5,12 +5,13 @@ import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.common.TransactionStatus import com.tangem.blockchain.common.Wallet import com.tangem.tap.common.extensions.toFormattedString +import com.tangem.tap.domain.extensions.toSendableAmounts data class PendingTransaction( - val address: String?, - val amount: String?, - val currency: String, - val type: PendingTransactionType + val address: String?, + val amount: String?, + val currency: String, + val type: PendingTransactionType ) enum class PendingTransactionType { Incoming, Outgoing, Unknown } @@ -36,10 +37,10 @@ fun TransactionData.toPendingTransaction(walletAddress: String): PendingTransact } return PendingTransaction( - if (address == "unknown") null else address, - this.amount.value?.toFormattedString(amount.decimals), - this.amount.currencySymbol, - type + if (address == "unknown") null else address, + this.amount.value?.toFormattedString(amount.decimals), + this.amount.currencySymbol, + type ) } @@ -66,4 +67,8 @@ fun Wallet.getPendingTransactions(): List { fun Wallet.hasPendingTransactions(): Boolean { return getPendingTransactions().isNotEmpty() +} + +fun Wallet.hasSendableAmountsOrPendingTransactions(): Boolean { + return hasPendingTransactions() || amounts.toSendableAmounts().isNotEmpty() } \ 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 aa6094f26c..54e7f12586 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 @@ -12,7 +12,7 @@ import com.tangem.tap.common.toggleWidget.WidgetState import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.extensions.toSendableAmounts import com.tangem.tap.domain.topup.TradeCryptoHelper -import com.tangem.tap.features.twins.redux.TwinCardsState +import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState import com.tangem.tap.features.wallet.models.PendingTransaction import com.tangem.tap.features.wallet.models.toPendingTransactions import com.tangem.tap.features.wallet.models.toPendingTransactionsForToken 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 bdf79de67a..513627f250 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 @@ -19,8 +19,6 @@ import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.extensions.toSendableAmounts import com.tangem.tap.features.home.redux.HomeAction import com.tangem.tap.features.send.redux.PrepareSendScreen -import com.tangem.tap.features.twins.redux.CreateTwinWalletMode -import com.tangem.tap.features.twins.redux.TwinCardsAction import com.tangem.tap.features.wallet.redux.* import com.tangem.tap.network.NetworkStateChanged import kotlinx.coroutines.launch @@ -96,31 +94,22 @@ class WalletMiddleware { } } is WalletAction.CreateWallet -> { - if (walletState.isTangemTwins) { - store.dispatch(TwinCardsAction.Wallet.Create( - walletState.twinCardsState.cardNumber!!, - CreateTwinWalletMode.CreateWallet - )) - } else { - scope.launch { - val result = tangemSdkManager.createWallet( - globalState.scanResponse?.card?.cardId - ) - when (result) { - is CompletionResult.Success -> { - val scanNoteResponse = globalState.scanResponse?.copy(card = result.data) - scanNoteResponse?.let { - globalState.tapWalletManager.onCardScanned(scanNoteResponse) - } - } - is CompletionResult.Failure -> { - (result.error as? TangemSdkError)?.let { error -> - FirebaseAnalyticsHandler.logCardSdkError( - error, - FirebaseAnalyticsHandler.ActionToLog.CreateWallet, - card = store.state.detailsState.card - ) - } + scope.launch { + val result = tangemSdkManager.createWallet( + globalState.scanResponse?.card?.cardId + ) + when (result) { + is CompletionResult.Success -> { + val scanNoteResponse = globalState.scanResponse?.copy(card = result.data) + scanNoteResponse?.let { store.onCardScanned(scanNoteResponse) } + } + is CompletionResult.Failure -> { + (result.error as? TangemSdkError)?.let { error -> + FirebaseAnalyticsHandler.logCardSdkError( + error, + FirebaseAnalyticsHandler.ActionToLog.CreateWallet, + card = store.state.detailsState.card + ) } } } 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 8e0e9df126..ba5d6c7d56 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 @@ -38,13 +38,12 @@ private fun internalReduce(action: Action, state: AppState): WalletState { is WalletAction.ResetState -> newState = WalletState() is WalletAction.SetIfTestnetCard -> newState = newState.copy(isTestnet = action.isTestnet) is WalletAction.EmptyWallet -> { - val creatingWalletAllowed = state.twinCardsState.isCreatingTwinCardsAllowed newState = newState.copy( state = ProgressState.Done, wallets = listOf( WalletData( currencyData = BalanceWidgetData(BalanceStatus.EmptyCard), - mainButton = WalletMainButton.CreateWalletButton(creatingWalletAllowed), + mainButton = WalletMainButton.CreateWalletButton(true), ) ) ) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt index b12080f1bd..f7978e35d8 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt @@ -150,8 +150,8 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber tv_twin_card_number.show() @@ -107,11 +105,6 @@ class SingleWalletView : WalletView { if (twinCardsState?.cardNumber == null) { tv_twin_card_number.hide() } - if (twinCardsState?.showTwinOnboarding == true) { - store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingTwins)) - } - - } private fun setupButtons( diff --git a/app/src/main/res/drawable/card_placeholder.xml b/app/src/main/res/drawable/card_placeholder.xml deleted file mode 100644 index 1cfef76e78..0000000000 --- a/app/src/main/res/drawable/card_placeholder.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/card_placeholder_black.xml b/app/src/main/res/drawable/card_placeholder_black.xml new file mode 100644 index 0000000000..0fef87ee39 --- /dev/null +++ b/app/src/main/res/drawable/card_placeholder_black.xml @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/card_placeholder_white.xml b/app/src/main/res/drawable/card_placeholder_white.xml new file mode 100644 index 0000000000..d980dc5599 --- /dev/null +++ b/app/src/main/res/drawable/card_placeholder_white.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_home_back_card.xml b/app/src/main/res/drawable/ic_home_back_card.xml deleted file mode 100644 index f15c47ba3f..0000000000 --- a/app/src/main/res/drawable/ic_home_back_card.xml +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_home_front_card.xml b/app/src/main/res/drawable/ic_home_front_card.xml deleted file mode 100644 index 19969e806e..0000000000 --- a/app/src/main/res/drawable/ic_home_front_card.xml +++ /dev/null @@ -1,118 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout-h680dp/layout_onboarding_bottom_action_views.xml b/app/src/main/res/layout-h680dp/layout_onboarding_container_bottom.xml similarity index 83% rename from app/src/main/res/layout-h680dp/layout_onboarding_bottom_action_views.xml rename to app/src/main/res/layout-h680dp/layout_onboarding_container_bottom.xml index a603013d47..e635e3ee6f 100644 --- a/app/src/main/res/layout-h680dp/layout_onboarding_bottom_action_views.xml +++ b/app/src/main/res/layout-h680dp/layout_onboarding_container_bottom.xml @@ -7,6 +7,7 @@ android:clipChildren="false"> + + diff --git a/app/src/main/res/layout/fragment_twin_cards_create.xml b/app/src/main/res/layout/fragment_twin_cards_create.xml deleted file mode 100644 index 7549f32998..0000000000 --- a/app/src/main/res/layout/fragment_twin_cards_create.xml +++ /dev/null @@ -1,160 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_twin_cards_warning.xml b/app/src/main/res/layout/fragment_twin_cards_warning.xml deleted file mode 100644 index 6ca8d8f68c..0000000000 --- a/app/src/main/res/layout/fragment_twin_cards_warning.xml +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_twin_cards_welcome.xml b/app/src/main/res/layout/fragment_twin_cards_welcome.xml deleted file mode 100644 index c92e2adeb5..0000000000 --- a/app/src/main/res/layout/fragment_twin_cards_welcome.xml +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_wallet.xml b/app/src/main/res/layout/fragment_wallet.xml index 3733c23089..bc98ff9e41 100644 --- a/app/src/main/res/layout/fragment_wallet.xml +++ b/app/src/main/res/layout/fragment_wallet.xml @@ -58,7 +58,7 @@ android:elevation="3dp" android:maxHeight="215dp" android:scaleType="fitCenter" - android:src="@drawable/card_placeholder" + android:src="@drawable/card_placeholder_black" app:layout_constraintTop_toTopOf="parent" /> + + + + + + + + + + + + - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/layout_twin_cards_orange.xml b/app/src/main/res/layout/layout_twin_cards_orange.xml deleted file mode 100644 index f524c5dd49..0000000000 --- a/app/src/main/res/layout/layout_twin_cards_orange.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/lp_onboarding_create_wallet.xml b/app/src/main/res/layout/lp_onboarding_create_wallet.xml index fa4505f353..3c5cd91655 100644 --- a/app/src/main/res/layout/lp_onboarding_create_wallet.xml +++ b/app/src/main/res/layout/lp_onboarding_create_wallet.xml @@ -6,6 +6,11 @@ android:layout_height="match_parent" android:visibility="visible"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/lp_onboarding_topup_wallet.xml b/app/src/main/res/layout/lp_onboarding_topup_wallet.xml index 65705f3fce..6b924ea771 100644 --- a/app/src/main/res/layout/lp_onboarding_topup_wallet.xml +++ b/app/src/main/res/layout/lp_onboarding_topup_wallet.xml @@ -6,6 +6,11 @@ android:layout_height="match_parent" android:visibility="visible"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/test_leapfrog_widget.xml b/app/src/main/res/layout/test_leapfrog_widget.xml index 3fbf992180..c4fddb3183 100644 --- a/app/src/main/res/layout/test_leapfrog_widget.xml +++ b/app/src/main/res/layout/test_leapfrog_widget.xml @@ -11,13 +11,12 @@ + android:src="@drawable/card_placeholder_black" + android:tint="@color/colorPrimary" /> + android:src="@drawable/card_placeholder_black" /> \ No newline at end of file diff --git a/app/src/main/res/layout/view_bg_twins_welcome.xml b/app/src/main/res/layout/view_bg_twins_welcome.xml new file mode 100644 index 0000000000..5202b1851b --- /dev/null +++ b/app/src/main/res/layout/view_bg_twins_welcome.xml @@ -0,0 +1,46 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_onboarding_progress.xml b/app/src/main/res/layout/view_onboarding_progress.xml index 415f8bd19c..8b2e151d1a 100644 --- a/app/src/main/res/layout/view_onboarding_progress.xml +++ b/app/src/main/res/layout/view_onboarding_progress.xml @@ -1,10 +1,13 @@ - + + + + diff --git a/app/src/main/res/layout/view_onboarding_tv_balance.xml b/app/src/main/res/layout/view_onboarding_tv_balance.xml index 5dd54d7067..6577933837 100644 --- a/app/src/main/res/layout/view_onboarding_tv_balance.xml +++ b/app/src/main/res/layout/view_onboarding_tv_balance.xml @@ -12,6 +12,7 @@ android:id="@+id/tv_balance" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:elevation="0dp" android:fontFamily="sans-serif-medium" android:text="@string/onboarding_balance_title" android:textAllCaps="true" diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml index 17edb1b769..0d8661c471 100644 --- a/app/src/main/res/values/strings_untranslated.xml +++ b/app/src/main/res/values/strings_untranslated.xml @@ -14,25 +14,22 @@ Tangem Twin One wallet. Two cards. - This one that you are holding in your hands -and the other one with number %s.\n\nBoth cards can be used to extract funds from -this wallet. - + This one that you are holding in your handsand the other one with number #%s. Card %s of 2 Re-create twin wallet - This action is irreversible. You will not have access to the old wallet.\nThe wallet re-creation procedure consists of three steps. You must complete it to the end, otherwise you will have to start from the beginning. - Re-create wallet - Step %s - Tap the #%s twin card - 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 - 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. Card terms of use + Tangem Twin + This action is irreversible. You will not have access to the old wallet. + Tap the #%s twin card + 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 + 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. + The twin address was successfully created //Common @@ -188,11 +185,13 @@ this wallet. Show the wallet’s address Scan the address to top up your wallet - Done! + Success! Your crypto card is activated and ready to be used Continue Balance + If the process of re-creating the wallet gets interrupted in any way, you’ll have to start over. + Internal error: can\'t create wallet manager diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index f008a00608..fe7f02a527 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -5,8 +5,7 @@ @color/colorPrimary @color/colorSecondary @color/darkGray6 - @color/backgroundLightGray - false + true true true @null From 9febfba3218717f6c149ce36b96c8795288f7204 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 28 Oct 2021 00:24:35 +0300 Subject: [PATCH 095/189] Updated on 2026-08-14 --- .../tangem/tap/common/redux/navigation/NavigationReducer.kt | 3 +-- app/src/main/java/com/tangem/tap/features/BaseFragment.kt | 5 ----- .../onboarding/products/note/OnboardingNoteFragment.kt | 2 ++ .../products/otherCards/OnboardingOtherCardsFragment.kt | 2 ++ .../onboarding/products/twins/ui/TwinsCardsFragment.kt | 2 ++ .../java/com/tangem/tap/features/send/ui/SendFragment.kt | 2 ++ 6 files changed, 9 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationReducer.kt index 80cd5e3260..e742aa2e6b 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationReducer.kt @@ -20,8 +20,7 @@ private fun internalReduce(action: Action, state: AppState): NavigationState { navState.copy(backStack = navState.backStack + navigationAction.screen) } is NavigationAction.PopBackTo -> { - val screen = - navigationAction.screen ?: navState.activity?.get()?.getPreviousScreen() + val screen = navigationAction.screen ?: navState.activity?.get()?.getPreviousScreen() val index = navState.backStack.lastIndexOf(screen) + 1 state.navigationState.copy(backStack = navState.backStack.subList(0, index)) } diff --git a/app/src/main/java/com/tangem/tap/features/BaseFragment.kt b/app/src/main/java/com/tangem/tap/features/BaseFragment.kt index 8e1c2e098a..8b4d0d3f70 100644 --- a/app/src/main/java/com/tangem/tap/features/BaseFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/BaseFragment.kt @@ -38,11 +38,6 @@ abstract class BaseFragment(layoutId: Int) : Fragment(layoutId), FragmentOnBackP return mainView } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - addBackPressHandler(this) - } - override fun handleOnBackPressed() { store.dispatch(NavigationAction.PopBackTo()) } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt index 80b671393f..94eebeac22 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt @@ -19,6 +19,7 @@ import com.tangem.tap.common.postUi import com.tangem.tap.common.redux.navigation.ShareElement import com.tangem.tap.common.toggleWidget.RefreshBalanceWidget import com.tangem.tap.common.transitions.InternalNoteLayoutTransition +import com.tangem.tap.features.addBackPressHandler import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteAction import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteState @@ -49,6 +50,7 @@ class OnboardingNoteFragment : BaseOnboardingFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + addBackPressHandler(this) imv_front_card.transitionName = ShareElement.imvFrontCard startPostponedEnterTransition() diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt index f57e92fe03..cdd2871e00 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt @@ -10,6 +10,7 @@ import androidx.transition.TransitionManager import com.tangem.tap.common.extensions.getDrawableCompat import com.tangem.tap.common.redux.navigation.ShareElement import com.tangem.tap.common.transitions.InternalNoteLayoutTransition +import com.tangem.tap.features.addBackPressHandler import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment import com.tangem.tap.features.onboarding.products.note.swapToBitmapDrawable import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsAction @@ -36,6 +37,7 @@ class OnboardingOtherCardsFragment : BaseOnboardingFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + addBackPressHandler(this) reconfigureLayoutForTwins() twinsWidget = TwinsCardWidget(LeapfrogWidget(cards_container)) { 250f } diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt index 4c35c25b24..4d9528605a 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt @@ -25,6 +25,7 @@ import com.tangem.tap.common.snackBar.MaxAmountSnackbar import com.tangem.tap.common.text.truncateMiddleWith import com.tangem.tap.common.toggleWidget.* import com.tangem.tap.features.BaseStoreFragment +import com.tangem.tap.features.addBackPressHandler import com.tangem.tap.features.send.redux.* import com.tangem.tap.features.send.redux.AddressPayIdActionUi.* import com.tangem.tap.features.send.redux.AmountActionUi.* @@ -64,6 +65,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + addBackPressHandler(this) etAmountToSend = view.findViewById(R.id.etAmountToSend) From d5cfa5a8bb75f819995efb3811a0b8b4ff6df177 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 28 Oct 2021 01:51:17 +0300 Subject: [PATCH 096/189] Updated on 2026-08-14 --- .../tap/features/onboarding/OnboardingManager.kt | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 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 2b4894d5b9..984f512d4f 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 @@ -55,7 +55,7 @@ class OnboardingManager( } suspend fun updateBalance(walletManager: WalletManager): OnboardingWalletBalance { - return when (val result = walletManager.safeUpdate()) { + val balance = when (val result = walletManager.safeUpdate()) { is Result.Success -> { val wallet = walletManager.wallet val valueOfAmount = wallet.amounts[AmountType.Coin]?.value @@ -63,11 +63,10 @@ class OnboardingManager( val customError = TapError.CustomError("Amount is NULL") OnboardingWalletBalance.criticalError(customError) } else { - val currency = Currency.Blockchain(walletManager.wallet.blockchain) val balance = if (valueOfAmount.isZero()) { - OnboardingWalletBalance.done(valueOfAmount, wallet.hasPendingTransactions(), currency) + OnboardingWalletBalance.done(valueOfAmount, wallet.hasPendingTransactions()) } else { - OnboardingWalletBalance.done(valueOfAmount, false, currency) + OnboardingWalletBalance.done(valueOfAmount, false) } balance } @@ -81,6 +80,8 @@ class OnboardingManager( } } } + + return balance.copy(currency = Currency.Blockchain(walletManager.wallet.blockchain)) } fun activationStarted(cardId: String) { @@ -122,7 +123,10 @@ data class OnboardingWalletBalance( state = ProgressState.Loading ) - fun done(value: BigDecimal, hasTransactions: Boolean, currency: Currency.Blockchain): OnboardingWalletBalance = - OnboardingWalletBalance(value, currency, hasTransactions, ProgressState.Done) + fun done(value: BigDecimal, hasTransactions: Boolean): OnboardingWalletBalance = OnboardingWalletBalance( + value, + hasIncomingTransaction = hasTransactions, + state = ProgressState.Done + ) } } \ No newline at end of file From b96179be53643e5ebdb066699e66885471ccdac5 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 28 Oct 2021 01:52:07 +0300 Subject: [PATCH 097/189] Updated on 2026-08-14 --- .../toggleWidget/RefreshBalanceWidget.kt | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/common/toggleWidget/RefreshBalanceWidget.kt b/app/src/main/java/com/tangem/tap/common/toggleWidget/RefreshBalanceWidget.kt index e7ef08e026..277f839c05 100644 --- a/app/src/main/java/com/tangem/tap/common/toggleWidget/RefreshBalanceWidget.kt +++ b/app/src/main/java/com/tangem/tap/common/toggleWidget/RefreshBalanceWidget.kt @@ -41,30 +41,31 @@ class RefreshBalanceWidget( val progressState = state as? ProgressState ?: return val currentStateByView = getState() - - if (progressState == currentStateByView) return + when { + currentStateByView == progressState -> return + currentStateByView == ProgressState.Done -> if (progressState == ProgressState.Error) return + } animateState(progressState) viewSwitcher.showNext() - } private fun animateState(state: ProgressState) { when (state) { ProgressState.Done, ProgressState.Error -> { progressViewAnimation?.cancel() + progressViewAnimation = null } ProgressState.Loading -> { - val animation = RotateAnimation( - 0f, 360f, - Animation.RELATIVE_TO_SELF, 0.5f, - Animation.RELATIVE_TO_SELF, 0.5f + progressViewAnimation = RotateAnimation( + 0f, 360f, + Animation.RELATIVE_TO_SELF, 0.5f, + Animation.RELATIVE_TO_SELF, 0.5f ) - animation.duration = 700 - animation.interpolator = AccelerateInterpolator() - animation.repeatCount = -1 - arrowView.startAnimation(animation) - progressViewAnimation = animation + progressViewAnimation?.duration = 700 + progressViewAnimation?.interpolator = AccelerateInterpolator() + progressViewAnimation?.repeatCount = -1 + progressViewAnimation?.let { arrowView.startAnimation(it) } } } } @@ -88,10 +89,10 @@ class RefreshBalanceWidget( class ShowAnimation : AnimationSet(true) { init { addAnimation(ScaleAnimation( - 0f, 1f, - 0f, 1f, - Animation.RELATIVE_TO_SELF, 0.5f, - Animation.RELATIVE_TO_SELF, 0.5f) + 0f, 1f, + 0f, 1f, + Animation.RELATIVE_TO_SELF, 0.5f, + Animation.RELATIVE_TO_SELF, 0.5f) ) addAnimation(AlphaAnimation(0f, 1f)) duration = 300 From f94f78eb487a498aacdb20b9b5142e6f679b9751 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 28 Oct 2021 01:53:25 +0300 Subject: [PATCH 098/189] Updated on 2026-08-14 --- .../tap/domain/extensions/Blockchain.kt | 6 +++--- .../products/note/OnboardingNoteFragment.kt | 19 +++++++++++++------ .../note/redux/OnboardingNoteAction.kt | 2 ++ .../note/redux/OnboardingNoteMiddleware.kt | 6 ++++-- .../note/redux/OnboardingNoteReducer.kt | 3 +++ .../note/redux/OnboardingNoteState.kt | 2 ++ .../main/res/values/strings_untranslated.xml | 1 + 7 files changed, 28 insertions(+), 11 deletions(-) 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 d658847574..4be4dda58f 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 @@ -13,10 +13,10 @@ fun Blockchain.isNoAccountError(exception: Throwable): Boolean { } } -fun Blockchain.amountToCreateAccount(token: Token? = null): Double? { +fun Blockchain.amountToCreateAccount(token: Token? = null): BigDecimal? { return when (this) { - Blockchain.Stellar -> if (token?.symbol == NODL) 1.5 else 1.toDouble() - Blockchain.XRP -> 10.toDouble() + Blockchain.Stellar -> if (token?.symbol == NODL) BigDecimal(1.5) else BigDecimal.ZERO + Blockchain.XRP -> BigDecimal(10) else -> null } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt index 94eebeac22..b59e7ea667 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt @@ -15,7 +15,6 @@ import com.tangem.tangem_sdk_new.extensions.fadeIn import com.tangem.tangem_sdk_new.extensions.fadeOut import com.tangem.tap.common.extensions.getDrawableCompat import com.tangem.tap.common.extensions.stripZeroPlainString -import com.tangem.tap.common.postUi import com.tangem.tap.common.redux.navigation.ShareElement import com.tangem.tap.common.toggleWidget.RefreshBalanceWidget import com.tangem.tap.common.transitions.InternalNoteLayoutTransition @@ -137,14 +136,22 @@ class OnboardingNoteFragment : BaseOnboardingFragment() { } tv_header.setText(R.string.onboarding_top_up_header) - tv_body.setText(R.string.onboarding_top_up_body) + if (state.balanceNonCriticalError == null) { + tv_body.setText(R.string.onboarding_top_up_body) + } else { + state.walletBalance.amountToCreateAccount?.let { amount -> + val tvBodyMessage = getString( + R.string.onboarding_top_up_body_no_account_error, + amount, state.walletBalance.currency.currencySymbol + ) + tv_body.text = tvBodyMessage + } + } btnRefreshBalanceWidget.changeState(state.walletBalance.state) if (btnRefreshBalanceWidget.isShowing != true) { - postUi(300) { - btnRefreshBalanceWidget.mainView.setOnClickListener { - store.dispatch(OnboardingNoteAction.Balance.Update) - } + btnRefreshBalanceWidget.mainView.setOnClickListener { + store.dispatch(OnboardingNoteAction.Balance.Update) } } imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_rectangle_rounded_8)) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt index 73b588ee69..3a370e6c72 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt @@ -1,6 +1,7 @@ package com.tangem.tap.features.onboarding.products.note.redux import com.tangem.blockchain.common.WalletManager +import com.tangem.tap.domain.TapError import com.tangem.tap.features.onboarding.OnboardingWalletBalance import com.tangem.tap.features.wallet.redux.Artwork import org.rekotlin.Action @@ -26,6 +27,7 @@ sealed class OnboardingNoteAction : Action { sealed class Balance { object Update : OnboardingNoteAction() data class Set(val balance: OnboardingWalletBalance) : OnboardingNoteAction() + data class SetNonCriticalError(val error: TapError?) : OnboardingNoteAction() } sealed class Confetti { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt index 0ef12f9c8f..125ba49fff 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt @@ -119,9 +119,11 @@ private fun handleNoteAction(action: Action, dispatch: DispatchFunction) { val loadedBalance = onboardingManager.updateBalance(walletManager) loadedBalance.criticalError?.let { store.dispatchErrorNotification(it) } delay(if (isLoadedBefore) 0 else 300) - withMainContext { store.dispatch(OnboardingNoteAction.Balance.Set(loadedBalance)) } + withMainContext { + store.dispatch(OnboardingNoteAction.Balance.Set(loadedBalance)) + store.dispatch(OnboardingNoteAction.Balance.SetNonCriticalError(loadedBalance.error)) + } } - } is OnboardingNoteAction.Balance.Set -> { if (action.balance.balanceIsToppedUp()) { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt index 206a4893ab..260f41fc20 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt @@ -26,6 +26,9 @@ private fun internalReduce(action: Action, appState: AppState): OnboardingNoteSt is OnboardingNoteAction.Balance.Set -> { state = state.copy(walletBalance = action.balance) } + is OnboardingNoteAction.Balance.SetNonCriticalError -> { + state = state.copy(balanceNonCriticalError = action.error) + } is OnboardingNoteAction.SetStepOfScreen -> { if (action.step != state.currentStep && state.steps.contains(action.step)) { state = state.copy(currentStep = action.step) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt index 23def7368f..3cf07bea6b 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt @@ -1,6 +1,7 @@ package com.tangem.tap.features.onboarding.products.note.redux import com.tangem.blockchain.common.WalletManager +import com.tangem.tap.domain.TapError import com.tangem.tap.features.onboarding.OnboardingWalletBalance import com.tangem.tap.features.wallet.redux.Artwork import org.rekotlin.StateType @@ -13,6 +14,7 @@ data class OnboardingNoteState( // UI val cardArtwork: Artwork? = null, val walletBalance: OnboardingWalletBalance = OnboardingWalletBalance.loading(), + val balanceNonCriticalError: TapError? = null, val currentStep: OnboardingNoteStep = OnboardingNoteStep.None, val steps: List = OnboardingNoteStep.values().toList(), val showConfetti: Boolean = false, diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml index 0d8661c471..cb9cdc7224 100644 --- a/app/src/main/res/values/strings_untranslated.xml +++ b/app/src/main/res/values/strings_untranslated.xml @@ -181,6 +181,7 @@ Top up your wallet To get started, simply top up the card with any amount + To get started, simply top up the wallet with more than %s %s. Buy crypto Show the wallet’s address Scan the address to top up your wallet From 4bf16417fb988c6d4c1fff250db2ce808a3c021f Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 28 Oct 2021 01:54:15 +0300 Subject: [PATCH 099/189] Updated on 2026-08-14 --- .../tap/domain/twins/TwinCardsManager.kt | 13 +++-------- .../twins/redux/TwinCardsMiddleware.kt | 22 +++---------------- 2 files changed, 6 insertions(+), 29 deletions(-) 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 e16ccd79cb..861f3db801 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 @@ -26,14 +26,9 @@ class TwinCardsManager(private val card: Card, assetReader: AssetReader) { private val issuerKeyPair: KeyPair = getIssuerKeys(assetReader, card.issuer.publicKey.toHexString()) suspend fun createFirstWallet(message: Message): CompletionResult { - val response = tangemSdkManager.runTaskAsync( - CreateFirstTwinWalletTask(), currentCardId, message - ) + val response = tangemSdkManager.runTaskAsync(CreateFirstTwinWalletTask(), currentCardId, message) when (response) { - is CompletionResult.Success -> { - currentCardPublicKey = response.data.wallet.publicKey.toHexString() - return response - } + is CompletionResult.Success -> currentCardPublicKey = response.data.wallet.publicKey.toHexString() is CompletionResult.Failure -> { (response.error as? TangemSdkError)?.let { error -> FirebaseAnalyticsHandler.logCardSdkError( @@ -42,13 +37,11 @@ class TwinCardsManager(private val card: Card, assetReader: AssetReader) { card = card ) } - return response } } - + return response } - suspend fun createSecondWallet( initialMessage: Message, preparingMessage: Message, 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 f56502e209..26fbfb104b 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 @@ -72,7 +72,6 @@ private fun handle(action: Action, dispatch: DispatchFunction) { if (twinCardsState.mode == CreateTwinWalletMode.CreateWallet) { onboardingManager?.activationFinished(getScanResponse().card.cardId) twinCardsState.pairCardId?.let { onboardingManager?.activationFinished(it) } - } } @@ -121,23 +120,9 @@ private fun handle(action: Action, dispatch: DispatchFunction) { } is TwinCardsAction.SetStepOfScreen -> { when (action.step) { - TwinCardsStep.WelcomeOnly -> { - preferencesStorage.saveTwinsOnboardingShown() - } - TwinCardsStep.Welcome -> { - preferencesStorage.saveTwinsOnboardingShown() - } - TwinCardsStep.Warning -> { - } - TwinCardsStep.CreateFirstWallet -> { - } - TwinCardsStep.CreateSecondWallet -> { - } - TwinCardsStep.CreateThirdWallet -> { - } - TwinCardsStep.TopUpWallet -> { - store.dispatch(TwinCardsAction.Balance.Update) - } + TwinCardsStep.WelcomeOnly -> preferencesStorage.saveTwinsOnboardingShown() + TwinCardsStep.Welcome -> preferencesStorage.saveTwinsOnboardingShown() + TwinCardsStep.TopUpWallet -> store.dispatch(TwinCardsAction.Balance.Update) TwinCardsStep.Done -> { finishCardActivation() postUi(500) { store.dispatch(TwinCardsAction.Confetti.Show) } @@ -200,7 +185,6 @@ private fun handle(action: Action, dispatch: DispatchFunction) { } } } - is TwinCardsAction.Balance.Update -> { val walletManager = if (twinCardsState.walletManager != null) { twinCardsState.walletManager From 2f7c0447fc83891d458fc989231a2644b12c6aad Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 28 Oct 2021 02:17:42 +0300 Subject: [PATCH 100/189] Updated on 2026-08-14 --- app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt index 01ef833c3d..d734698fd5 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt @@ -58,7 +58,7 @@ object TapWorkarounds { "AB02" to Blockchain.Ethereum, "AB03" to Blockchain.CardanoShelley, "AB04" to Blockchain.Dogecoin, - "AB05" to Blockchain.Binance, + "AB05" to Blockchain.BSC, "AB06" to Blockchain.XRP, ) } From 5bf70bba4e2bdbf36ee82611872e3aa3dbbdd582 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 28 Oct 2021 14:23:39 +0300 Subject: [PATCH 101/189] Updated on 2026-08-14 --- .../onboarding/products/twins/redux/TwinCardsReducer.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsReducer.kt index 1d18eb4dec..153f91e796 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsReducer.kt @@ -29,7 +29,10 @@ private fun internalReduce(action: Action, state: AppState): TwinCardsState { } } is TwinCardsAction.SetMode -> { - state = state.copy(mode = action.mode) + state = state.copy( + mode = action.mode, + userWasUnderstandIfWalletRecreate = false, + ) } is TwinCardsAction.SetStepOfScreen -> { state = state.copy(currentStep = action.step) From dc22b9fc2c6fdb02a5445823ab7366e048d1b080 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 28 Oct 2021 14:28:08 +0300 Subject: [PATCH 102/189] Updated on 2026-08-14 --- .../features/onboarding/products/twins/ui/TwinsCardsFragment.kt | 1 + 1 file changed, 1 insertion(+) 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 dee8230f9f..e46bae4aaf 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 @@ -287,6 +287,7 @@ class TwinsCardsFragment : BaseOnboardingFragment() { store.dispatch(TwinCardsAction.TopUp) } + btn_alternative_action.isVisible = true btn_alternative_action.setText(R.string.onboarding_top_up_button_show_wallet_address) btn_alternative_action.setOnClickListener { store.dispatch(TwinCardsAction.ShowAddressInfoDialog) From 0207333df16bcb97512bf148e5416203a19d555f Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 28 Oct 2021 14:28:28 +0300 Subject: [PATCH 103/189] Updated on 2026-08-14 --- app/src/main/res/layout/view_onboarding_refresh_balance.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/res/layout/view_onboarding_refresh_balance.xml b/app/src/main/res/layout/view_onboarding_refresh_balance.xml index c9bf3aacb2..88daba2ce2 100644 --- a/app/src/main/res/layout/view_onboarding_refresh_balance.xml +++ b/app/src/main/res/layout/view_onboarding_refresh_balance.xml @@ -4,7 +4,6 @@ android:id="@+id/btn_refresh_balance" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:background="?attr/selectableItemBackgroundBorderless" android:clickable="true" android:focusable="true"> From c8b2424a1f16cb7927e80eb315f1d00d9aac40ec Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 28 Oct 2021 15:07:37 +0300 Subject: [PATCH 104/189] Updated on 2026-08-14 --- .../onboarding/products/twins/ui/TwinsCardsFragment.kt | 2 +- app/src/main/res/layout/view_onboarding_tv_balance.xml | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) 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 e46bae4aaf..05c71f8c96 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 @@ -77,7 +77,7 @@ class TwinsCardsFragment : BaseOnboardingFragment() { addBackPressHandler(this) reconfigureLayoutForTwins() - twinsWidget = TwinsCardWidget(LeapfrogWidget(cards_container)) { 250f } + twinsWidget = TwinsCardWidget(LeapfrogWidget(cards_container)) { 285f } btnRefreshBalanceWidget = RefreshBalanceWidget(onboarding_main_container) toolbar.title = getText(R.string.twins_recreate_toolbar) diff --git a/app/src/main/res/layout/view_onboarding_tv_balance.xml b/app/src/main/res/layout/view_onboarding_tv_balance.xml index 6577933837..5e9f7e3c35 100644 --- a/app/src/main/res/layout/view_onboarding_tv_balance.xml +++ b/app/src/main/res/layout/view_onboarding_tv_balance.xml @@ -19,6 +19,10 @@ android:textColor="#ABABAB" android:textSize="14sp" /> + + Date: Thu, 28 Oct 2021 15:08:03 +0300 Subject: [PATCH 105/189] Updated on 2026-08-14 --- app/src/main/res/layout/view_onboarding_progress.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/layout/view_onboarding_progress.xml b/app/src/main/res/layout/view_onboarding_progress.xml index 8b2e151d1a..6dbac9141f 100644 --- a/app/src/main/res/layout/view_onboarding_progress.xml +++ b/app/src/main/res/layout/view_onboarding_progress.xml @@ -6,8 +6,8 @@ style="@style/Widget.AppCompat.ProgressBar.Horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginStart="32dp" - android:layout_marginTop="16dp" - android:layout_marginEnd="32dp" + android:layout_marginStart="16dp" + android:layout_marginEnd="16dp" + android:progressBackgroundTint="#401ACE80" android:progressTint="@color/accent" /> From f7c05ae07a0d62a9bb8578f4b5ee4432cf10a378 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 28 Oct 2021 15:12:49 +0300 Subject: [PATCH 106/189] Updated on 2026-08-14 --- app/src/main/res/values/strings_untranslated.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml index cb9cdc7224..d1ac99cf9c 100644 --- a/app/src/main/res/values/strings_untranslated.xml +++ b/app/src/main/res/values/strings_untranslated.xml @@ -14,7 +14,7 @@ Tangem Twin One wallet. Two cards. - This one that you are holding in your handsand the other one with number #%s. + This one that you are holding in your hands and the other one with number #%s. Card %s of 2 From d78328de1c8d1471b9df428d22ecb0536cc14c62 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 28 Oct 2021 16:10:22 +0300 Subject: [PATCH 107/189] Updated on 2026-08-14 --- .../res/drawable/shape_circle_home_screen.xml | 14 ++++++++ .../res/layout/layout_onboarding_home_top.xml | 5 ++- app/src/main/res/layout/view_bg_home.xml | 32 +++++++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 app/src/main/res/drawable/shape_circle_home_screen.xml create mode 100644 app/src/main/res/layout/view_bg_home.xml diff --git a/app/src/main/res/drawable/shape_circle_home_screen.xml b/app/src/main/res/drawable/shape_circle_home_screen.xml new file mode 100644 index 0000000000..6cc2f8cc4e --- /dev/null +++ b/app/src/main/res/drawable/shape_circle_home_screen.xml @@ -0,0 +1,14 @@ + + + + + + + diff --git a/app/src/main/res/layout/layout_onboarding_home_top.xml b/app/src/main/res/layout/layout_onboarding_home_top.xml index c038dc2463..fe45273f14 100644 --- a/app/src/main/res/layout/layout_onboarding_home_top.xml +++ b/app/src/main/res/layout/layout_onboarding_home_top.xml @@ -3,7 +3,10 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/onboarding_main_container" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:clipChildren="false"> + + + + + + + + + + + \ No newline at end of file From 356c59fc9cf545481a6e941af73c727871db65b7 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 28 Oct 2021 16:22:11 +0300 Subject: [PATCH 108/189] Updated on 2026-08-14 --- app/src/main/res/layout/dialog_onboarding_address_info.xml | 5 ++++- app/src/main/res/layout/view_onboarding_tv_balance.xml | 2 ++ app/src/main/res/values/styles.xml | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/layout/dialog_onboarding_address_info.xml b/app/src/main/res/layout/dialog_onboarding_address_info.xml index 92c780e969..a3c34a8f06 100644 --- a/app/src/main/res/layout/dialog_onboarding_address_info.xml +++ b/app/src/main/res/layout/dialog_onboarding_address_info.xml @@ -1,6 +1,7 @@ @@ -68,10 +69,12 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:ellipsize="middle" + android:fontFamily="sans-serif-medium" android:maxWidth="170dp" android:minWidth="120dp" android:singleLine="true" - android:text="addlkasLNXDLKNnkjkjhnjkjnljnnlsenn" /> + android:textSize="15sp" + tools:text="addlkasLNXDLKNnkjkjhnjkjnljnnlsenn" /> diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index fe7f02a527..b506eaed20 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -90,12 +90,14 @@ From 0a4e9e2d83b7c0d68c2e8db379f28a867d8fbb24 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 28 Oct 2021 16:40:08 +0300 Subject: [PATCH 109/189] Updated on 2026-08-14 --- .../products/note/redux/OnboardingNoteMiddleware.kt | 5 +++-- .../otherCards/redux/OnboardingOtherCardsMiddleware.kt | 4 ++-- .../features/wallet/redux/middlewares/WalletMiddleware.kt | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt index 125ba49fff..b182753729 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt @@ -9,6 +9,7 @@ import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction +import com.tangem.tap.domain.DELAY_SDK_DIALOG_CLOSE import com.tangem.tap.domain.TapError import com.tangem.tap.domain.extensions.hasWallets import com.tangem.tap.domain.extensions.makePrimaryWalletManager @@ -69,7 +70,7 @@ private fun handleNoteAction(action: Action, dispatch: DispatchFunction) { } OnboardingNoteStep.Done -> { onboardingManager.activationFinished(card.cardId) - postUi(500) { store.dispatch(OnboardingNoteAction.Confetti.Show) } + postUi(DELAY_SDK_DIALOG_CLOSE) { store.dispatch(OnboardingNoteAction.Confetti.Show) } } } } @@ -148,7 +149,7 @@ private fun handleNoteAction(action: Action, dispatch: DispatchFunction) { store.dispatch(GlobalAction.Onboarding.Stop) scope.launch { store.onCardScanned(scanResponse) - store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) + withMainContext { store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) } } } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt index fd2dbee87d..4a6f973e1f 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt @@ -58,7 +58,7 @@ private fun handleOtherCardsAction(action: Action, dispatch: DispatchFunction) { is OnboardingOtherCardsAction.SetStepOfScreen -> { if (action.step == OnboardingOtherCardsStep.Done) { onboardingManager.activationFinished(card.cardId) - postUi(500) { store.dispatch(OnboardingOtherCardsAction.Confetti.Show) } + postUi(200) { store.dispatch(OnboardingOtherCardsAction.Confetti.Show) } } } is OnboardingOtherCardsAction.CreateWallet -> { @@ -85,7 +85,7 @@ private fun handleOtherCardsAction(action: Action, dispatch: DispatchFunction) { store.dispatch(GlobalAction.Onboarding.Stop) scope.launch { store.onCardScanned(onboardingManager.scanResponse) - store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) + withMainContext { store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) } } } } 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 513627f250..9ec6e10f13 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 @@ -147,7 +147,7 @@ class WalletMiddleware { WalletAction.LoadArtwork(action.card, response.data.artwork?.id), GlobalAction.SetIfCardVerifiedOnline(response.data.passed) ) - actionList.forEach { store.dispatch(it) } + withMainContext { actionList.forEach { store.dispatch(it) } } } is Result.Failure -> { store.dispatchOnMain(GlobalAction.SetIfCardVerifiedOnline(false)) From 206016549c64621692aa3e3c0c42538e301f5ce3 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 28 Oct 2021 18:45:20 +0300 Subject: [PATCH 110/189] Updated on 2026-08-14 --- .../tangem/tap/common/extensions/WalletManager.kt | 2 +- .../main/java/com/tangem/tap/domain/TapErrors.kt | 2 ++ .../tap/features/onboarding/OnboardingManager.kt | 6 +++++- .../products/note/OnboardingNoteFragment.kt | 10 +++++++--- .../products/note/redux/OnboardingNoteAction.kt | 1 + .../note/redux/OnboardingNoteMiddleware.kt | 3 ++- .../products/note/redux/OnboardingNoteReducer.kt | 3 +++ .../products/note/redux/OnboardingNoteState.kt | 1 + .../products/twins/redux/TwinCardsAction.kt | 3 +++ .../products/twins/redux/TwinCardsMiddleware.kt | 6 +++++- .../products/twins/redux/TwinCardsReducer.kt | 6 ++++++ .../products/twins/redux/TwinCardsState.kt | 3 +++ .../products/twins/ui/TwinsCardsFragment.kt | 15 +++++++-------- app/src/main/res/values/strings_untranslated.xml | 2 ++ 14 files changed, 48 insertions(+), 15 deletions(-) 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 8f7402ff1f..70a4fcc286 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 @@ -29,7 +29,7 @@ suspend fun WalletManager.safeUpdate(): Result = try { if (blockchain.isNoAccountError(exception) && amountToCreateAccount != null) { Result.Failure(TapError.WalletManagerUpdate.NoAccountError(amountToCreateAccount.toString())) } else { - val message = exception.localizedMessage ?: "Unknown exception during WalletManager update" + val message = exception.localizedMessage ?: "An error has occurred. Try later" Result.Failure(TapError.WalletManagerUpdate.InternalError(message)) } } diff --git a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt index c84e3f0d29..f4b680ec9f 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt @@ -46,6 +46,8 @@ sealed class TapError( sealed class WalletManagerUpdate { class NoAccountError(amountToCreateAccount: String): CustomError(amountToCreateAccount) class InternalError(message: String): CustomError(message) + object BlockchainIsUnreachable: TapError(R.string.wallet_balance_blockchain_unreachable) + object BlockchainIsUnreachableTryLater: TapError(R.string.wallet_balance_blockchain_unreachable_try_later) } data class ValidateTransactionErrors( 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 984f512d4f..60d3cc323f 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 @@ -20,6 +20,7 @@ import com.tangem.tap.features.wallet.redux.ProgressState import com.tangem.tap.persistence.UsedCardsPrefStorage import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import timber.log.Timber import java.math.BigDecimal import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine @@ -76,7 +77,10 @@ class OnboardingManager( when (error) { is TapError.WalletManagerUpdate.NoAccountError -> OnboardingWalletBalance.error(error) // NoInternetConnection, WalletManagerUpdate.InternalError - else -> OnboardingWalletBalance.criticalError(error) + else -> { + Timber.e(error.localizedMessage) + OnboardingWalletBalance.criticalError(TapError.WalletManagerUpdate.BlockchainIsUnreachableTryLater) + } } } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt index b59e7ea667..96086474e5 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt @@ -91,9 +91,13 @@ class OnboardingNoteFragment : BaseOnboardingFragment() { private fun setBalance(state: OnboardingNoteState) { if (state.walletBalance.currency.blockchain == Blockchain.Unknown) return - val balanceValue = state.walletBalance.value.stripZeroPlainString() - val currency = state.walletBalance.currency.currencySymbol - tv_balance_value.text = "$balanceValue $currency" + if (state.balanceCriticalError == null) { + val balanceValue = state.walletBalance.value.stripZeroPlainString() + val currency = state.walletBalance.currency.currencySymbol + tv_balance_value.text = "$balanceValue $currency" + } else { + tv_balance_value.text = "–" + } } private fun setupNoneState(state: OnboardingNoteState) { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt index 3a370e6c72..cce3d41924 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt @@ -27,6 +27,7 @@ sealed class OnboardingNoteAction : Action { sealed class Balance { object Update : OnboardingNoteAction() data class Set(val balance: OnboardingWalletBalance) : OnboardingNoteAction() + data class SetCriticalError(val error: TapError?) : OnboardingNoteAction() data class SetNonCriticalError(val error: TapError?) : OnboardingNoteAction() } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt index b182753729..c3af3a169a 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt @@ -118,10 +118,11 @@ private fun handleNoteAction(action: Action, dispatch: DispatchFunction) { scope.launch { val loadedBalance = onboardingManager.updateBalance(walletManager) - loadedBalance.criticalError?.let { store.dispatchErrorNotification(it) } delay(if (isLoadedBefore) 0 else 300) + loadedBalance.criticalError?.let { store.dispatchErrorNotification(it) } withMainContext { store.dispatch(OnboardingNoteAction.Balance.Set(loadedBalance)) + store.dispatch(OnboardingNoteAction.Balance.SetCriticalError(loadedBalance.criticalError)) store.dispatch(OnboardingNoteAction.Balance.SetNonCriticalError(loadedBalance.error)) } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt index 260f41fc20..913dca781f 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt @@ -26,6 +26,9 @@ private fun internalReduce(action: Action, appState: AppState): OnboardingNoteSt is OnboardingNoteAction.Balance.Set -> { state = state.copy(walletBalance = action.balance) } + is OnboardingNoteAction.Balance.SetCriticalError -> { + state = state.copy(balanceCriticalError = action.error) + } is OnboardingNoteAction.Balance.SetNonCriticalError -> { state = state.copy(balanceNonCriticalError = action.error) } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt index 3cf07bea6b..c1967db335 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt @@ -15,6 +15,7 @@ data class OnboardingNoteState( val cardArtwork: Artwork? = null, val walletBalance: OnboardingWalletBalance = OnboardingWalletBalance.loading(), val balanceNonCriticalError: TapError? = null, + val balanceCriticalError: TapError? = null, val currentStep: OnboardingNoteStep = OnboardingNoteStep.None, val steps: List = OnboardingNoteStep.values().toList(), val showConfetti: Boolean = false, diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsAction.kt index 7d48937c17..766930141a 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsAction.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsAction.kt @@ -4,6 +4,7 @@ import com.tangem.Message import com.tangem.blockchain.common.WalletManager import com.tangem.common.extensions.VoidCallback import com.tangem.tap.common.redux.StateDialog +import com.tangem.tap.domain.TapError import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.domain.twins.AssetReader import com.tangem.tap.domain.twins.TwinCardsManager @@ -55,6 +56,8 @@ sealed class TwinCardsAction : Action { sealed class Balance { object Update : TwinCardsAction() data class Set(val balance: OnboardingWalletBalance) : TwinCardsAction() + data class SetCriticalError(val error: TapError?) : TwinCardsAction() + data class SetNonCriticalError(val error: TapError?) : TwinCardsAction() } sealed class Confetti { 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 26fbfb104b..fc245c3db4 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 @@ -212,7 +212,11 @@ private fun handle(action: Action, dispatch: DispatchFunction) { val loadedBalance = onboardingManager?.updateBalance(walletManager) ?: return@launch loadedBalance.criticalError?.let { store.dispatchErrorNotification(it) } delay(if (isLoadedBefore) 0 else 300) - withMainContext { store.dispatch(TwinCardsAction.Balance.Set(loadedBalance)) } + withMainContext { + store.dispatch(TwinCardsAction.Balance.Set(loadedBalance)) + store.dispatch(TwinCardsAction.Balance.SetCriticalError(loadedBalance.criticalError)) + store.dispatch(TwinCardsAction.Balance.SetNonCriticalError(loadedBalance.error)) + } } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsReducer.kt index 153f91e796..437213c588 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsReducer.kt @@ -55,6 +55,12 @@ private fun internalReduce(action: Action, state: AppState): TwinCardsState { is TwinCardsAction.Balance.Set -> { state = state.copy(walletBalance = action.balance) } + is TwinCardsAction.Balance.SetCriticalError -> { + state = state.copy(balanceCriticalError = action.error) + } + is TwinCardsAction.Balance.SetNonCriticalError -> { + state = state.copy(balanceNonCriticalError = action.error) + } is TwinCardsAction.Confetti.Show -> { state = state.copy(showConfetti = true) } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsState.kt index ac731df22a..22834a920e 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsState.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsState.kt @@ -1,6 +1,7 @@ package com.tangem.tap.features.onboarding.products.twins.redux import com.tangem.blockchain.common.WalletManager +import com.tangem.tap.domain.TapError import com.tangem.tap.domain.twins.TwinCardNumber import com.tangem.tap.domain.twins.TwinCardsManager import com.tangem.tap.features.onboarding.OnboardingWalletBalance @@ -22,6 +23,8 @@ data class TwinCardsState( val pairCardId: String? = null, // available after create\recreate wallets val walletManager: WalletManager? = null, val walletBalance: OnboardingWalletBalance = OnboardingWalletBalance.loading(), + val balanceNonCriticalError: TapError? = null, + val balanceCriticalError: TapError? = null, val showConfetti: Boolean = false, ) : StateType { 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 05c71f8c96..ccc015f2e6 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 @@ -114,11 +114,6 @@ class TwinsCardsFragment : BaseOnboardingFragment() { override fun newState(state: TwinCardsState) { if (activity == null) return -// toolbar.title = when (state.mode) { -// CreateTwinWalletMode.CreateWallet -> getText(R.string.wallet_button_create_wallet) -// CreateTwinWalletMode.RecreateWallet -> getText(R.string.twins_recreate_toolbar) -// } - pb_state.max = state.steps.size - 1 pb_state.progress = state.progress @@ -141,9 +136,13 @@ class TwinsCardsFragment : BaseOnboardingFragment() { private fun setBalance(state: TwinCardsState) { if (state.walletBalance.currency.blockchain == Blockchain.Unknown) return - val balanceValue = state.walletBalance.value.stripZeroPlainString() - val currency = state.walletBalance.currency.currencySymbol - tv_balance_value.text = "$balanceValue $currency" + if (state.balanceCriticalError == null) { + val balanceValue = state.walletBalance.value.stripZeroPlainString() + val currency = state.walletBalance.currency.currencySymbol + tv_balance_value.text = "$balanceValue $currency" + } else { + tv_balance_value.text = "–" + } } private fun setupWelcomeOnlyState(state: TwinCardsState) { diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml index d1ac99cf9c..d34ea02c07 100644 --- a/app/src/main/res/values/strings_untranslated.xml +++ b/app/src/main/res/values/strings_untranslated.xml @@ -194,5 +194,7 @@ If the process of re-creating the wallet gets interrupted in any way, you’ll have to start over. Internal error: can\'t create wallet manager + Blockchain is unreachable. Try later + From 641d7fa37d238a922006075fe62a6a500957faf4 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 28 Oct 2021 18:46:16 +0300 Subject: [PATCH 111/189] Updated on 2026-08-14 --- app/src/main/res/layout/layout_onboarding_container_bottom.xml | 2 +- app/src/main/res/values/styles.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/layout/layout_onboarding_container_bottom.xml b/app/src/main/res/layout/layout_onboarding_container_bottom.xml index dc1a1f74ee..d5e9dbcc4a 100644 --- a/app/src/main/res/layout/layout_onboarding_container_bottom.xml +++ b/app/src/main/res/layout/layout_onboarding_container_bottom.xml @@ -36,7 +36,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="64dp" - android:layout_marginTop="10dp" + android:layout_marginTop="14dp" android:layout_marginEnd="64dp" android:minLines="3" app:layout_constraintBottom_toTopOf="@+id/fl_scan_card_container" diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index b506eaed20..43173d1bad 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -97,7 +97,7 @@ From a1d9aa64619ec12566acca6ffc3c7838d0efe2f8 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 28 Oct 2021 19:40:34 +0300 Subject: [PATCH 112/189] Updated on 2026-08-14 --- .../com/tangem/tap/common/images/Picasso.kt | 1 + .../features/onboarding/OnboardingManager.kt | 45 ++++++++++--------- .../products/note/OnboardingNoteFragment.kt | 8 +++- .../note/redux/OnboardingNoteAction.kt | 3 +- .../note/redux/OnboardingNoteMiddleware.kt | 4 +- .../note/redux/OnboardingNoteReducer.kt | 4 +- .../note/redux/OnboardingNoteState.kt | 3 +- .../OnboardingOtherCardsFragment.kt | 9 +++- .../redux/OnboardingOtherCardsAction.kt | 3 +- .../redux/OnboardingOtherCardsMiddleware.kt | 4 +- .../redux/OnboardingOtherCardsReducer.kt | 4 +- .../redux/OnboardingOtherCardsState.kt | 3 +- .../products/twins/ui/TwinsCardsFragment.kt | 2 + 13 files changed, 52 insertions(+), 41 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/common/images/Picasso.kt b/app/src/main/java/com/tangem/tap/common/images/Picasso.kt index 7d2a956bde..9220859e8b 100644 --- a/app/src/main/java/com/tangem/tap/common/images/Picasso.kt +++ b/app/src/main/java/com/tangem/tap/common/images/Picasso.kt @@ -21,6 +21,7 @@ class PicassoHelper { .downloader(OkHttp3Downloader(getOkHttpForPicasso(application))) .build() picasso.isLoggingEnabled = BuildConfig.DEBUG + picasso.setIndicatorsEnabled(BuildConfig.DEBUG) Picasso.setSingletonInstance(picasso) } 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 60d3cc323f..3aa671c5c3 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 @@ -10,20 +10,14 @@ import com.tangem.operations.attestation.OnlineCardVerifier import com.tangem.tap.common.extensions.isPositive import com.tangem.tap.common.extensions.safeUpdate import com.tangem.tap.domain.TapError -import com.tangem.tap.domain.UrlBitmapLoader import com.tangem.tap.domain.extensions.getOrLoadCardArtworkUrl import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.features.wallet.models.hasPendingTransactions -import com.tangem.tap.features.wallet.redux.Artwork import com.tangem.tap.features.wallet.redux.Currency import com.tangem.tap.features.wallet.redux.ProgressState import com.tangem.tap.persistence.UsedCardsPrefStorage -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext import timber.log.Timber import java.math.BigDecimal -import kotlin.coroutines.resume -import kotlin.coroutines.suspendCoroutine /** [REDACTED_AUTHOR] @@ -36,24 +30,31 @@ class OnboardingManager( var cardInfo: Result? = null private set - suspend fun loadArtwork(): Artwork { - cardInfo = OnlineCardVerifier().getCardInfo(scanResponse.card.cardId, scanResponse.card.cardPublicKey) - return loadArtwork(scanResponse.card.getOrLoadCardArtworkUrl(cardInfo)) + suspend fun loadArtworkUrl(): String { + val cardInfo = cardInfo + ?: OnlineCardVerifier().getCardInfo(scanResponse.card.cardId, scanResponse.card.cardPublicKey) + this.cardInfo = cardInfo + return 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 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()) { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt index 96086474e5..7eb8b3a5fc 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt @@ -10,6 +10,7 @@ import androidx.annotation.LayoutRes import androidx.constraintlayout.widget.ConstraintSet import androidx.core.view.isVisible import androidx.transition.TransitionManager +import com.squareup.picasso.Picasso import com.tangem.blockchain.common.Blockchain import com.tangem.tangem_sdk_new.extensions.fadeIn import com.tangem.tangem_sdk_new.extensions.fadeOut @@ -73,7 +74,12 @@ class OnboardingNoteFragment : BaseOnboardingFragment() { override fun newState(state: OnboardingNoteState) { if (activity == null) return - state.cardArtwork?.let { imv_front_card.swapToBitmapDrawable(it.artwork) } + Picasso.get() + .load(state.cardArtworkUrl) + .error(R.drawable.card_placeholder_black) + .placeholder(R.drawable.card_placeholder_black) + ?.into(imv_front_card) + pb_state.max = state.steps.size - 1 pb_state.progress = state.progress diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt index cce3d41924..5b377fd072 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt @@ -3,7 +3,6 @@ package com.tangem.tap.features.onboarding.products.note.redux import com.tangem.blockchain.common.WalletManager import com.tangem.tap.domain.TapError import com.tangem.tap.features.onboarding.OnboardingWalletBalance -import com.tangem.tap.features.wallet.redux.Artwork import org.rekotlin.Action /** @@ -17,7 +16,7 @@ sealed class OnboardingNoteAction : Action { object ShowAddressInfoDialog : OnboardingNoteAction() // from redux - class SetArtwork(val artwork: Artwork) : OnboardingNoteAction() + class SetArtworkUrl(val artworkUrl: String) : OnboardingNoteAction() data class SetWalletManager(val walletManager: WalletManager) : OnboardingNoteAction() object DetermineStepOfScreen : OnboardingNoteAction() object Done : OnboardingNoteAction() diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt index c3af3a169a..7565ff0c9b 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt @@ -51,8 +51,8 @@ private fun handleNoteAction(action: Action, dispatch: DispatchFunction) { when (action) { is OnboardingNoteAction.LoadCardArtwork -> { scope.launch { - val artwork = onboardingManager.loadArtwork() - withMainContext { store.dispatch(OnboardingNoteAction.SetArtwork(artwork)) } + val artworkUrl = onboardingManager.loadArtworkUrl() + withMainContext { store.dispatch(OnboardingNoteAction.SetArtworkUrl(artworkUrl)) } } } is OnboardingNoteAction.DetermineStepOfScreen -> { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt index 913dca781f..7626922560 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt @@ -17,8 +17,8 @@ private fun internalReduce(action: Action, appState: AppState): OnboardingNoteSt is GlobalAction.Onboarding.Start -> { state = OnboardingNoteState() } - is OnboardingNoteAction.SetArtwork -> { - state = state.copy(cardArtwork = action.artwork) + is OnboardingNoteAction.SetArtworkUrl -> { + state = state.copy(cardArtworkUrl = action.artworkUrl) } is OnboardingNoteAction.SetWalletManager -> { state = state.copy(walletManager = action.walletManager) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt index c1967db335..5bc772fa3a 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt @@ -3,7 +3,6 @@ package com.tangem.tap.features.onboarding.products.note.redux import com.tangem.blockchain.common.WalletManager import com.tangem.tap.domain.TapError import com.tangem.tap.features.onboarding.OnboardingWalletBalance -import com.tangem.tap.features.wallet.redux.Artwork import org.rekotlin.StateType /** @@ -12,7 +11,7 @@ import org.rekotlin.StateType data class OnboardingNoteState( val walletManager: WalletManager? = null, // UI - val cardArtwork: Artwork? = null, + val cardArtworkUrl: String? = null, val walletBalance: OnboardingWalletBalance = OnboardingWalletBalance.loading(), val balanceNonCriticalError: TapError? = null, val balanceCriticalError: TapError? = null, diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt index cdd2871e00..0072388bcb 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt @@ -7,12 +7,12 @@ import androidx.annotation.LayoutRes import androidx.constraintlayout.widget.ConstraintSet import androidx.core.view.isVisible import androidx.transition.TransitionManager +import com.squareup.picasso.Picasso import com.tangem.tap.common.extensions.getDrawableCompat import com.tangem.tap.common.redux.navigation.ShareElement import com.tangem.tap.common.transitions.InternalNoteLayoutTransition import com.tangem.tap.features.addBackPressHandler import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment -import com.tangem.tap.features.onboarding.products.note.swapToBitmapDrawable import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsAction import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsState import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsStep @@ -60,7 +60,12 @@ class OnboardingOtherCardsFragment : BaseOnboardingFragment { scope.launch { - val artwork = onboardingManager.loadArtwork() - withMainContext { store.dispatch(OnboardingOtherCardsAction.SetArtwork(artwork)) } + val artworkUrl = onboardingManager.loadArtworkUrl() + withMainContext { store.dispatch(OnboardingOtherCardsAction.SetArtworkUrl(artworkUrl)) } } } is OnboardingOtherCardsAction.DetermineStepOfScreen -> { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsReducer.kt index 129b7ea280..e8dc2ae111 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsReducer.kt @@ -17,8 +17,8 @@ private fun internalReduce(action: Action, appState: AppState): OnboardingOtherC is GlobalAction.Onboarding.Start -> { state = OnboardingOtherCardsState() } - is OnboardingOtherCardsAction.SetArtwork -> { - state = state.copy(cardArtwork = action.artwork) + is OnboardingOtherCardsAction.SetArtworkUrl -> { + state = state.copy(cardArtworkUrl = action.artworkUrl) } is OnboardingOtherCardsAction.SetStepOfScreen -> { if (action.step != state.currentStep && state.steps.contains(action.step)) { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsState.kt index b306101506..f502350050 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsState.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsState.kt @@ -1,6 +1,5 @@ package com.tangem.tap.features.onboarding.products.otherCards.redux -import com.tangem.tap.features.wallet.redux.Artwork import org.rekotlin.StateType /** @@ -8,7 +7,7 @@ import org.rekotlin.StateType */ data class OnboardingOtherCardsState( // UI - val cardArtwork: Artwork? = null, + val cardArtworkUrl: String? = null, val showConfetti: Boolean = false, val currentStep: OnboardingOtherCardsStep = OnboardingOtherCardsStep.None, val steps: List = OnboardingOtherCardsStep.values().toList(), 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 ccc015f2e6..81e11f7e47 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 @@ -84,11 +84,13 @@ class TwinsCardsFragment : BaseOnboardingFragment() { Picasso.get() .load(Artwork.TWIN_CARD_1) + .error(R.drawable.card_placeholder_black) .placeholder(R.drawable.card_placeholder_black) ?.into(imv_twin_front_card) Picasso.get() .load(Artwork.TWIN_CARD_2) + .error(R.drawable.card_placeholder_white) .placeholder(R.drawable.card_placeholder_white) ?.into(imv_twin_back_card) } From c06552db0a2711d1dd82369ad7ce5f481762e968 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 29 Oct 2021 11:24:59 +0300 Subject: [PATCH 113/189] Updated on 2026-08-14 --- .../com/tangem/tap/common/extensions/Specific.kt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt b/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt index ab289dc6b6..330457dbf6 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt @@ -23,9 +23,17 @@ fun BigDecimal.toFormattedString( } fun BigDecimal.toFormattedCurrencyString( - decimals: Int, currency: String, roundingMode: RoundingMode = RoundingMode.DOWN + decimals: Int, currency: String, roundingMode: RoundingMode = RoundingMode.DOWN, + limitNumberOfDecimals: Boolean = true ): String { - val formattedAmount = this.toFormattedString(decimals = decimals, roundingMode = roundingMode) + val decimalsForRounding = if (limitNumberOfDecimals){ + if (decimals > 8) 8 else decimals + } else { + decimals + } + val formattedAmount = this.toFormattedString( + decimals = decimalsForRounding, roundingMode = roundingMode + ) return "$formattedAmount $currency" } From 1280c6e5327ab690a64dffa86116af0c692626fd Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 29 Oct 2021 18:28:46 +0300 Subject: [PATCH 114/189] Updated on 2026-08-14 --- .../onboarding/products/note/OnboardingNoteFragment.kt | 3 +++ .../products/otherCards/OnboardingOtherCardsFragment.kt | 2 ++ app/src/main/res/values/strings_untranslated.xml | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt index 7eb8b3a5fc..e0661b0359 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt @@ -132,6 +132,8 @@ class OnboardingNoteFragment : BaseOnboardingFragment() { imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_circle)) updateConstraints(state.currentStep, R.layout.lp_onboarding_create_wallet) + + btn_alternative_action.isVisible = false // temporary } private fun setupTopUpWalletState(state: OnboardingNoteState) { @@ -140,6 +142,7 @@ class OnboardingNoteFragment : BaseOnboardingFragment() { store.dispatch(OnboardingNoteAction.TopUp) } + btn_alternative_action.isVisible = true btn_alternative_action.setText(R.string.onboarding_top_up_button_show_wallet_address) btn_alternative_action.setOnClickListener { store.dispatch(OnboardingNoteAction.ShowAddressInfoDialog) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt index 0072388bcb..22e479721b 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt @@ -87,6 +87,8 @@ class OnboardingOtherCardsFragment : BaseOnboardingFragmentGet a new card Activating card - What does it mean? + How it works? Create a wallet Let’s generate all the keys on your card and create a secure wallet Create wallet From efb2a36f418623a66c05024396a79e83aa598b28 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 29 Oct 2021 18:43:12 +0300 Subject: [PATCH 115/189] Updated on 2026-08-14 --- .../tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt | 2 +- app/src/main/res/values/strings_untranslated.xml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt index 0f164965d4..496a8ff731 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt @@ -21,7 +21,7 @@ class ScanFailsDialog { setPositiveButton(R.string.alert_button_request_support) { _, _ -> store.dispatch(GlobalAction.SendFeedback(ScanFailsEmail())) } - setNegativeButton(R.string.common_cancel) { _, _ -> } + setNeutralButton(R.string.alert_troubleshooting_scan_card_ok) { _, _ -> } setOnDismissListener { store.dispatchDialogHide() } }.create() } diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml index eca949a339..e0a8e57348 100644 --- a/app/src/main/res/values/strings_untranslated.xml +++ b/app/src/main/res/values/strings_untranslated.xml @@ -57,6 +57,7 @@ Reason: %s. Do you want to send feedback? Are you having difficulty scanning your card? Please try to tap the card exactly as shown in the animation or request support. + I\'m okay Try again Request support Send feedback From ef58ec6ab1473c2a367d97d4aa62a35c072d010c Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 29 Oct 2021 19:41:20 +0300 Subject: [PATCH 116/189] Updated on 2026-08-14 --- .../tap/features/feedback/FeedbackManager.kt | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/feedback/FeedbackManager.kt b/app/src/main/java/com/tangem/tap/features/feedback/FeedbackManager.kt index 6007d61e23..16edbb6987 100644 --- a/app/src/main/java/com/tangem/tap/features/feedback/FeedbackManager.kt +++ b/app/src/main/java/com/tangem/tap/features/feedback/FeedbackManager.kt @@ -39,6 +39,7 @@ class FeedbackManager( } fun send(emailData: EmailData) { + emailData.prepare(infoHolder) val fileLog = if (emailData is ScanFailsEmail) createLogFile() else null sendTo( email = getSupportEmail(), @@ -235,6 +236,9 @@ class AdditionalEmailInfo { interface EmailData { val subject: String val mainMessage: String + + fun prepare(infoHolder: AdditionalEmailInfo) {} + fun createOptionalMessage(infoHolder: AdditionalEmailInfo): String fun joinTogether(infoHolder: AdditionalEmailInfo): String { @@ -298,8 +302,23 @@ class SendTransactionFailedEmail(private val error: String) : EmailData { } class FeedbackEmail : EmailData { - override val subject: String = "Tangem Tap feedback" - override val mainMessage: String = "Hi Tangem," + override val subject: String + get() = if (isS2CCard) s2cSubject else tangemSubject + override val mainMessage: String + get() = if (isS2CCard) s2cMainMessage else tangemMainMessage + + private val tangemSubject = "Tangem Tap feedback" + private val tangemMainMessage = "Hi Tangem," + + private val s2cSubject = "Feedback" + private val s2cMainMessage = "Hi support team," + + private var isS2CCard = false + + override fun prepare(infoHolder: AdditionalEmailInfo) { + isS2CCard = TapWorkarounds.isStart2CoinIssuer(infoHolder.cardIssuer) + } + override fun createOptionalMessage(infoHolder: AdditionalEmailInfo): String { val builder = StringBuilder() builder.appendKeyValue("Card ID", infoHolder.cardId) From f43d1c5ae54f2c475665cd0ddaa78de6b276deb4 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 29 Oct 2021 20:40:52 +0300 Subject: [PATCH 117/189] Updated on 2026-08-14 --- app/src/main/AndroidManifest.xml | 7 ++ .../main/java/com/tangem/tap/MainActivity.kt | 2 +- .../tangem/tap/common/extensions/Activity.kt | 63 +++++++++++++ .../tap/features/feedback/FeedbackManager.kt | 89 +++++++------------ 4 files changed, 104 insertions(+), 57 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/common/extensions/Activity.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c2d4929889..f3c400a3ec 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -19,6 +19,13 @@ + + + + + + + + * + * + * + * + * + */ +fun Activity.sendEmail( + email: String, + subject: String, + message: String, + file: File? = null, + onFail: ((Exception) -> Unit)? = null +) { + fun createEmailShareIntent(recipient: String, subject: String, text: String, file: File? = null): Intent { + val builder = ShareCompat.IntentBuilder.from(this) + .setType("message/rfc822") + .setEmailTo(arrayOf(recipient)) + .setSubject(subject) + .setText(text) + file?.let { builder.setStream(FileProvider.getUriForFile(this, "${packageName}.provider", it)) } + return builder.intent + } + + val originalIntent = createEmailShareIntent(email, subject, message, file) + val emailFilterIntent = Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:")) + + val originalIntentResults = packageManager.queryIntentActivities(originalIntent, 0) + val emailFilterIntentResults = packageManager.queryIntentActivities(emailFilterIntent, 0) + + val targetedIntents = originalIntentResults + .filter { originalResult -> + emailFilterIntentResults.any { + originalResult.activityInfo.packageName == it.activityInfo.packageName + } + } + .map { + createEmailShareIntent(email, subject, message, file).apply { + setPackage(it.activityInfo.packageName) + } + } + .toMutableList() + try { + val chooserIntent = Intent.createChooser(targetedIntents.removeAt(0), "Send mail...") + chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedIntents.toTypedArray()) + chooserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + ContextCompat.startActivity(this, chooserIntent, null) + } catch (ex: Exception) { + onFail?.invoke(ex) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/feedback/FeedbackManager.kt b/app/src/main/java/com/tangem/tap/features/feedback/FeedbackManager.kt index 16edbb6987..68b6262389 100644 --- a/app/src/main/java/com/tangem/tap/features/feedback/FeedbackManager.kt +++ b/app/src/main/java/com/tangem/tap/features/feedback/FeedbackManager.kt @@ -1,19 +1,14 @@ package com.tangem.tap.features.feedback import android.app.Activity -import android.content.ActivityNotFoundException import android.content.Context -import android.content.Intent import android.content.pm.PackageManager -import android.net.Uri import android.os.Build -import androidx.core.app.ShareCompat -import androidx.core.content.ContextCompat -import androidx.core.content.FileProvider import com.tangem.Log import com.tangem.TangemSdkLogger import com.tangem.blockchain.common.* import com.tangem.common.card.Card +import com.tangem.tap.common.extensions.sendEmail import com.tangem.tap.common.extensions.stripZeroPlainString import com.tangem.tap.domain.TapWorkarounds import timber.log.Timber @@ -28,23 +23,26 @@ import java.util.* [REDACTED_AUTHOR] */ class FeedbackManager( - val infoHolder: AdditionalEmailInfo, - private val logCollector: TangemLogCollector, + val infoHolder: AdditionalEmailInfo, + private val logCollector: TangemLogCollector, ) { private lateinit var activity: Activity - fun updateAcivity(activity: Activity) { + fun updateActivity(activity: Activity) { this.activity = activity } - fun send(emailData: EmailData) { + fun send(emailData: EmailData, onFail: ((Exception) -> Unit)? = null) { + if (!this::activity.isInitialized) return + emailData.prepare(infoHolder) val fileLog = if (emailData is ScanFailsEmail) createLogFile() else null - sendTo( + activity.sendEmail( email = getSupportEmail(), subject = emailData.subject, message = emailData.joinTogether(infoHolder), - fileLog = fileLog + file = fileLog, + onFail = onFail ) } @@ -56,42 +54,6 @@ class FeedbackManager( } } - private fun sendTo(email: String, subject: String, message: String, fileLog: File? = null) { - val emailFilterIntent = Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:")) - val originalIntentResults = activity.packageManager.queryIntentActivities(emailFilterIntent, 0) - val emailFilterIntentResults = activity.packageManager.queryIntentActivities(emailFilterIntent, 0) - val targetedIntents = originalIntentResults - .filter { originalResult -> - emailFilterIntentResults.any { - originalResult.activityInfo.packageName == it.activityInfo.packageName - } - } - .map { - createEmailShareIntent(email, subject, message, fileLog).apply { - setPackage(it.activityInfo.packageName) - } - } - .toMutableList() - try { - val chooserIntent = Intent.createChooser(targetedIntents.removeAt(0), "Send mail...") - chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedIntents.toTypedArray()) - chooserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - ContextCompat.startActivity(activity, chooserIntent, null) - } catch (ex: ActivityNotFoundException) { - Timber.e(ex) - } - } - - private fun createEmailShareIntent(recipient: String, subject: String, text: String, file: File? = null): Intent { - val builder = ShareCompat.IntentBuilder.from(activity) - .setType("message/rfc822") - .setEmailTo(arrayOf(recipient)) - .setSubject(subject) - .setText(text) - file?.let { builder.setStream(FileProvider.getUriForFile(activity, "${activity.packageName}.provider", it)) } - return builder.intent - } - private fun createLogFile(): File? { return try { val file = File(activity.filesDir, "logs.txt") @@ -135,12 +97,12 @@ class TangemLogCollector : TangemSdkLogger { class AdditionalEmailInfo { class EmailWalletInfo( - var blockchain: Blockchain = Blockchain.Unknown, - var address: String = "", - var explorerLink: String = "", - var host: String = "", - // var outputsCount: String = "" - // var transactionHex: String = "" + var blockchain: Blockchain = Blockchain.Unknown, + var address: String = "", + var explorerLink: String = "", + var host: String = "", + // var outputsCount: String = "" + // var transactionHex: String = "" ) var appVersion: String = "" @@ -179,7 +141,7 @@ class AdditionalEmailInfo { cardFirmwareVersion = card.firmwareVersion.stringValue cardIssuer = card.issuer.name signedHashesCount = card.wallets - .joinToString(";") { "${it.curve?.curve} - ${it.totalSignedHashes}" } + .joinToString(";") { "${it.curve?.curve} - ${it.totalSignedHashes}" } } fun setWalletsInfo(walletManagers: List) { @@ -239,10 +201,18 @@ interface EmailData { fun prepare(infoHolder: AdditionalEmailInfo) {} + fun appendDelimiter(builder: StringBuilder) { + builder.append("----------\n") + } + + fun appendBlankLine(builder: StringBuilder) { + builder.append("\n") + } + fun createOptionalMessage(infoHolder: AdditionalEmailInfo): String fun joinTogether(infoHolder: AdditionalEmailInfo): String { - return "$mainMessage\n\n\n\n\n" + + return "$mainMessage\n\n\n\n" + "Following information is optional. You can erase it if you don’t want to share it.\n" + createOptionalMessage(infoHolder) } @@ -257,6 +227,7 @@ class RateCanBeBetterEmail : EmailData { return StringBuilder().apply { appendKeyValue("Card ID", infoHolder.cardId) appendKeyValue("Blockchain", walletInfo.blockchain.fullName) + appendBlankLine(this) appendKeyValue("Phone model", infoHolder.phoneModel) appendKeyValue("OS version", infoHolder.osVersion) appendKeyValue("App version", infoHolder.appVersion) @@ -269,6 +240,7 @@ class ScanFailsEmail : EmailData { override val mainMessage: String = "Please tell us what card do you have?" override fun createOptionalMessage(infoHolder: AdditionalEmailInfo): String { return StringBuilder().apply { + appendBlankLine(this) appendKeyValue("Phone model", infoHolder.phoneModel) appendKeyValue("OS version", infoHolder.osVersion) appendKeyValue("App version", infoHolder.appVersion) @@ -284,6 +256,7 @@ class SendTransactionFailedEmail(private val error: String) : EmailData { return StringBuilder().apply { appendKeyValue("Error", error) appendKeyValue("Card ID", infoHolder.cardId) + appendDelimiter(this) appendKeyValue("Blockchain", walletInfo.blockchain.fullName) appendKeyValue("Host", walletInfo.host) appendKeyValue("Token", infoHolder.token) @@ -291,6 +264,7 @@ class SendTransactionFailedEmail(private val error: String) : EmailData { appendKeyValue("Destination address", infoHolder.destinationAddress) appendKeyValue("Amount", infoHolder.amount) appendKeyValue("Fee", infoHolder.fee) + appendBlankLine(this) appendKeyValue("Phone model", infoHolder.phoneModel) appendKeyValue("OS version", infoHolder.osVersion) appendKeyValue("App version", infoHolder.appVersion) @@ -326,11 +300,14 @@ class FeedbackEmail : EmailData { builder.appendKeyValue("Signed hashes", infoHolder.signedHashesCount) infoHolder.walletsInfo.forEach { + appendDelimiter(builder) builder.appendKeyValue("Blockchain", it.blockchain.fullName) builder.appendKeyValue("Host", it.host) builder.appendKeyValue("Wallet address", it.address) builder.appendKeyValue("Explorer link", it.explorerLink) } + appendBlankLine(builder) + // appendKeyValue("Outputs count", infoHolder.outputsCount) builder.appendKeyValue("Phone model", infoHolder.phoneModel) builder.appendKeyValue("OS version", infoHolder.osVersion) From 3e99e1f1c81f6a0590ddf3eb6087e1b1f828754a Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 29 Oct 2021 20:56:31 +0300 Subject: [PATCH 118/189] Updated on 2026-08-14 --- app/src/main/res/layout/lp_onboarding_done.xml | 2 +- app/src/main/res/layout/lp_onboarding_done_activation.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/layout/lp_onboarding_done.xml b/app/src/main/res/layout/lp_onboarding_done.xml index af43383cc7..e6f1cb76fa 100644 --- a/app/src/main/res/layout/lp_onboarding_done.xml +++ b/app/src/main/res/layout/lp_onboarding_done.xml @@ -25,7 +25,7 @@ Date: Fri, 29 Oct 2021 21:52:35 +0300 Subject: [PATCH 119/189] Updated on 2026-08-14 --- .../main/java/com/tangem/tap/features/home/HomeFragment.kt | 6 +++++- .../onboarding/products/twins/ui/TwinsCardsFragment.kt | 7 +++++++ app/src/main/res/layout/fragment_onboarding_main.xml | 1 + app/src/main/res/layout/view_bg_home.xml | 5 ++++- app/src/main/res/layout/view_bg_twins_welcome.xml | 3 +++ 5 files changed, 20 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt index 0d337c6da9..f28523564f 100644 --- a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt @@ -18,6 +18,7 @@ import com.tangem.wallet.R import kotlinx.android.synthetic.main.fragment_onboarding_main.* import kotlinx.android.synthetic.main.layout_onboarding_container_bottom.* import kotlinx.android.synthetic.main.layout_onboarding_home_top.* +import kotlinx.android.synthetic.main.view_bg_home.* class HomeFragment : BaseOnboardingFragment() { @@ -39,7 +40,10 @@ class HomeFragment : BaseOnboardingFragment() { val shareTransition = FragmentShareTransition( listOf( ShareElement(imv_front_card, ShareElement.imvFrontCard), - ShareElement(imv_back_card, ShareElement.imvBackCard) + ShareElement(imv_back_card, ShareElement.imvBackCard), + ShareElement(bg_circle_large), + ShareElement(bg_circle_medium), + ShareElement(bg_circle_min), ), FrontCardEnterTransition(), FrontCardExitTransition() 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 81e11f7e47..a634d311c0 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 @@ -102,6 +102,9 @@ class TwinsCardsFragment : BaseOnboardingFragment() { imv_twin_front_card.transitionName = ShareElement.imvFrontCard imv_twin_back_card.transitionName = ShareElement.imvBackCard + + // if don't this, the bg_circle_... is overflow the app_bar + app_bar.bringToFront() } override fun subscribeToStore() { @@ -158,6 +161,7 @@ class TwinsCardsFragment : BaseOnboardingFragment() { private fun setupWelcomeState(state: TwinCardsState, mainAction: Action) { twinsWidget.toWelcome(false) { startPostponedEnterTransition() } + onboarding_twins_welcome_bg.show() pb_state.hide() tv_header.setText(R.string.twins_onboarding_subtitle) @@ -169,6 +173,8 @@ class TwinsCardsFragment : BaseOnboardingFragment() { private fun setupWarningState(state: TwinCardsState) { twinsWidget.toWelcome(false) { startPostponedEnterTransition() } + + onboarding_twins_welcome_bg.hide() pb_state.hide() chb_understand.show() @@ -186,6 +192,7 @@ class TwinsCardsFragment : BaseOnboardingFragment() { } private fun setupCreateFirstWalletState(state: TwinCardsState) { + onboarding_twins_welcome_bg.hide() bg_circle_large.hide() bg_circle_medium.hide() bg_circle_min.hide() diff --git a/app/src/main/res/layout/fragment_onboarding_main.xml b/app/src/main/res/layout/fragment_onboarding_main.xml index 40df33aeb5..6d567ce91b 100644 --- a/app/src/main/res/layout/fragment_onboarding_main.xml +++ b/app/src/main/res/layout/fragment_onboarding_main.xml @@ -14,6 +14,7 @@ style="@style/Widget.MaterialComponents.Toolbar.Surface" android:layout_width="match_parent" android:layout_height="wrap_content" + android:background="@android:color/transparent" android:fitsSystemWindows="false" app:liftOnScroll="true"> diff --git a/app/src/main/res/layout/view_bg_home.xml b/app/src/main/res/layout/view_bg_home.xml index bff701e6c4..4cba83727d 100644 --- a/app/src/main/res/layout/view_bg_home.xml +++ b/app/src/main/res/layout/view_bg_home.xml @@ -9,6 +9,7 @@ android:layout_width="670dp" android:layout_height="670dp" android:src="@drawable/shape_circle_home_screen" + android:transitionName="bg_circle_large" android:translationX="160dp" android:translationY="-175dp" /> @@ -18,14 +19,16 @@ android:layout_height="650dp" android:src="@drawable/shape_circle" android:tint="@color/backgroundWhite" + android:transitionName="bg_circle_medium" android:translationX="170dp" android:translationY="-165dp" /> diff --git a/app/src/main/res/layout/view_bg_twins_welcome.xml b/app/src/main/res/layout/view_bg_twins_welcome.xml index 5202b1851b..acfee09420 100644 --- a/app/src/main/res/layout/view_bg_twins_welcome.xml +++ b/app/src/main/res/layout/view_bg_twins_welcome.xml @@ -12,6 +12,7 @@ android:scaleType="fitXY" android:src="@drawable/shape_circle" android:tint="#DEDEE0" + android:transitionName="bg_circle_large" android:translationX="-272dp" android:translationY="-200dp" app:layout_constraintStart_toStartOf="parent" @@ -25,6 +26,7 @@ android:scaleType="fitXY" android:src="@drawable/shape_circle" android:tint="#DCDCDC" + android:transitionName="bg_circle_medium" android:translationX="-254dp" android:translationY="-269dp" app:layout_constraintStart_toStartOf="parent" @@ -38,6 +40,7 @@ android:scaleType="fitXY" android:src="@drawable/shape_circle" android:tint="#D9D9D9" + android:transitionName="bg_circle_min" android:translationX="-394dp" android:translationY="-411dp" app:layout_constraintStart_toStartOf="parent" From 262223b43d21dfa69841dda7f2188c2da5881aff Mon Sep 17 00:00:00 2001 From: Tangem Date: Sun, 31 Oct 2021 12:06:56 +0300 Subject: [PATCH 120/189] Updated on 2026-08-14 --- .../disclaimer/ui/DisclaimerFragment.kt | 4 +- .../res/drawable/card_placeholder_black.xml | 120 ++++++------------ 2 files changed, 43 insertions(+), 81 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt b/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt index ec7a45dd3d..82f131a3a2 100644 --- a/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt @@ -25,8 +25,8 @@ class DisclaimerFragment : Fragment(R.layout.fragment_disclaimer), } }) val inflater = TransitionInflater.from(requireContext()) - enterTransition = inflater.inflateTransition(R.transition.slide_right) - exitTransition = inflater.inflateTransition(R.transition.fade) + enterTransition = inflater.inflateTransition(android.R.transition.slide_bottom) + exitTransition = inflater.inflateTransition(android.R.transition.slide_top) } override fun onStart() { diff --git a/app/src/main/res/drawable/card_placeholder_black.xml b/app/src/main/res/drawable/card_placeholder_black.xml index 0fef87ee39..83fab8e0f7 100644 --- a/app/src/main/res/drawable/card_placeholder_black.xml +++ b/app/src/main/res/drawable/card_placeholder_black.xml @@ -1,126 +1,88 @@ - - - - - - - - - + android:width="214dp" + android:height="130dp" + android:viewportWidth="214" + android:viewportHeight="130"> + android:pathData="M1.043,8.273C1.019,4.256 4.265,1 8.294,1H205.706C209.735,1 213.019,4.256 213.043,8.273L213.709,121.725C213.733,125.742 210.487,128.998 206.459,128.998H9.046C5.018,128.998 1.733,125.742 1.709,121.725L1.043,8.273Z" /> - - - - - - - + android:pathData="M1.043,8.273C1.019,4.256 4.265,1 8.294,1H205.706C209.735,1 213.019,4.256 213.043,8.273L213.709,121.725C213.733,125.742 210.487,128.998 206.459,128.998H9.046C5.018,128.998 1.733,125.742 1.709,121.725L1.043,8.273Z" + android:strokeWidth="0.5" /> + android:pathData="M25.274,26.4L25.274,32.64L28.079,32.64C28.188,32.639 28.292,32.596 28.369,32.522C28.446,32.447 28.49,32.346 28.491,32.241L28.491,26.4L25.274,26.4Z" /> + android:pathData="M19,32.241C19.001,32.346 19.045,32.447 19.122,32.522C19.199,32.596 19.303,32.639 19.412,32.64L22.217,32.64L22.217,26.4L19,26.4L19,32.241Z" /> + android:pathData="M28.491,23.2L28.491,20.409C28.491,20.301 28.448,20.197 28.372,20.12C28.296,20.043 28.194,20 28.086,20L19.405,20C19.297,20 19.195,20.043 19.119,20.12C19.043,20.197 19,20.301 19,20.409L19,23.2L28.491,23.2Z" /> + android:pathData="M43.503,24.163C42.922,23.921 42.292,23.812 41.661,23.846C40.827,23.844 39.996,23.934 39.183,24.113L39.183,25.532C39.832,25.455 40.483,25.412 41.136,25.4C41.486,25.385 41.836,25.418 42.177,25.5C42.286,25.532 42.385,25.59 42.465,25.669C42.546,25.748 42.605,25.846 42.637,25.953C42.74,26.334 42.783,26.728 42.766,27.122L42.766,27.539C41.997,27.498 41.385,27.476 40.965,27.476C40.519,27.454 40.073,27.539 39.667,27.725C39.347,27.903 39.109,28.195 38.999,28.54C38.847,29.044 38.779,29.569 38.796,30.094C38.74,30.771 38.901,31.448 39.257,32.029C39.445,32.243 39.683,32.409 39.95,32.513C40.216,32.618 40.505,32.659 40.791,32.632C41.127,32.632 42.172,32.605 42.633,32.007C42.709,31.912 42.774,31.808 42.826,31.698L42.877,32.496L44.572,32.496L44.572,27.222C44.596,26.539 44.52,25.857 44.346,25.196C44.284,24.977 44.178,24.772 44.033,24.594C43.888,24.417 43.708,24.27 43.503,24.163ZM42.748,29.256C42.796,29.736 42.713,30.219 42.508,30.656C42.409,30.782 42.272,30.876 42.117,30.924C41.942,30.972 41.76,30.995 41.578,30.992C41.294,31.028 41.006,30.953 40.777,30.783C40.623,30.523 40.558,30.221 40.592,29.922C40.582,29.681 40.607,29.44 40.666,29.206C40.684,29.139 40.716,29.075 40.761,29.021C40.805,28.966 40.861,28.921 40.924,28.889C41.098,28.821 41.285,28.791 41.472,28.803L42.748,28.803L42.748,29.256Z" /> + android:pathData="M58.169,24.645C58.133,24.584 58.095,24.526 58.053,24.471C57.494,23.736 56.162,23.846 56.162,23.846C55.722,23.831 55.29,23.964 54.929,24.222C54.564,24.573 54.329,25.042 54.263,25.55C54.001,27.328 54.001,29.136 54.263,30.914C54.326,31.417 54.555,31.882 54.911,32.233C55.299,32.52 55.774,32.659 56.251,32.623C56.251,32.623 56.278,32.623 56.296,32.623C56.296,32.623 57.498,32.72 58.084,32.104C58.095,32.607 58.062,33.11 57.985,33.606C57.966,33.738 57.915,33.864 57.837,33.971C57.759,34.078 57.656,34.164 57.539,34.222C57.166,34.351 56.773,34.406 56.381,34.382L54.535,34.35L54.535,35.788C54.876,35.853 55.219,35.9 55.563,35.93C55.988,35.972 56.39,35.99 56.774,35.99C57.49,36.044 58.205,35.884 58.834,35.53C59.055,35.374 59.242,35.174 59.386,34.943C59.529,34.71 59.626,34.451 59.67,34.18C59.813,33.297 59.875,32.402 59.853,31.507L59.853,23.979L58.209,23.979L58.169,24.645ZM57.963,30.221C57.936,30.452 57.824,30.664 57.65,30.813C57.447,30.94 57.212,30.999 56.975,30.983C56.711,31.006 56.446,30.95 56.211,30.823C56.028,30.636 55.923,30.385 55.916,30.12C55.815,28.876 55.815,27.625 55.916,26.381C55.923,26.125 56.025,25.882 56.202,25.701C56.442,25.579 56.709,25.526 56.975,25.55C57.227,25.529 57.479,25.599 57.686,25.747C57.87,25.947 57.981,26.207 57.999,26.482C58.066,27.072 58.093,27.666 58.079,28.26C58.101,28.916 58.062,29.573 57.963,30.221Z" /> + android:pathData="M66.713,24.124C66.131,23.787 65.461,23.636 64.793,23.691C64.098,23.642 63.404,23.802 62.799,24.152C62.358,24.473 62.062,24.959 61.975,25.502C61.831,26.388 61.77,27.286 61.793,28.184C61.769,29.083 61.833,29.983 61.984,30.87C62.031,31.139 62.131,31.395 62.278,31.623C62.425,31.851 62.616,32.046 62.84,32.197C63.478,32.531 64.194,32.681 64.911,32.631C65.367,32.63 65.823,32.603 66.276,32.552C66.653,32.517 67.025,32.453 67.392,32.359L67.392,30.916C66.554,31.004 65.867,31.05 65.33,31.05C64.933,31.074 64.535,31.025 64.155,30.907C64.038,30.857 63.936,30.778 63.856,30.677C63.777,30.577 63.723,30.458 63.7,30.331C63.618,29.823 63.585,29.307 63.6,28.792L67.574,28.792L67.574,28.165C67.596,27.241 67.548,26.317 67.433,25.4C67.368,24.896 67.109,24.438 66.713,24.124ZM65.849,27.428L63.6,27.428C63.6,26.95 63.632,26.473 63.696,26C63.704,25.887 63.736,25.777 63.79,25.678C63.843,25.579 63.917,25.492 64.005,25.424C64.473,25.233 64.994,25.233 65.462,25.424C65.548,25.494 65.619,25.583 65.671,25.683C65.723,25.782 65.754,25.892 65.762,26.004C65.833,26.475 65.862,26.952 65.849,27.428Z" /> + android:pathData="M78.824,25.12C78.747,24.744 78.542,24.406 78.243,24.161C77.886,23.928 77.46,23.817 77.032,23.844C77.032,23.844 76.991,23.844 76.963,23.844C76.963,23.844 75.581,23.767 75.01,24.464C74.948,24.542 74.892,24.625 74.844,24.712C74.752,24.478 74.592,24.275 74.383,24.129C74.024,23.923 73.61,23.826 73.195,23.848C73.195,23.848 71.813,23.767 71.242,24.468C71.178,24.545 71.122,24.629 71.076,24.717L71.03,23.984L69.349,23.984L69.349,32.48L71.191,32.48L71.191,28.051C71.182,27.52 71.201,26.988 71.251,26.459C71.263,26.198 71.362,25.949 71.532,25.749C71.736,25.584 71.997,25.506 72.26,25.531C72.493,25.513 72.727,25.557 72.937,25.658C73.015,25.731 73.076,25.819 73.119,25.916C73.162,26.013 73.185,26.118 73.186,26.223C73.234,26.747 73.254,27.272 73.245,27.798L73.245,32.48L75.088,32.48L75.088,28.051C75.088,27.314 75.088,26.785 75.143,26.459C75.156,26.197 75.258,25.946 75.434,25.749C75.642,25.586 75.906,25.508 76.171,25.531C76.405,25.513 76.641,25.557 76.852,25.658C76.929,25.732 76.99,25.82 77.032,25.917C77.074,26.014 77.096,26.118 77.096,26.223C77.147,26.747 77.167,27.272 77.156,27.798L77.156,32.48L78.999,32.48L78.999,26.884C79.009,26.291 78.951,25.699 78.824,25.12Z" /> + android:pathData="M51.691,24.171C51.307,23.928 50.851,23.814 50.394,23.845C50.394,23.845 50.356,23.845 50.338,23.845C50.338,23.845 48.938,23.773 48.364,24.465C48.323,24.514 48.286,24.565 48.252,24.619L48.21,23.981L46.507,23.981L46.507,32.48L48.373,32.48L48.373,28.049C48.363,27.521 48.384,26.992 48.434,26.465C48.448,26.203 48.553,25.952 48.733,25.755C48.958,25.584 49.242,25.503 49.526,25.528C49.774,25.511 50.023,25.555 50.249,25.655C50.333,25.728 50.401,25.816 50.448,25.915C50.496,26.014 50.521,26.121 50.524,26.23C50.578,26.75 50.6,27.273 50.59,27.796L50.59,32.48L52.456,32.48L52.456,26.963C52.471,26.356 52.419,25.749 52.302,25.153C52.226,24.765 52.009,24.417 51.691,24.171Z" /> + android:pathData="M36.543,21.44L34.653,21.44L34.653,23.877L33.477,23.877L33.477,25.434L34.653,25.434L34.653,32.48L36.543,32.48L36.543,25.434L37.981,25.434L37.981,23.877L36.543,23.877L36.543,21.44Z" /> + android:pathData="M23.463,105.118L21.826,104.112L21.904,106H21.024L21.087,104.112L19.441,105.125L19.02,104.335L20.707,103.493L19.016,102.645L19.428,101.882L21.08,102.875L21.001,101H21.874L21.818,102.882L23.451,101.882L23.885,102.645L22.184,103.5L23.895,104.342L23.463,105.118Z" /> + android:pathData="M32.161,105.118L30.524,104.112L30.602,106H29.722L29.785,104.112L28.139,105.125L27.718,104.335L29.405,103.493L27.715,102.645L28.126,101.882L29.778,102.875L29.699,101H30.573L30.517,102.882L32.149,101.882L32.583,102.645L30.883,103.5L32.593,104.342L32.161,105.118Z" /> + android:pathData="M40.86,105.118L39.222,104.112L39.3,106H38.42L38.483,104.112L36.837,105.125L36.416,104.335L38.104,103.493L36.413,102.645L36.825,101.882L38.476,102.875L38.398,101H39.271L39.215,102.882L40.847,101.882L41.282,102.645L39.581,103.5L41.292,104.342L40.86,105.118Z" /> + android:pathData="M49.558,105.118L47.92,104.112L47.998,106H47.119L47.181,104.112L45.535,105.125L45.114,104.335L46.802,103.493L45.111,102.645L45.523,101.882L47.174,102.875L47.096,101H47.969L47.913,102.882L49.546,101.882L49.98,102.645L48.279,103.5L49.99,104.342L49.558,105.118Z" /> + android:pathData="M63.14,105.118L61.502,104.112L61.58,106H60.701L60.763,104.112L59.117,105.125L58.696,104.335L60.384,103.493L58.693,102.645L59.105,101.882L60.756,102.875L60.678,101H61.551L61.495,102.882L63.128,101.882L63.562,102.645L61.861,103.5L63.572,104.342L63.14,105.118Z" /> + android:pathData="M71.838,105.118L70.2,104.112L70.279,106H69.399L69.462,104.112L67.815,105.125L67.394,104.335L69.082,103.493L67.391,102.645L67.803,101.882L69.454,102.875L69.376,101H70.249L70.193,102.882L71.826,101.882L72.26,102.645L70.559,103.5L72.27,104.342L71.838,105.118Z" /> + android:pathData="M80.536,105.118L78.898,104.112L78.977,106H78.097L78.16,104.112L76.514,105.125L76.093,104.335L77.78,103.493L76.089,102.645L76.501,101.882L78.152,102.875L78.074,101H78.947L78.891,102.882L80.524,101.882L80.958,102.645L79.257,103.5L80.968,104.342L80.536,105.118Z" /> + android:fillColor="#A5A5A5" + android:pathData="M89.234,105.118L87.597,104.112L87.675,106H86.795L86.858,104.112L85.212,105.125L84.791,104.335L86.478,103.493L84.788,102.645L85.2,101.882L86.851,102.875L86.772,101H87.646L87.59,102.882L89.222,101.882L89.657,102.645L87.956,103.5L89.666,104.342L89.234,105.118Z" /> + android:fillColor="#A5A5A5" + android:pathData="M102.816,105.118L101.179,104.112L101.257,106H100.377L100.44,104.112L98.794,105.125L98.373,104.335L100.06,103.493L98.369,102.645L98.781,101.882L100.433,102.875L100.354,101H101.227L101.171,102.882L102.804,101.882L103.238,102.645L101.538,103.5L103.248,104.342L102.816,105.118Z" /> + android:fillColor="#A5A5A5" + android:pathData="M111.515,105.118L109.877,104.112L109.955,106H109.075L109.138,104.112L107.492,105.125L107.071,104.335L108.758,103.493L107.068,102.645L107.48,101.882L109.131,102.875L109.053,101H109.926L109.87,102.882L111.502,101.882L111.937,102.645L110.236,103.5L111.947,104.342L111.515,105.118Z" /> + android:fillColor="#A5A5A5" + android:pathData="M120.213,105.118L118.575,104.112L118.653,106H117.774L117.836,104.112L116.19,105.125L115.769,104.335L117.457,103.493L115.766,102.645L116.178,101.882L117.829,102.875L117.751,101H118.624L118.568,102.882L120.201,101.882L120.635,102.645L118.934,103.5L120.645,104.342L120.213,105.118Z" /> + android:fillColor="#A5A5A5" + android:pathData="M128.911,105.118L127.273,104.112L127.352,106H126.472L126.535,104.112L124.888,105.125L124.467,104.335L126.155,103.493L124.464,102.645L124.876,101.882L126.527,102.875L126.449,101H127.322L127.266,102.882L128.899,101.882L129.333,102.645L127.632,103.5L129.343,104.342L128.911,105.118Z" /> + android:fillColor="#A5A5A5" + android:pathData="M142.493,105.118L140.855,104.112L140.933,106H140.054L140.116,104.112L138.47,105.125L138.049,104.335L139.737,103.493L138.046,102.645L138.458,101.882L140.109,102.875L140.031,101H140.904L140.848,102.882L142.481,101.882L142.915,102.645L141.214,103.5L142.925,104.342L142.493,105.118Z" /> + android:fillColor="#A5A5A5" + android:pathData="M151.191,105.118L149.553,104.112L149.632,106H148.752L148.815,104.112L147.169,105.125L146.748,104.335L148.435,103.493L146.744,102.645L147.156,101.882L148.807,102.875L148.729,101H149.602L149.546,102.882L151.179,101.882L151.613,102.645L149.912,103.5L151.623,104.342L151.191,105.118Z" /> + android:fillColor="#A5A5A5" + android:pathData="M159.889,105.118L158.252,104.112L158.33,106H157.45L157.513,104.112L155.867,105.125L155.446,104.335L157.133,103.493L155.443,102.645L155.854,101.882L157.506,102.875L157.427,101H158.3L158.244,102.882L159.877,101.882L160.311,102.645L158.611,103.5L160.321,104.342L159.889,105.118Z" /> + android:fillColor="#A5A5A5" + android:pathData="M168.588,105.118L166.95,104.112L167.028,106H166.148L166.211,104.112L164.565,105.125L164.144,104.335L165.831,103.493L164.141,102.645L164.553,101.882L166.204,102.875L166.126,101H166.999L166.943,102.882L168.575,101.882L169.01,102.645L167.309,103.5L169.02,104.342L168.588,105.118Z" /> From 8d048abd51203ef99619395bd7bc48cd4d314d4e Mon Sep 17 00:00:00 2001 From: Tangem Date: Sun, 31 Oct 2021 17:38:30 +0300 Subject: [PATCH 121/189] Updated on 2026-08-14 --- .../products/note/OnboardingNoteFragment.kt | 25 +++++++++----- .../note/redux/OnboardingNoteState.kt | 6 ++++ .../products/twins/redux/TwinCardsState.kt | 6 ++++ .../products/twins/ui/TwinsCardsFragment.kt | 33 +++++++++++-------- .../main/res/values/strings_untranslated.xml | 3 ++ 5 files changed, 51 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt index e0661b0359..644bf15d51 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt @@ -137,15 +137,24 @@ class OnboardingNoteFragment : BaseOnboardingFragment() { } private fun setupTopUpWalletState(state: OnboardingNoteState) { - btn_main_action.setText(R.string.onboarding_top_up_button_but_crypto) - btn_main_action.setOnClickListener { - store.dispatch(OnboardingNoteAction.TopUp) - } + if (state.isBuyAllowed) { + btn_main_action.setText(R.string.onboarding_top_up_button_but_crypto) + btn_main_action.setOnClickListener { + store.dispatch(OnboardingNoteAction.TopUp) + } - btn_alternative_action.isVisible = true - btn_alternative_action.setText(R.string.onboarding_top_up_button_show_wallet_address) - btn_alternative_action.setOnClickListener { - store.dispatch(OnboardingNoteAction.ShowAddressInfoDialog) + btn_alternative_action.isVisible = true + btn_alternative_action.setText(R.string.onboarding_top_up_button_show_wallet_address) + btn_alternative_action.setOnClickListener { + store.dispatch(OnboardingNoteAction.ShowAddressInfoDialog) + } + } else { + btn_main_action.setText(R.string.onboarding_button_receive_crypto) + btn_main_action.setOnClickListener { + store.dispatch(OnboardingNoteAction.ShowAddressInfoDialog) + } + + btn_alternative_action.isVisible = false } tv_header.setText(R.string.onboarding_top_up_header) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt index 5bc772fa3a..5178c2eb72 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt @@ -3,7 +3,9 @@ package com.tangem.tap.features.onboarding.products.note.redux import com.tangem.blockchain.common.WalletManager import com.tangem.tap.domain.TapError import com.tangem.tap.features.onboarding.OnboardingWalletBalance +import com.tangem.tap.store import org.rekotlin.StateType +import kotlin.properties.ReadOnlyProperty /** [REDACTED_AUTHOR] @@ -22,6 +24,10 @@ data class OnboardingNoteState( val progress: Int get() = steps.indexOf(currentStep) + + val isBuyAllowed: Boolean by ReadOnlyProperty { thisRef, property -> + store.state.globalState.moonPayUserStatus?.isBuyAllowed ?: false + } } enum class OnboardingNoteStep { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsState.kt index 22834a920e..7e8cea4f57 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsState.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsState.kt @@ -5,7 +5,9 @@ import com.tangem.tap.domain.TapError import com.tangem.tap.domain.twins.TwinCardNumber import com.tangem.tap.domain.twins.TwinCardsManager import com.tangem.tap.features.onboarding.OnboardingWalletBalance +import com.tangem.tap.store import org.rekotlin.StateType +import kotlin.properties.ReadOnlyProperty /** [REDACTED_AUTHOR] @@ -52,6 +54,10 @@ data class TwinCardsState( val showAlert: Boolean get() = currentStep == TwinCardsStep.CreateSecondWallet || currentStep == TwinCardsStep.CreateThirdWallet + + val isBuyAllowed: Boolean by ReadOnlyProperty { thisRef, property -> + store.state.globalState.moonPayUserStatus?.isBuyAllowed ?: false + } } enum class CreateTwinWalletMode { CreateWallet, RecreateWallet } 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 a634d311c0..140211ceb8 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 @@ -14,7 +14,6 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.common.extensions.VoidCallback import com.tangem.tap.common.extensions.* import com.tangem.tap.common.leapfrogWidget.LeapfrogWidget -import com.tangem.tap.common.postUi import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.common.redux.navigation.ShareElement @@ -290,15 +289,24 @@ class TwinsCardsFragment : BaseOnboardingFragment() { } } - btn_main_action.setText(R.string.onboarding_top_up_button_but_crypto) - btn_main_action.setOnClickListener { - store.dispatch(TwinCardsAction.TopUp) - } + if (state.isBuyAllowed) { + btn_main_action.setText(R.string.onboarding_top_up_button_but_crypto) + btn_main_action.setOnClickListener { + store.dispatch(TwinCardsAction.TopUp) + } - btn_alternative_action.isVisible = true - btn_alternative_action.setText(R.string.onboarding_top_up_button_show_wallet_address) - btn_alternative_action.setOnClickListener { - store.dispatch(TwinCardsAction.ShowAddressInfoDialog) + btn_alternative_action.isVisible = true + btn_alternative_action.setText(R.string.onboarding_top_up_button_show_wallet_address) + btn_alternative_action.setOnClickListener { + store.dispatch(TwinCardsAction.ShowAddressInfoDialog) + } + } else { + btn_main_action.setText(R.string.onboarding_button_receive_crypto) + btn_main_action.setOnClickListener { + store.dispatch(TwinCardsAction.ShowAddressInfoDialog) + } + + btn_alternative_action.isVisible = false } tv_header.setText(R.string.onboarding_top_up_header) @@ -306,13 +314,10 @@ class TwinsCardsFragment : BaseOnboardingFragment() { btnRefreshBalanceWidget.changeState(state.walletBalance.state) if (btnRefreshBalanceWidget.isShowing != true) { - postUi(300) { - btnRefreshBalanceWidget.mainView.setOnClickListener { - store.dispatch(TwinCardsAction.Balance.Update) - } + btnRefreshBalanceWidget.mainView.setOnClickListener { + store.dispatch(TwinCardsAction.Balance.Update) } } - imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_rectangle_rounded_8)) updateConstraints(state.currentStep, R.layout.lp_onboarding_topup_wallet_twins) } diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml index e0a8e57348..5e515d4049 100644 --- a/app/src/main/res/values/strings_untranslated.xml +++ b/app/src/main/res/values/strings_untranslated.xml @@ -176,6 +176,8 @@ Activating card How it works? + Receive crypto + Create a wallet Let’s generate all the keys on your card and create a secure wallet Create wallet @@ -195,6 +197,7 @@ If the process of re-creating the wallet gets interrupted in any way, you’ll have to start over. Internal error: can\'t create wallet manager + Blockchain is unreachable. Try later From 4674a83d0b32efb43c1edbaab5f6300139bf4b76 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sun, 31 Oct 2021 18:05:04 +0300 Subject: [PATCH 122/189] Updated on 2026-08-14 --- app/src/main/res/values/strings_untranslated.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml index 5e515d4049..f189ef8196 100644 --- a/app/src/main/res/values/strings_untranslated.xml +++ b/app/src/main/res/values/strings_untranslated.xml @@ -193,6 +193,8 @@ Your crypto card is activated and ready to be used Continue Balance + Send only %s (%s) to this address. Sending any other currency will result in its irreversible loss. + Send only %s (%s) from %@snetwork to this address. Sending any other currency will result in its irreversible loss. If the process of re-creating the wallet gets interrupted in any way, you’ll have to start over. From dbcb1b57753c8b3f6e93a776e4a8fb9560560990 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sun, 31 Oct 2021 21:17:08 +0300 Subject: [PATCH 123/189] Updated on 2026-08-14 --- .../main/java/com/tangem/tap/domain/extensions/Blockchain.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 4be4dda58f..5ec3ea7672 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 @@ -15,7 +15,7 @@ fun Blockchain.isNoAccountError(exception: Throwable): Boolean { fun Blockchain.amountToCreateAccount(token: Token? = null): BigDecimal? { return when (this) { - Blockchain.Stellar -> if (token?.symbol == NODL) BigDecimal(1.5) else BigDecimal.ZERO + Blockchain.Stellar -> if (token?.symbol == NODL) BigDecimal(1.5) else BigDecimal.ONE Blockchain.XRP -> BigDecimal(10) else -> null } From 96f039595809d4208a117e04c84dc54400c90e59 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 1 Nov 2021 01:08:28 +0300 Subject: [PATCH 124/189] Updated on 2026-08-14 --- .../products/twins/redux/TwinCardsMiddleware.kt | 11 ++++++++++- .../products/twins/ui/TwinsCardsFragment.kt | 12 +++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) 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 fc245c3db4..761fee04ea 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 @@ -178,7 +178,16 @@ private fun handle(action: Action, dispatch: DispatchFunction) { is Result.Success -> { updateScanResponse(result.data) delay(DELAY_SDK_DIALOG_CLOSE) - withMainContext { store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.TopUpWallet)) } + withMainContext { + when (twinCardsState.mode) { + CreateTwinWalletMode.CreateWallet -> { + store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.TopUpWallet)) + } + CreateTwinWalletMode.RecreateWallet -> { + store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.Done)) + } + } + } } is Result.Failure -> { } 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 140211ceb8..d0ba56eeb5 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 @@ -37,7 +37,6 @@ import kotlinx.android.synthetic.main.layout_onboarding_container_top.* import kotlinx.android.synthetic.main.view_bg_twins_welcome.* import kotlinx.android.synthetic.main.view_onboarding_progress.* import kotlinx.android.synthetic.main.view_onboarding_tv_balance.* -import org.rekotlin.Action class TwinsCardsFragment : BaseOnboardingFragment() { @@ -150,14 +149,17 @@ class TwinsCardsFragment : BaseOnboardingFragment() { } private fun setupWelcomeOnlyState(state: TwinCardsState) { - setupWelcomeState(state, NavigationAction.NavigateTo(AppScreen.Wallet)) + setupWelcomeState(state) { + store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) + store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.None)) + } } private fun setupWelcomeState(state: TwinCardsState) { - setupWelcomeState(state, TwinCardsAction.SetStepOfScreen(TwinCardsStep.CreateFirstWallet)) + setupWelcomeState(state) { store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.CreateFirstWallet)) } } - private fun setupWelcomeState(state: TwinCardsState, mainAction: Action) { + private fun setupWelcomeState(state: TwinCardsState, mainAction: VoidCallback) { twinsWidget.toWelcome(false) { startPostponedEnterTransition() } onboarding_twins_welcome_bg.show() @@ -167,7 +169,7 @@ class TwinsCardsFragment : BaseOnboardingFragment() { tv_body.text = getString(R.string.twins_onboarding_description_format, state.cardNumber?.pairIndexNumber()) btn_main_action.setText(R.string.common_continue) - btn_main_action.setOnClickListener { store.dispatch(mainAction) } + btn_main_action.setOnClickListener { mainAction() } } private fun setupWarningState(state: TwinCardsState) { From e51220365e969efd0bdc41c96e6bdf788477349f Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 1 Nov 2021 15:08:51 +0300 Subject: [PATCH 125/189] Updated on 2026-08-14 --- app/build.gradle | 5 ++--- app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 0b53a8df5e..bdc8173275 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -71,14 +71,13 @@ dependencies { implementation 'androidx.fragment:fragment-ktx:1.3.6' implementation 'androidx.constraintlayout:constraintlayout:2.1.1' implementation 'com.google.android.material:material:1.4.0' - implementation "androidx.core:core-ktx:1.6.0" implementation 'com.google.android.play:core:1.10.2' 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-42' - implementation 'com.tangem.tangem-sdk-kotlin:core:develop-91' - implementation 'com.tangem.tangem-sdk-kotlin:android:develop-91' + implementation 'com.tangem.tangem-sdk-kotlin:core:develop-92' + implementation 'com.tangem.tangem-sdk-kotlin:android:develop-92' // WebView implementation "androidx.browser:browser:1.3.0" 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 3a3acc1a87..27776c28b7 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -125,6 +125,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co companion object { val config = Config( + allowUntrustedCards = true, filter = CardFilter( allowedCardTypes = FirmwareVersion.FirmwareType.values().toList() ) From bc00c1f06c8173fc3cdcec4a59822244e96fd92c Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 2 Nov 2021 18:45:37 +0300 Subject: [PATCH 126/189] Updated on 2026-08-14 --- app/src/main/java/com/tangem/tap/common/extensions/Store.kt | 4 ++-- .../tap/features/wallet/redux/middlewares/WalletMiddleware.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) 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 17db11e102..50087bbf93 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) { - store.state.globalState.tapWalletManager.onCardScanned(scanResponse) +suspend fun Store<*>.onCardScanned(scanResponse: ScanResponse, addAnalytics: Boolean = true) { + store.state.globalState.tapWalletManager.onCardScanned(scanResponse, addAnalytics) } fun Store<*>.dispatchOpenUrl(url: String) { 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 9ec6e10f13..376a38136b 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 @@ -101,7 +101,7 @@ class WalletMiddleware { when (result) { is CompletionResult.Success -> { val scanNoteResponse = globalState.scanResponse?.copy(card = result.data) - scanNoteResponse?.let { store.onCardScanned(scanNoteResponse) } + scanNoteResponse?.let { store.onCardScanned(scanNoteResponse, false) } } is CompletionResult.Failure -> { (result.error as? TangemSdkError)?.let { error -> From 26b07ae924de32a75a84deeacd18778c8360f7c1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 2 Nov 2021 19:11:42 +0300 Subject: [PATCH 127/189] 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 bdc8173275..d08be40ffd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -75,7 +75,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-42' + implementation 'com.tangem:blockchain:develop-43' implementation 'com.tangem.tangem-sdk-kotlin:core:develop-92' implementation 'com.tangem.tangem-sdk-kotlin:android:develop-92' From 87638025cf6a12ff127e80f4594aeb5c4c3f96c9 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 2 Nov 2021 23:39:24 +0300 Subject: [PATCH 128/189] Updated on 2026-08-14 --- .../tap/features/send/redux/middlewares/AmountMiddleware.kt | 4 ++-- .../features/send/redux/middlewares/RequestFeeMiddleware.kt | 4 ++-- .../tap/features/send/redux/middlewares/SendMiddleware.kt | 2 +- .../com/tangem/tap/features/send/redux/reducers/FeeReducer.kt | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt index dcf3bf5a43..3d59d0a270 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt @@ -68,9 +68,9 @@ class AmountMiddleware { } else { val amountErrors = extractErrorsForAmountField(transactionErrors) if (amountErrors.isNotEmpty()) { + transactionErrors.removeAll(amountErrors) dispatch(AmountAction.SetAmountError(createValidateTransactionError(amountErrors, walletManager))) } - transactionErrors.removeAll(amountErrors) if (transactionErrors.isNotEmpty()) { dispatch(SendAction.SendError(createValidateTransactionError(transactionErrors, walletManager))) } @@ -84,7 +84,7 @@ class AmountMiddleware { dispatch(AmountAction.SetAmount(sendState.amountState.balanceCrypto, false)) if (sendState.amountState.isCoinAmount()) { - dispatch(FeeAction.ChangeLayoutVisibility(main = true, controls = true, chipGroup = true)) + dispatch(FeeAction.ChangeLayoutVisibility(main = true, controls = true)) dispatch(FeeActionUi.ChangeIncludeFee(true)) } diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/RequestFeeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/RequestFeeMiddleware.kt index b2523e9e7a..04d5b420d5 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/RequestFeeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/RequestFeeMiddleware.kt @@ -29,7 +29,7 @@ class RequestFeeMiddleware { if (!SendState.isReadyToRequestFee()) { dispatch(FeeAction.FeeCalculation.SetFeeError(FeeAction.Error.ADDRESS_OR_AMOUNT_IS_EMPTY)) - dispatch(FeeAction.ChangeLayoutVisibility(main = false, chipGroup = true)) +// dispatch(FeeAction.ChangeLayoutVisibility(main = false, chipGroup = true)) dispatch(ReceiptAction.RefreshReceipt) dispatch(SendAction.ChangeSendButtonState(sendState.getButtonState())) return @@ -60,7 +60,7 @@ class RequestFeeMiddleware { } is Result.Failure -> { dispatch(FeeAction.FeeCalculation.SetFeeError(FeeAction.Error.REQUEST_FAILED)) - dispatch(FeeAction.ChangeLayoutVisibility(main = false, controls = false, chipGroup = false)) + dispatch(FeeAction.ChangeLayoutVisibility(main = false)) } } dispatch(AmountActionUi.CheckAmountToSend) 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 26d2f8dd27..5f3e43fd59 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 @@ -254,7 +254,7 @@ fun extractErrorsForAmountField(errors: EnumSet): EnumSet { - val notAcceptable = listOf(TransactionError.FeeExceedsBalance, TransactionError.FeeExceedsBalance) + val notAcceptable = listOf(TransactionError.AmountExceedsBalance, TransactionError.FeeExceedsBalance) if (!showIntoAmountField.containsAll(notAcceptable)) showIntoAmountField.add(it) } TransactionError.InvalidAmountValue -> showIntoAmountField.add(it) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/FeeReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/FeeReducer.kt index f5da58ab54..d0a1a13072 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/FeeReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/FeeReducer.kt @@ -45,7 +45,7 @@ class FeeReducer : SendInternalReducer { state.copy( mainLayoutIsVisible = getVisibility(state.mainLayoutIsVisible, action.main), controlsLayoutIsVisible = getVisibility(state.controlsLayoutIsVisible, action.controls), - feeChipGroupIsVisible = getVisibility(state.mainLayoutIsVisible, action.chipGroup) + feeChipGroupIsVisible = getVisibility(state.feeChipGroupIsVisible, action.chipGroup) ) } is FeeAction.FeeCalculation.SetFeeResult -> { From 8dd2e578b4f9c92633e9a478da8c4662f8df5218 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 3 Nov 2021 10:24:09 +0300 Subject: [PATCH 129/189] Updated on 2026-08-14 --- .../com/tangem/tap/domain/TapWalletManager.kt | 8 +-- .../tap/domain/tokens/CurrenciesRepository.kt | 50 +++++++++---------- .../features/tokens/redux/TokensReducer.kt | 4 +- 3 files changed, 31 insertions(+), 31 deletions(-) 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 14f7b50b6b..7c2ec7f173 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -157,22 +157,22 @@ class TapWalletManager { private fun loadMultiWalletData( card: Card, primaryBlockchain: Blockchain?, primaryWalletManager: WalletManager? ) { - val primaryTokens = primaryWalletManager?.cardTokens ?: emptySet() + val primaryTokens = primaryWalletManager?.cardTokens?.toList() ?: emptyList() val savedCurrencies = currenciesRepository.loadCardCurrencies(card.cardId) if (savedCurrencies == null) { if (primaryBlockchain != null && primaryWalletManager != null) { store.dispatch(WalletAction.MultiWallet.SaveCurrencies( CardCurrencies( - blockchains = setOf(primaryBlockchain), tokens = primaryTokens + blockchains = listOf(primaryBlockchain), tokens = primaryTokens ))) store.dispatch(WalletAction.MultiWallet.AddWalletManagers(primaryWalletManager)) store.dispatch(WalletAction.MultiWallet.AddBlockchains(listOf(primaryBlockchain))) store.dispatch(WalletAction.MultiWallet.AddTokens(primaryTokens.toList())) } else { - val blockchains = setOf(Blockchain.Bitcoin, Blockchain.Ethereum) + val blockchains = listOf(Blockchain.Bitcoin, Blockchain.Ethereum) store.dispatch(WalletAction.MultiWallet.SaveCurrencies( - CardCurrencies(blockchains = blockchains, tokens = emptySet()) + CardCurrencies(blockchains = blockchains, tokens = emptyList()) )) val walletManagers = walletManagerFactory.makeWalletManagersForApp(card, blockchains.toList()) 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 26aa55369c..18ea01ebbc 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 @@ -14,14 +14,14 @@ import com.tangem.tap.network.createMoshi class CurrenciesRepository(val context: Application) { private val moshi = createMoshi() - private val blockchainsAdapter: JsonAdapter> = moshi.adapter( - Types.newParameterizedType(Set::class.java, Blockchain::class.java) + private val blockchainsAdapter: JsonAdapter> = moshi.adapter( + Types.newParameterizedType(List::class.java, Blockchain::class.java) ) - private val tokensAdapter: JsonAdapter> = moshi.adapter( - Types.newParameterizedType(Set::class.java, TokenDao::class.java) + private val tokensAdapter: JsonAdapter> = moshi.adapter( + Types.newParameterizedType(List::class.java, TokenDao::class.java) ) - private val obsoleteTokensAdapter: JsonAdapter> = moshi.adapter( - Types.newParameterizedType(Set::class.java, ObsoleteTokenDao::class.java) + private val obsoleteTokensAdapter: JsonAdapter> = moshi.adapter( + Types.newParameterizedType(List::class.java, ObsoleteTokenDao::class.java) ) fun loadCardCurrencies(cardId: String): CardCurrencies? { @@ -51,48 +51,48 @@ class CurrenciesRepository(val context: Application) { } fun removeToken(cardId: String, token: Token) { - val tokens = loadSavedTokens(cardId).filterNot { it == token }.toSet() + val tokens = loadSavedTokens(cardId).filterNot { it == token } saveTokens(cardId, tokens) } fun removeBlockchain(cardId: String, blockchain: Blockchain) { - val blockchains = loadSavedBlockchains(cardId).filterNot { it == blockchain }.toSet() + val blockchains = loadSavedBlockchains(cardId).filterNot { it == blockchain } saveBlockchains(cardId, blockchains) } - private fun loadSavedTokens(cardId: String): Set { + private fun loadSavedTokens(cardId: String): List { val json = try { context.readFileText(getFileNameForTokens(cardId)) } catch (exception: Exception) { - return emptySet() + return emptyList() } return try { - tokensAdapter.fromJson(json)!!.map { it.toToken() }.toSet() + tokensAdapter.fromJson(json)!!.map { it.toToken() } } catch (exception: Exception) { try { - obsoleteTokensAdapter.fromJson(json)!!.map { it.toToken() }.toSet() + obsoleteTokensAdapter.fromJson(json)!!.map { it.toToken() } } catch (exception: Exception) { - emptySet() + emptyList() } } } - private fun saveTokens(cardId: String, tokens: Set) { - val json = tokensAdapter.toJson(tokens.map { TokenDao.fromToken(it) }.toSet()) + private fun saveTokens(cardId: String, tokens: List) { + val json = tokensAdapter.toJson(tokens.map { TokenDao.fromToken(it) }.distinct()) context.rewriteFile(json, getFileNameForTokens(cardId)) } - private fun loadSavedBlockchains(cardId: String): Set { + private fun loadSavedBlockchains(cardId: String): List { return try { val json = context.readFileText(getFileNameForBlockchains(cardId)) - blockchainsAdapter.fromJson(json) ?: emptySet() + blockchainsAdapter.fromJson(json) ?: emptyList() } catch (exception: Exception) { - emptySet() + emptyList() } } - private fun saveBlockchains(cardId: String, blockchains: Set) { + private fun saveBlockchains(cardId: String, blockchains: List) { val json = blockchainsAdapter.toJson(blockchains) context.rewriteFile(json, getFileNameForBlockchains(cardId)) } @@ -224,12 +224,12 @@ data class ObsoleteTokenDao( @JsonClass(generateAdapter = true) data class CardCurrenciesDao( - val tokens: Set, - val blockchains: Set, + val tokens: List, + val blockchains: List, ) { fun toCardCurrencies(): CardCurrencies { return CardCurrencies( - tokens = tokens.map { it.toToken() }.toSet(), + tokens = tokens.map { it.toToken() }.distinct(), blockchains = blockchains ) } @@ -237,7 +237,7 @@ data class CardCurrenciesDao( companion object { fun fromCardCurrencies(cardCurrencies: CardCurrencies): CardCurrenciesDao { return CardCurrenciesDao( - tokens = cardCurrencies.tokens.map { TokenDao.fromToken(it) }.toSet(), + tokens = cardCurrencies.tokens.map { TokenDao.fromToken(it) }.distinct(), blockchains = cardCurrencies.blockchains ) } @@ -245,6 +245,6 @@ data class CardCurrenciesDao( } data class CardCurrencies( - val tokens: Set, - val blockchains: Set, + val tokens: List, + val blockchains: List, ) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensReducer.kt b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensReducer.kt index 66a7dd928b..878eed1113 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensReducer.kt @@ -33,7 +33,7 @@ private fun internalReduce(action: Action, state: AppState): TokensState { } private fun List.toCardCurrencies(): CardCurrencies { - val tokens = mapNotNull { (it.currency as? Currency.Token)?.token }.toSet() - val blockchains = mapNotNull { (it.currency as? Currency.Blockchain)?.blockchain }.toSet() + val tokens = mapNotNull { (it.currency as? Currency.Token)?.token }.distinct() + val blockchains = mapNotNull { (it.currency as? Currency.Blockchain)?.blockchain }.distinct() return CardCurrencies(tokens = tokens, blockchains = blockchains) } \ No newline at end of file From 66217c1493c0109694a62302d3f0d99111048800 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 3 Nov 2021 10:28:03 +0300 Subject: [PATCH 130/189] Updated on 2026-08-14 --- .../tap/features/wallet/redux/WalletState.kt | 25 ++++++++++++-- .../wallet/ui/adapters/WalletAdapter.kt | 33 ++----------------- 2 files changed, 24 insertions(+), 34 deletions(-) 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 54e7f12586..e766e87035 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 @@ -132,12 +132,31 @@ data class WalletState( fun replaceWalletInWallets(walletData: WalletData?): List { if (walletData == null) return wallets - return wallets.filter { it.currencyData.currency != walletData.currencyData.currency } + walletData + var changed = false + val updatedWallets = wallets.map { + if (it.currencyData.currency == walletData.currencyData.currency) { + changed = true + walletData + } else { + it + } + } + return if (changed) updatedWallets else wallets + walletData } fun replaceSomeWallets(newWallets: List): List { - val currencies = newWallets.map { it.currencyData.currency } - return wallets.filter { !currencies.contains(it.currencyData.currency) } + newWallets + val remainingWallets: MutableList = newWallets.toMutableList() + val updatedWallets = wallets.map { wallet -> + val newWallet = newWallets + .firstOrNull { wallet.currencyData.currency == it.currencyData.currency } + if (newWallet == null) { + wallet + } else { + remainingWallets.remove(newWallet) + newWallet + } + } + return updatedWallets + remainingWallets } fun addWalletManagers(newWalletManagers: List): WalletState { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt index b90adc2cb6..f804f6acef 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt @@ -18,7 +18,6 @@ import com.tangem.tap.features.wallet.ui.BalanceStatus import com.tangem.tap.store import com.tangem.wallet.R import kotlinx.android.synthetic.main.item_currency_wallet.view.* -import java.math.BigDecimal class WalletAdapter : ListAdapter(DiffUtilCallback) { @@ -28,36 +27,8 @@ class WalletAdapter } fun submitList(list: List, primaryBlockchain: Blockchain?, primaryToken: Token? = null) { - val listModified = list.toMutableList() - val primaryBlockchainWallet = when ( - val index = listModified.indexOfFirst { - (it.currency as? Currency.Blockchain)?.blockchain == primaryBlockchain - } - ) { - -1 -> null - else -> listModified.removeAt(index) - } - - - val primaryTokenWallet = if (primaryToken == null) null else when ( - val index = listModified.indexOfFirst { (it.currency as? Currency.Token)?.token == primaryToken } - ) { - -1 -> null - else -> listModified.removeAt(index) - } - - if (list.all { it.currencyData.fiatAmountFormatted == null }) { - val sortedList = listOfNotNull(primaryBlockchainWallet, primaryTokenWallet) + listModified - super.submitList(sortedList) - return - } - - val sorted = listModified.sortedWith( - compareByDescending { it.currencyData.fiatAmount ?: BigDecimal.ZERO } - .thenBy { it.currencyData.currency } - ) - val sortedList = listOfNotNull(primaryBlockchainWallet, primaryTokenWallet) + sorted - super.submitList(sortedList) + // We used this method to sort the list of currencies. Sorting is disabled for now. + super.submitList(list) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WalletsViewHolder { From c58966bcca268f0a7a157db9f53441c4017ed633 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 3 Nov 2021 11:33:17 +0300 Subject: [PATCH 131/189] Updated on 2026-08-14 --- app/src/main/res/values/strings_untranslated.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml index f189ef8196..314565b97f 100644 --- a/app/src/main/res/values/strings_untranslated.xml +++ b/app/src/main/res/values/strings_untranslated.xml @@ -89,6 +89,7 @@ ex. USD Coin ex. USDC Added + Remove token //Details Blockchain From 74d04cde075fd2f80a331cf67f0f0220de951dfe Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 3 Nov 2021 11:34:53 +0300 Subject: [PATCH 132/189] Updated on 2026-08-14 --- .../wallet/ui/WalletDetailsFragment.kt | 6 ++--- .../wallet/ui/wallet/SingleWalletView.kt | 24 +++++++++++++++---- .../drawable/ic_arrow_down_short_button.xml | 12 ++++++++++ .../res/layout/fragment_wallet_details.xml | 6 ++--- .../layout/layout_wallet_short_buttons.xml | 9 ++++--- app/src/main/res/menu/wallet_details.xml | 4 ++-- 6 files changed, 45 insertions(+), 16 deletions(-) create mode 100644 app/src/main/res/drawable/ic_arrow_down_short_button.xml 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 f2072f7cf3..9a52c58948 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 @@ -84,7 +84,7 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS btn_share.setOnClickListener { store.dispatch(WalletAction.ShowDialog.QrCode) } - btn_top_up.setOnClickListener { store.dispatch(WalletAction.TradeCryptoAction.Buy) } + btn_trade.setOnClickListener { store.dispatch(WalletAction.TradeCryptoAction.Buy) } btn_sell.setOnClickListener { store.dispatch(WalletAction.TradeCryptoAction.Sell) } } @@ -138,7 +138,7 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS srl_wallet_details.isRefreshing = false } - btn_top_up.isEnabled = selectedWallet.tradeCryptoState.buyingAllowed + btn_trade.isEnabled = selectedWallet.tradeCryptoState.buyingAllowed btn_sell.show(selectedWallet.tradeCryptoState.sellingAllowed) } @@ -286,7 +286,7 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { - R.id.meny_remove -> { + R.id.menu_remove -> { store.state.walletState.getSelectedWalletData()?.let { walletData -> store.dispatch(WalletAction.MultiWallet.RemoveWallet(walletData)) store.dispatch(WalletAction.MultiWallet.SelectWallet(null)) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt index 2522ba7135..874c3abfec 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt @@ -142,12 +142,10 @@ class SingleWalletView : WalletView { } btn_show_qr.setOnClickListener { store.dispatch(WalletAction.ShowDialog.QrCode) } - btn_top_up.setOnClickListener { - tradeCryptoAction(state.tradeCryptoState) - } + setupTradeButton(fragment, state.tradeCryptoState) } - private fun tradeCryptoAction(tradeCryptoState: TradeCryptoState) { + private fun setupTradeButton(fragment: WalletFragment, tradeCryptoState: TradeCryptoState) { val allowedToBuy = tradeCryptoState.buyingAllowed val allowedToSell = tradeCryptoState.sellingAllowed val action = when { @@ -156,7 +154,23 @@ class SingleWalletView : WalletView { allowedToBuy && allowedToSell -> WalletAction.ShowDialog.ChooseTradeActionDialog else -> null } - if (action != null) store.dispatch(action) + val text = when { + allowedToBuy && !allowedToSell -> R.string.wallet_button_buy + !allowedToBuy && allowedToSell -> R.string.wallet_button_sell + allowedToBuy && allowedToSell -> R.string.wallet_button_trade + else -> R.string.wallet_button_trade + } + val icon = when { + allowedToBuy && !allowedToSell -> R.drawable.ic_arrow_up_short_btn + !allowedToBuy && allowedToSell -> R.drawable.ic_arrow_down_short_button + allowedToBuy && allowedToSell -> R.drawable.ic_arrows_up_down_short_btn + else -> R.string.wallet_button_trade + } + with(fragment) { + btn_trade.text = getText(text) + btn_trade.setCompoundDrawablesWithIntrinsicBounds(0, icon, 0, 0) + btn_trade.setOnClickListener { if (action != null) store.dispatch(action) } + } } private fun setupButtonsType(state: WalletData, fragment: WalletFragment) = with(fragment) { diff --git a/app/src/main/res/drawable/ic_arrow_down_short_button.xml b/app/src/main/res/drawable/ic_arrow_down_short_button.xml new file mode 100644 index 0000000000..18b58e3f6c --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_down_short_button.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/app/src/main/res/layout/fragment_wallet_details.xml b/app/src/main/res/layout/fragment_wallet_details.xml index 7b61f56d8d..3c69d924f7 100644 --- a/app/src/main/res/layout/fragment_wallet_details.xml +++ b/app/src/main/res/layout/fragment_wallet_details.xml @@ -131,7 +131,7 @@ app:constraint_referenced_ids="l_wallet_details,card_pending_transaction_warning" /> + app:constraint_referenced_ids="btn_trade, btn_sell" /> diff --git a/app/src/main/res/layout/layout_wallet_short_buttons.xml b/app/src/main/res/layout/layout_wallet_short_buttons.xml index be494ee95b..61217164ee 100644 --- a/app/src/main/res/layout/layout_wallet_short_buttons.xml +++ b/app/src/main/res/layout/layout_wallet_short_buttons.xml @@ -9,6 +9,7 @@ android:id="@+id/btn_scan_short" style="@style/TapBlackButton" android:layout_width="0dp" + android:layout_height="52dp" android:layout_marginStart="16dp" android:layout_marginTop="30dp" android:layout_marginBottom="33dp" @@ -17,14 +18,15 @@ android:paddingTop="7dp" android:text="@string/wallet_button_scan" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toStartOf="@id/btn_top_up" + app:layout_constraintEnd_toStartOf="@id/btn_trade" app:layout_constraintStart_toStartOf="parent" app:layout_constraintVertical_bias="1" /> \ No newline at end of file diff --git a/app/src/main/res/menu/wallet_details.xml b/app/src/main/res/menu/wallet_details.xml index 9622311e82..08ba8df389 100644 --- a/app/src/main/res/menu/wallet_details.xml +++ b/app/src/main/res/menu/wallet_details.xml @@ -2,7 +2,7 @@ \ No newline at end of file From 8f5808df689661cddb458ed55a0fcb60cad5fb31 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 3 Nov 2021 11:41:10 +0300 Subject: [PATCH 133/189] Updated on 2026-08-14 --- .../java/com/tangem/tap/features/details/ui/DetailsFragment.kt | 2 ++ 1 file changed, 2 insertions(+) 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 cb51e326dd..ac9be2e1a0 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 @@ -10,6 +10,7 @@ import com.tangem.tap.common.extensions.show import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction +import com.tangem.tap.domain.isMultiwalletAllowed import com.tangem.tap.domain.twins.getTwinCardIdForUser import com.tangem.tap.features.details.redux.DetailsAction import com.tangem.tap.features.details.redux.DetailsState @@ -112,6 +113,7 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber Date: Wed, 3 Nov 2021 12:40:18 +0300 Subject: [PATCH 134/189] Updated on 2026-08-14 --- app/src/main/res/layout/fragment_details.xml | 183 ++++++++++--------- 1 file changed, 92 insertions(+), 91 deletions(-) diff --git a/app/src/main/res/layout/fragment_details.xml b/app/src/main/res/layout/fragment_details.xml index 70c51c9eb3..90df722e29 100644 --- a/app/src/main/res/layout/fragment_details.xml +++ b/app/src/main/res/layout/fragment_details.xml @@ -43,27 +43,46 @@ android:layout_marginEnd="16dp" android:layout_marginBottom="33dp"> + + + + + app:layout_constraintTop_toBottomOf="@id/tv_card_title" /> + + + + + - - + app:layout_constraintTop_toBottomOf="@id/tv_security_title" /> + app:layout_constraintTop_toBottomOf="@id/tv_erase_wallet" /> + @@ -187,6 +222,37 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tv_settings_title" /> + + + + + app:layout_constraintTop_toBottomOf="@id/tv_card_tou" /> - - - - - - - - - - - - - From cbebea5690122ab50852fd4d5b97bcb621056cc8 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 3 Nov 2021 13:00:34 +0300 Subject: [PATCH 135/189] Updated on 2026-08-14 --- .../tap/features/wallet/ui/WalletDetailsFragment.kt | 8 +++++--- app/src/main/res/color/menu_item_color.xml | 5 +++++ app/src/main/res/layout/fragment_wallet_details.xml | 1 + app/src/main/res/values/styles.xml | 8 ++++++++ 4 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 app/src/main/res/color/menu_item_color.xml 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 9a52c58948..9a4051f137 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 @@ -300,9 +300,11 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - if (store.state.walletState.canBeRemoved(store.state.walletState.getSelectedWalletData())) { - inflater.inflate(R.menu.wallet_details, menu) - } + inflater.inflate(R.menu.wallet_details, menu) + val walletCanBeRemoved = store.state.walletState.canBeRemoved( + store.state.walletState.getSelectedWalletData() + ) + menu.getItem(0).isEnabled = walletCanBeRemoved } } diff --git a/app/src/main/res/color/menu_item_color.xml b/app/src/main/res/color/menu_item_color.xml new file mode 100644 index 0000000000..f8d7307051 --- /dev/null +++ b/app/src/main/res/color/menu_item_color.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_wallet_details.xml b/app/src/main/res/layout/fragment_wallet_details.xml index 3c69d924f7..27da2ac50f 100644 --- a/app/src/main/res/layout/fragment_wallet_details.xml +++ b/app/src/main/res/layout/fragment_wallet_details.xml @@ -23,6 +23,7 @@ android:layout_height="?attr/actionBarSize" app:menu="@menu/wallet_details" app:navigationIcon="@drawable/ic_baseline_arrow_back_24" + android:theme="@style/ThemeOverlay.MyTheme.Toolbar" app:title=" " /> diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 43173d1bad..286cddae59 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -20,6 +20,14 @@ @android:color/white + + +