From 3591aeffda49e2ea6bdedf45547f7938127fcbae Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 26 Apr 2021 10:20:08 +0300 Subject: [PATCH 01/83] Updated on 2026-08-14 --- app/src/main/assets/tangem-android-config | 1 - app/src/main/assets/tangem-app-config | 1 + app/src/main/java/com/tangem/tap/domain/configurable/Loader.kt | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) delete mode 160000 app/src/main/assets/tangem-android-config create mode 160000 app/src/main/assets/tangem-app-config diff --git a/app/src/main/assets/tangem-android-config b/app/src/main/assets/tangem-android-config deleted file mode 160000 index cbdda0d566..0000000000 --- a/app/src/main/assets/tangem-android-config +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cbdda0d566da0dd1baa534fd5ce815ce7c88cea0 diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config new file mode 160000 index 0000000000..415eb6e9cc --- /dev/null +++ b/app/src/main/assets/tangem-app-config @@ -0,0 +1 @@ +Subproject commit 415eb6e9ccfcdc3573391b869adeba433f790087 diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/Loader.kt b/app/src/main/java/com/tangem/tap/domain/configurable/Loader.kt index 3471ed78ee..873b71c500 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/Loader.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/Loader.kt @@ -10,7 +10,7 @@ interface Loader { companion object { const val featuresName = "features_${BuildConfig.CONFIG_ENVIRONMENT}" - const val configValuesName = "tangem-android-config/config_${BuildConfig.CONFIG_ENVIRONMENT}" + const val configValuesName = "tangem-app-config/config_${BuildConfig.CONFIG_ENVIRONMENT}" const val warnings = "warnings_${BuildConfig.CONFIG_ENVIRONMENT}" } } \ No newline at end of file From cc8e3809619416114a28aaaa2dc455629796c5b1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 24 Jun 2021 12:18:31 +0300 Subject: [PATCH 02/83] 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 03/83] 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 04/83] 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 05/83] 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 06/83] 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 07/83] 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 08/83] 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 09/83] 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 10/83] 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 11/83] 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 12/83] 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 13/83] 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 14/83] 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 15/83] 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 16/83] 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 17/83] 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 18/83] 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 19/83] 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 20/83] 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 21/83] 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 22/83] 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 23/83] 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 24/83] 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 25/83] 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 26/83] 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 27/83] 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 28/83] 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 29/83] 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 30/83] 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 31/83] 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 32/83] 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 33/83] 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 34/83] 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 35/83] 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 36/83] 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 37/83] 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 38/83] 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 39/83] 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 40/83] 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 41/83] 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 42/83] 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 43/83] 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 44/83] 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 45/83] 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 46/83] 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 47/83] 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 48/83] 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 49/83] 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 50/83] 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 51/83] 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 52/83] 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 53/83] 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 54/83] 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 55/83] 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 56/83] 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 57/83] 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 58/83] 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 59/83] 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 60/83] 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 61/83] 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 62/83] 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 63/83] 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 64/83] 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 65/83] 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 66/83] 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 67/83] 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 68/83] 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 69/83] 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 70/83] 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 71/83] 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 72/83] 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 73/83] 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 74/83] 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 75/83] 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 76/83] 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 d087c388e253762d8df435a4c969fc1f4975250a Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 22 Oct 2021 03:45:54 +0300 Subject: [PATCH 79/83] 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 0acf130492776f0dd187c314e656c860b9c42e3c Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 22 Oct 2021 12:19:51 +0300 Subject: [PATCH 80/83] 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 81/83] 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 82/83] 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 61cf84a62650ee7e308806b8309e59a0e241e7ea Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 25 Oct 2021 15:44:05 +0300 Subject: [PATCH 83/83] 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"