Updated on 2026-08-14
This commit is contained in:
commit
2a36bb65b5
262 changed files with 6989 additions and 2102 deletions
|
|
@ -119,6 +119,7 @@ dependencies {
|
||||||
implementation(projects.domain.card)
|
implementation(projects.domain.card)
|
||||||
implementation(projects.domain.demo)
|
implementation(projects.domain.demo)
|
||||||
implementation(projects.domain.demo.models)
|
implementation(projects.domain.demo.models)
|
||||||
|
implementation(projects.domain.express)
|
||||||
implementation(projects.domain.wallets)
|
implementation(projects.domain.wallets)
|
||||||
implementation(projects.domain.wallets.models)
|
implementation(projects.domain.wallets.models)
|
||||||
implementation(projects.domain.settings)
|
implementation(projects.domain.settings)
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,10 @@ object TestConstants {
|
||||||
|
|
||||||
const val RECIPIENT_ADDRESS = "0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0"
|
const val RECIPIENT_ADDRESS = "0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0"
|
||||||
const val BITCOIN_ADDRESS = "bc1qtg9aa6jcpqtvun0pe0uct7sxm8nq2nsxfmfxm3"
|
const val BITCOIN_ADDRESS = "bc1qtg9aa6jcpqtvun0pe0uct7sxm8nq2nsxfmfxm3"
|
||||||
const val CARDANO_ADDRESS = "addr1q8f9499e58k4hhfd9vhawprxt3xd94x7rmlyp33ee4xkatakcl2zgkrg0p6ceqkndtkw4cumfe9enhdph8yhuswn785srksm9p"
|
const val CARDANO_ADDRESS =
|
||||||
|
"addr1q8f9499e58k4hhfd9vhawprxt3xd94x7rmlyp33ee4xkatakcl2zgkrg0p6ceqkndtkw4cumfe9enhdph8yhuswn785srksm9p"
|
||||||
|
const val SOLANA_RECIPIENT_ADDRESS = "5fcy9woa8Di1QHcce65CsV3XKrxdB2pD4HJx5xx82ipM"
|
||||||
|
const val POLKADOT_RECIPIENT_ADDRESS = "143TfgFYAFfM86LRzt4UcFNU3KosxCndBCVz2U5HCxpLidKZ"
|
||||||
|
|
||||||
const val WAIT_UNTIL_TIMEOUT = 20_000L
|
const val WAIT_UNTIL_TIMEOUT = 20_000L
|
||||||
const val WAIT_UNTIL_TIMEOUT_LONG = 30_000L
|
const val WAIT_UNTIL_TIMEOUT_LONG = 30_000L
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,12 @@ fun BaseTestCase.swipeVertical(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun BaseTestCase.pullToRefresh() {
|
fun BaseTestCase.pullToRefresh(steps: Int = 1000) {
|
||||||
swipeVertical(
|
swipeVertical(
|
||||||
direction = SwipeDirection.DOWN,
|
direction = SwipeDirection.DOWN,
|
||||||
startHeightRatio = 0.2f,
|
startHeightRatio = 0.2f,
|
||||||
endHeightRatio = 0.8f,
|
endHeightRatio = 0.8f,
|
||||||
steps = 1000
|
steps = steps
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -116,3 +116,27 @@ fun BaseTestCase.checkMultiCurrencyMainScreen(
|
||||||
onMainScreen { organizeTokensButton().assertIsDisplayed() }
|
onMainScreen { organizeTokensButton().assertIsDisplayed() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun BaseTestCase.assertActionButtonsForMultiCurrencyWallet(isEnabled: Boolean = true) {
|
||||||
|
if (isEnabled) {
|
||||||
|
step("Assert 'Buy' button is enabled") {
|
||||||
|
onMainScreen { buyButton.assertIsEnabled() }
|
||||||
|
}
|
||||||
|
step("Assert 'Swap' button is enabled") {
|
||||||
|
onMainScreen { swapButton.assertIsEnabled() }
|
||||||
|
}
|
||||||
|
step("Assert 'Sell' button is enabled") {
|
||||||
|
onMainScreen { sellButton.assertIsEnabled() }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
step("Assert 'Buy' button is not enabled") {
|
||||||
|
onMainScreen { buyButton.assertIsNotEnabled() }
|
||||||
|
}
|
||||||
|
step("Assert 'Swap' button is not enabled") {
|
||||||
|
onMainScreen { swapButton.assertIsNotEnabled() }
|
||||||
|
}
|
||||||
|
step("Assert 'Sell' button is not enabled") {
|
||||||
|
onMainScreen { sellButton.assertIsNotEnabled() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ import com.tangem.screens.AlreadyUsedWalletDialogPageObject.requestSupportButton
|
||||||
import com.tangem.screens.AlreadyUsedWalletDialogPageObject.thisIsMyWalletButton
|
import com.tangem.screens.AlreadyUsedWalletDialogPageObject.thisIsMyWalletButton
|
||||||
import com.tangem.screens.AlreadyUsedWalletDialogPageObject.title
|
import com.tangem.screens.AlreadyUsedWalletDialogPageObject.title
|
||||||
import com.tangem.screens.ScanWarningDialogPageObject
|
import com.tangem.screens.ScanWarningDialogPageObject
|
||||||
|
import com.tangem.screens.onActionIsUnavailableDialog
|
||||||
import com.tangem.screens.onFailedTransactionDialog
|
import com.tangem.screens.onFailedTransactionDialog
|
||||||
import io.qameta.allure.kotlin.Allure.step
|
import io.qameta.allure.kotlin.Allure.step
|
||||||
|
|
||||||
|
|
@ -64,3 +65,15 @@ fun checkAlreadyUsedWalletDialog() {
|
||||||
AlreadyUsedWalletDialogPageObject { requestSupportButton.isDisplayed() }
|
AlreadyUsedWalletDialogPageObject { requestSupportButton.isDisplayed() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun BaseTestCase.checkActionIsUnavailableDialog() {
|
||||||
|
step("Assert 'Action is unavailable' dialog title is displayed") {
|
||||||
|
onActionIsUnavailableDialog { title.assertIsDisplayed() }
|
||||||
|
}
|
||||||
|
step("Assert 'Action is unavailable' dialog text is displayed") {
|
||||||
|
onActionIsUnavailableDialog { text.assertIsDisplayed() }
|
||||||
|
}
|
||||||
|
step("Assert 'Action is unavailable' dialog 'Ok' button is displayed") {
|
||||||
|
onActionIsUnavailableDialog { okButton.assertIsDisplayed() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
package com.tangem.scenarios
|
||||||
|
|
||||||
|
import com.tangem.common.BaseTestCase
|
||||||
|
import com.tangem.screens.onSendConfirmScreen
|
||||||
|
import io.github.kakaocup.compose.node.element.KNode
|
||||||
|
import io.qameta.allure.kotlin.Allure.step
|
||||||
|
|
||||||
|
fun BaseTestCase.checkSendWarning(
|
||||||
|
titleResId: Int,
|
||||||
|
messageResId: Int,
|
||||||
|
amount: String,
|
||||||
|
isDisplayed: Boolean = true,
|
||||||
|
) {
|
||||||
|
val assertDisplay = if (isDisplayed) "displayed" else "not displayed"
|
||||||
|
|
||||||
|
step("Assert 'Send confirm screen' is displayed") {
|
||||||
|
onSendConfirmScreen {
|
||||||
|
title.assertIsDisplayed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
step("Assert warning title is $assertDisplay") {
|
||||||
|
onSendConfirmScreen {
|
||||||
|
warningTitle(titleResId).assertVisibility(isDisplayed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
step("Assert warning icon is $assertDisplay") {
|
||||||
|
onSendConfirmScreen {
|
||||||
|
sendWarningIcon(messageResId, amount).assertVisibility(isDisplayed)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
step("Assert warning message is $assertDisplay") {
|
||||||
|
onSendConfirmScreen {
|
||||||
|
sendWarningMessage(messageResId, amount).assertVisibility(isDisplayed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isDisplayed)
|
||||||
|
step("Assert 'Send' button is disabled") {
|
||||||
|
onSendConfirmScreen {
|
||||||
|
sendButton.assertIsNotEnabled()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
step("Assert 'Send' button is enabled") {
|
||||||
|
onSendConfirmScreen {
|
||||||
|
sendButton.assertIsEnabled()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KNode.assertVisibility(shouldBeDisplayed: Boolean) {
|
||||||
|
if (shouldBeDisplayed) {
|
||||||
|
assertIsDisplayed()
|
||||||
|
} else {
|
||||||
|
assertIsNotDisplayed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
package com.tangem.screens
|
||||||
|
|
||||||
|
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
|
||||||
|
import com.tangem.common.BaseTestCase
|
||||||
|
import com.tangem.core.ui.R
|
||||||
|
import com.tangem.core.ui.test.BaseButtonTestTags
|
||||||
|
import com.tangem.core.ui.test.BaseDialogTestTags
|
||||||
|
import io.github.kakaocup.compose.node.element.ComposeScreen
|
||||||
|
import io.github.kakaocup.compose.node.element.ComposeScreen.Companion.onComposeScreen
|
||||||
|
import io.github.kakaocup.compose.node.element.KNode
|
||||||
|
import io.github.kakaocup.kakao.common.utilities.getResourceString
|
||||||
|
import com.tangem.common.ui.R as CommonUIR
|
||||||
|
|
||||||
|
class ActionIsUnavailableDialogPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
||||||
|
ComposeScreen<ActionIsUnavailableDialogPageObject>(semanticsProvider = semanticsProvider) {
|
||||||
|
|
||||||
|
val dialogContainer: KNode = child {
|
||||||
|
hasTestTag(BaseDialogTestTags.CONTAINER)
|
||||||
|
}
|
||||||
|
|
||||||
|
val title: KNode = child {
|
||||||
|
hasTestTag(BaseDialogTestTags.TITLE)
|
||||||
|
hasText(getResourceString(CommonUIR.string.action_buttons_something_wrong_alert_title))
|
||||||
|
useUnmergedTree = true
|
||||||
|
}
|
||||||
|
|
||||||
|
val text: KNode = child {
|
||||||
|
hasTestTag(BaseDialogTestTags.TEXT)
|
||||||
|
hasText(getResourceString(CommonUIR.string.action_buttons_something_wrong_alert_message))
|
||||||
|
useUnmergedTree = true
|
||||||
|
}
|
||||||
|
|
||||||
|
val okButton: KNode = child {
|
||||||
|
hasTestTag(BaseButtonTestTags.BUTTON)
|
||||||
|
hasText(getResourceString(R.string.common_ok))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun BaseTestCase.onActionIsUnavailableDialog(function: ActionIsUnavailableDialogPageObject.() -> Unit) =
|
||||||
|
onComposeScreen(composeTestRule, function)
|
||||||
|
|
@ -17,6 +17,14 @@ class DialogPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
||||||
hasTestTag(BaseDialogTestTags.CONTAINER)
|
hasTestTag(BaseDialogTestTags.CONTAINER)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val title: KNode = child {
|
||||||
|
hasTestTag(BaseDialogTestTags.TITLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
val text: KNode = child {
|
||||||
|
hasTestTag(BaseDialogTestTags.TEXT)
|
||||||
|
}
|
||||||
|
|
||||||
val cancelButton: KNode = child {
|
val cancelButton: KNode = child {
|
||||||
hasTestTag(BaseButtonTestTags.BUTTON)
|
hasTestTag(BaseButtonTestTags.BUTTON)
|
||||||
hasText(getResourceString(R.string.common_cancel))
|
hasText(getResourceString(R.string.common_cancel))
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.tangem.screens
|
||||||
|
|
||||||
|
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
|
||||||
|
import com.tangem.common.BaseTestCase
|
||||||
|
import com.tangem.core.ui.R
|
||||||
|
import com.tangem.core.ui.test.*
|
||||||
|
import io.github.kakaocup.compose.node.element.ComposeScreen
|
||||||
|
import io.github.kakaocup.compose.node.element.ComposeScreen.Companion.onComposeScreen
|
||||||
|
import io.github.kakaocup.compose.node.element.KNode
|
||||||
|
import io.github.kakaocup.kakao.common.utilities.getResourceString
|
||||||
|
|
||||||
|
class SellPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
||||||
|
ComposeScreen<SellPageObject>(semanticsProvider = semanticsProvider) {
|
||||||
|
|
||||||
|
val title: KNode = child {
|
||||||
|
hasTestTag(TopAppBarTestTags.TITLE)
|
||||||
|
hasText(getResourceString(R.string.common_sell))
|
||||||
|
useUnmergedTree = true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun BaseTestCase.onSellScreen(function: SellPageObject.() -> Unit) =
|
||||||
|
onComposeScreen(composeTestRule, function)
|
||||||
|
|
@ -2,17 +2,13 @@ package com.tangem.screens
|
||||||
|
|
||||||
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
|
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
|
||||||
import com.tangem.common.BaseTestCase
|
import com.tangem.common.BaseTestCase
|
||||||
import com.tangem.core.ui.R
|
import com.tangem.core.ui.test.*
|
||||||
import com.tangem.core.ui.test.BaseButtonTestTags
|
import com.tangem.wallet.R
|
||||||
import com.tangem.core.ui.test.NotificationTestTags
|
|
||||||
import com.tangem.core.ui.test.SendConfirmScreenTestTags
|
|
||||||
import com.tangem.core.ui.test.TopAppBarTestTags
|
|
||||||
import io.github.kakaocup.compose.node.element.ComposeScreen
|
import io.github.kakaocup.compose.node.element.ComposeScreen
|
||||||
import io.github.kakaocup.compose.node.element.ComposeScreen.Companion.onComposeScreen
|
import io.github.kakaocup.compose.node.element.ComposeScreen.Companion.onComposeScreen
|
||||||
import io.github.kakaocup.compose.node.element.KNode
|
import io.github.kakaocup.compose.node.element.KNode
|
||||||
import io.github.kakaocup.kakao.common.utilities.getResourceString
|
import io.github.kakaocup.kakao.common.utilities.getResourceString
|
||||||
import androidx.compose.ui.test.hasText as withText
|
import androidx.compose.ui.test.hasText as withText
|
||||||
import com.tangem.common.ui.R as CommonUiR
|
|
||||||
|
|
||||||
class SendConfirmPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
class SendConfirmPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
||||||
ComposeScreen<SendConfirmPageObject>(semanticsProvider = semanticsProvider) {
|
ComposeScreen<SendConfirmPageObject>(semanticsProvider = semanticsProvider) {
|
||||||
|
|
@ -23,15 +19,25 @@ class SendConfirmPageObject(semanticsProvider: SemanticsNodeInteractionsProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
val sendButton: KNode = child {
|
val sendButton: KNode = child {
|
||||||
hasTestTag(BaseButtonTestTags.TEXT)
|
hasTestTag(BaseButtonTestTags.BUTTON)
|
||||||
hasText(getResourceString(R.string.common_send))
|
hasAnyDescendant(withText(getResourceString(R.string.common_send)))
|
||||||
useUnmergedTree = true
|
useUnmergedTree = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val primaryAmount: KNode = child {
|
||||||
|
hasTestTag(BaseAmountBlockTestTags.PRIMARY_AMOUNT)
|
||||||
|
useUnmergedTree = true
|
||||||
|
}
|
||||||
|
|
||||||
val minimumSendAmountErrorTitle: KNode = child {
|
fun leaveDepositButton(amount: String): KNode = child {
|
||||||
|
hasTestTag(BaseButtonTestTags.BUTTON)
|
||||||
|
hasAnyDescendant(withText(getResourceString(R.string.send_notification_leave_button, amount)))
|
||||||
|
useUnmergedTree = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun warningTitle(titleResId: Int): KNode = child {
|
||||||
hasTestTag(NotificationTestTags.TITLE)
|
hasTestTag(NotificationTestTags.TITLE)
|
||||||
hasText(getResourceString(CommonUiR.string.send_notification_invalid_amount_title))
|
hasText(getResourceString(titleResId))
|
||||||
useUnmergedTree = true
|
useUnmergedTree = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,34 +46,35 @@ class SendConfirmPageObject(semanticsProvider: SemanticsNodeInteractionsProvider
|
||||||
useUnmergedTree = true
|
useUnmergedTree = true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun minimumSendAmountErrorIcon(amount: String): KNode = child {
|
fun sendWarningIcon(messageResId: Int, amount: String): KNode = child {
|
||||||
|
hasTestTag(NotificationTestTags.ICON)
|
||||||
hasAnySibling(
|
hasAnySibling(
|
||||||
withText(
|
withText(
|
||||||
getResourceString(
|
getResourceString(
|
||||||
CommonUiR.string.send_notification_invalid_minimum_amount_text,
|
messageResId,
|
||||||
amount,
|
amount,
|
||||||
amount,
|
amount,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
)
|
)
|
||||||
hasTestTag(NotificationTestTags.ICON)
|
|
||||||
useUnmergedTree = true
|
useUnmergedTree = true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun minimumSendAmountErrorMessage(
|
fun sendWarningMessage(
|
||||||
|
messageResId: Int,
|
||||||
amount: String,
|
amount: String,
|
||||||
): KNode = child {
|
): KNode = child {
|
||||||
hasTestTag(NotificationTestTags.MESSAGE)
|
hasTestTag(NotificationTestTags.MESSAGE)
|
||||||
hasText(
|
hasText(
|
||||||
getResourceString(
|
getResourceString(
|
||||||
CommonUiR.string.send_notification_invalid_minimum_amount_text,
|
messageResId,
|
||||||
amount,
|
amount,
|
||||||
amount,
|
amount,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
useUnmergedTree = true
|
useUnmergedTree = true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun BaseTestCase.onSendConfirmScreen(function: SendConfirmPageObject.() -> Unit) =
|
internal fun BaseTestCase.onSendConfirmScreen(function: SendConfirmPageObject.() -> Unit) =
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,12 @@ class SendPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
||||||
useUnmergedTree = true
|
useUnmergedTree = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val continueButton: KNode = child {
|
||||||
|
hasTestTag(BaseButtonTestTags.TEXT)
|
||||||
|
hasText(getResourceString(SendR.string.common_continue))
|
||||||
|
useUnmergedTree = true
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun BaseTestCase.onSendScreen(function: SendPageObject.() -> Unit) =
|
internal fun BaseTestCase.onSendScreen(function: SendPageObject.() -> Unit) =
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,16 @@ import com.tangem.common.BaseTestCase
|
||||||
import com.tangem.common.constants.TestConstants.CARDANO_ADDRESS
|
import com.tangem.common.constants.TestConstants.CARDANO_ADDRESS
|
||||||
import com.tangem.common.extensions.clickWithAssertion
|
import com.tangem.common.extensions.clickWithAssertion
|
||||||
import com.tangem.common.extensions.pullToRefresh
|
import com.tangem.common.extensions.pullToRefresh
|
||||||
|
import com.tangem.common.ui.R
|
||||||
import com.tangem.common.utils.resetWireMockScenarioState
|
import com.tangem.common.utils.resetWireMockScenarioState
|
||||||
import com.tangem.common.utils.setWireMockScenarioState
|
import com.tangem.common.utils.setWireMockScenarioState
|
||||||
|
import com.tangem.scenarios.checkSendWarning
|
||||||
import com.tangem.scenarios.openMainScreen
|
import com.tangem.scenarios.openMainScreen
|
||||||
import com.tangem.scenarios.synchronizeAddresses
|
import com.tangem.scenarios.synchronizeAddresses
|
||||||
import com.tangem.screens.*
|
import com.tangem.screens.onMainScreen
|
||||||
|
import com.tangem.screens.onSendAddressScreen
|
||||||
|
import com.tangem.screens.onSendScreen
|
||||||
|
import com.tangem.screens.onTokenDetailsScreen
|
||||||
import dagger.hilt.android.testing.HiltAndroidTest
|
import dagger.hilt.android.testing.HiltAndroidTest
|
||||||
import io.qameta.allure.kotlin.AllureId
|
import io.qameta.allure.kotlin.AllureId
|
||||||
import io.qameta.allure.kotlin.junit4.DisplayName
|
import io.qameta.allure.kotlin.junit4.DisplayName
|
||||||
|
|
@ -29,6 +34,9 @@ class BlockchainTest : BaseTestCase() {
|
||||||
val scenarioName = "user_tokens_api"
|
val scenarioName = "user_tokens_api"
|
||||||
val scenarioState = "Cardano"
|
val scenarioState = "Cardano"
|
||||||
|
|
||||||
|
val invalidAmountTitleResId = R.string.send_notification_invalid_amount_title
|
||||||
|
val invalidAmountMessageResId = R.string.send_notification_invalid_minimum_amount_text
|
||||||
|
|
||||||
setupHooks(
|
setupHooks(
|
||||||
additionalAfterSection = {
|
additionalAfterSection = {
|
||||||
resetWireMockScenarioState(scenarioName)
|
resetWireMockScenarioState(scenarioName)
|
||||||
|
|
@ -64,14 +72,12 @@ class BlockchainTest : BaseTestCase() {
|
||||||
step("Click on 'Next' button") {
|
step("Click on 'Next' button") {
|
||||||
onSendAddressScreen { nextButton.clickWithAssertion() }
|
onSendAddressScreen { nextButton.clickWithAssertion() }
|
||||||
}
|
}
|
||||||
step("Assert 'Invalid amount' error title is displayed") {
|
step("Assert 'Invalid amount warning' is displayed") {
|
||||||
onSendConfirmScreen { minimumSendAmountErrorTitle.assertIsDisplayed() }
|
checkSendWarning(
|
||||||
}
|
titleResId = invalidAmountTitleResId,
|
||||||
step("Assert 'Invalid amount' error icon is displayed") {
|
messageResId = invalidAmountMessageResId,
|
||||||
onSendConfirmScreen { minimumSendAmountErrorIcon(minAmount).assertIsDisplayed() }
|
amount = minAmount
|
||||||
}
|
)
|
||||||
step("Assert 'Invalid amount' error message is displayed") {
|
|
||||||
onSendConfirmScreen { minimumSendAmountErrorMessage(minAmount).assertIsDisplayed() }
|
|
||||||
}
|
}
|
||||||
step("Press system 'Back' button") {
|
step("Press system 'Back' button") {
|
||||||
device.uiDevice.pressBack()
|
device.uiDevice.pressBack()
|
||||||
|
|
@ -97,14 +103,13 @@ class BlockchainTest : BaseTestCase() {
|
||||||
step("Click on 'Next' button") {
|
step("Click on 'Next' button") {
|
||||||
onSendAddressScreen { nextButton.clickWithAssertion() }
|
onSendAddressScreen { nextButton.clickWithAssertion() }
|
||||||
}
|
}
|
||||||
step("Assert 'Invalid amount' error title is not displayed") {
|
step("Assert 'Invalid amount warning' is not displayed") {
|
||||||
onSendConfirmScreen { minimumSendAmountErrorTitle.assertIsNotDisplayed() }
|
checkSendWarning(
|
||||||
}
|
titleResId = invalidAmountTitleResId,
|
||||||
step("Assert 'Invalid amount' error icon is not displayed") {
|
messageResId = invalidAmountMessageResId,
|
||||||
onSendConfirmScreen { minimumSendAmountErrorIcon(minAmount).assertIsNotDisplayed() }
|
amount = minAmount,
|
||||||
}
|
isDisplayed = false
|
||||||
step("Assert 'Invalid amount' error message is not displayed") {
|
)
|
||||||
onSendConfirmScreen { minimumSendAmountErrorMessage(minAmount).assertIsNotDisplayed() }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -136,10 +141,16 @@ class BlockchainTest : BaseTestCase() {
|
||||||
setWireMockScenarioState(scenarioName = userTokensScenarioName, state = userTokensScenarioState)
|
setWireMockScenarioState(scenarioName = userTokensScenarioName, state = userTokensScenarioState)
|
||||||
}
|
}
|
||||||
step("Set WireMock scenario: '$rippleAccountInfoScenarioName' to state: '$rippleAccountInfoErrorState'") {
|
step("Set WireMock scenario: '$rippleAccountInfoScenarioName' to state: '$rippleAccountInfoErrorState'") {
|
||||||
setWireMockScenarioState(scenarioName = rippleAccountInfoScenarioName, state = rippleAccountInfoErrorState)
|
setWireMockScenarioState(
|
||||||
|
scenarioName = rippleAccountInfoScenarioName,
|
||||||
|
state = rippleAccountInfoErrorState
|
||||||
|
)
|
||||||
}
|
}
|
||||||
step("Set WireMock scenario: '$rippleAccountLinesScenarioName' to state: '$rippleAccountLinesErrorState'") {
|
step("Set WireMock scenario: '$rippleAccountLinesScenarioName' to state: '$rippleAccountLinesErrorState'") {
|
||||||
setWireMockScenarioState(scenarioName = rippleAccountLinesScenarioName, state = rippleAccountLinesErrorState)
|
setWireMockScenarioState(
|
||||||
|
scenarioName = rippleAccountLinesScenarioName,
|
||||||
|
state = rippleAccountLinesErrorState
|
||||||
|
)
|
||||||
}
|
}
|
||||||
step("Open 'Main Screen'") {
|
step("Open 'Main Screen'") {
|
||||||
openMainScreen()
|
openMainScreen()
|
||||||
|
|
@ -169,10 +180,16 @@ class BlockchainTest : BaseTestCase() {
|
||||||
setWireMockScenarioState(scenarioName = userTokensScenarioName, state = userTokensScenarioState)
|
setWireMockScenarioState(scenarioName = userTokensScenarioName, state = userTokensScenarioState)
|
||||||
}
|
}
|
||||||
step("Set WireMock scenario: '$rippleAccountInfoScenarioName' to state: '$rippleAccountInfoStartedState'") {
|
step("Set WireMock scenario: '$rippleAccountInfoScenarioName' to state: '$rippleAccountInfoStartedState'") {
|
||||||
setWireMockScenarioState(scenarioName = rippleAccountInfoScenarioName, state = rippleAccountInfoStartedState)
|
setWireMockScenarioState(
|
||||||
|
scenarioName = rippleAccountInfoScenarioName,
|
||||||
|
state = rippleAccountInfoStartedState
|
||||||
|
)
|
||||||
}
|
}
|
||||||
step("Set WireMock scenario: '$rippleAccountLinesScenarioName' to state: '$rippleAccountLinesStartedState'") {
|
step("Set WireMock scenario: '$rippleAccountLinesScenarioName' to state: '$rippleAccountLinesStartedState'") {
|
||||||
setWireMockScenarioState(scenarioName = rippleAccountLinesScenarioName, state = rippleAccountLinesStartedState)
|
setWireMockScenarioState(
|
||||||
|
scenarioName = rippleAccountLinesScenarioName,
|
||||||
|
state = rippleAccountLinesStartedState
|
||||||
|
)
|
||||||
}
|
}
|
||||||
step("Pull to refresh") {
|
step("Pull to refresh") {
|
||||||
pullToRefresh()
|
pullToRefresh()
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,18 @@ import com.tangem.common.BaseTestCase
|
||||||
import com.tangem.common.constants.TestConstants.BITCOIN_ADDRESS
|
import com.tangem.common.constants.TestConstants.BITCOIN_ADDRESS
|
||||||
import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT
|
import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT
|
||||||
import com.tangem.common.extensions.clickWithAssertion
|
import com.tangem.common.extensions.clickWithAssertion
|
||||||
|
import com.tangem.common.extensions.*
|
||||||
import com.tangem.common.utils.assertClipboardTextEquals
|
import com.tangem.common.utils.assertClipboardTextEquals
|
||||||
import com.tangem.common.utils.clearClipboard
|
import com.tangem.common.utils.clearClipboard
|
||||||
|
import com.tangem.common.utils.resetWireMockScenarioState
|
||||||
|
import com.tangem.common.utils.setWireMockScenarioState
|
||||||
|
import com.tangem.scenarios.assertActionButtonsForMultiCurrencyWallet
|
||||||
|
import com.tangem.scenarios.checkActionIsUnavailableDialog
|
||||||
import com.tangem.scenarios.openMainScreen
|
import com.tangem.scenarios.openMainScreen
|
||||||
import com.tangem.scenarios.synchronizeAddresses
|
import com.tangem.scenarios.synchronizeAddresses
|
||||||
import com.tangem.screens.*
|
import com.tangem.screens.*
|
||||||
|
import com.tangem.tap.domain.sdk.mocks.MockContent
|
||||||
|
import com.tangem.tap.domain.sdk.mocks.content.TwinsMockContent
|
||||||
import dagger.hilt.android.testing.HiltAndroidTest
|
import dagger.hilt.android.testing.HiltAndroidTest
|
||||||
import io.qameta.allure.kotlin.AllureId
|
import io.qameta.allure.kotlin.AllureId
|
||||||
import io.qameta.allure.kotlin.junit4.DisplayName
|
import io.qameta.allure.kotlin.junit4.DisplayName
|
||||||
|
|
@ -391,4 +398,258 @@ class MainScreenActionButtonsTest : BaseTestCase() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@AllureId("895")
|
||||||
|
@DisplayName("Action buttons: check blockchain information by click on 'Buy' button")
|
||||||
|
@Test
|
||||||
|
fun checkClickOnBuyButtonOnMainTest() {
|
||||||
|
val cardType: MockContent = TwinsMockContent
|
||||||
|
val cardName = "Twin"
|
||||||
|
val tokenTitle = "Bitcoin"
|
||||||
|
val tokenSymbol = "BTC"
|
||||||
|
|
||||||
|
setupHooks().run {
|
||||||
|
step("Open 'Main Screen' on '$cardName' card") {
|
||||||
|
openMainScreen(mockContent = cardType, isTwinsCard = true)
|
||||||
|
}
|
||||||
|
step("Assert 'Buy' button is displayed") {
|
||||||
|
onMainScreen { buyButton.assertIsDisplayed() }
|
||||||
|
}
|
||||||
|
step("Click on 'Buy' button") {
|
||||||
|
onMainScreen { buyButton.performClick() }
|
||||||
|
}
|
||||||
|
step("Click on 'Confirm' button in 'Dialog'") {
|
||||||
|
waitForIdle()
|
||||||
|
onDialog { confirmButton.clickWithAssertion() }
|
||||||
|
}
|
||||||
|
step("Assert top app bar title contains '$tokenTitle'") {
|
||||||
|
onBuyTokenDetailsScreen { topBarTitle.assertTextContains("Buy $tokenTitle") }
|
||||||
|
}
|
||||||
|
step("Assert fiat currency text field is displayed") {
|
||||||
|
onBuyTokenDetailsScreen { fiatAmountTextField.assertIsDisplayed() }
|
||||||
|
}
|
||||||
|
step("Assert fiat currency icon is displayed") {
|
||||||
|
onBuyTokenDetailsScreen { fiatCurrencyIcon.assertIsDisplayed() }
|
||||||
|
}
|
||||||
|
step("Assert token amount field is displayed") {
|
||||||
|
onBuyTokenDetailsScreen { tokenAmountField.assertTextContains(tokenSymbol, substring = true) }
|
||||||
|
}
|
||||||
|
step("Assert 'Continue' button") {
|
||||||
|
onBuyTokenDetailsScreen { continueButton.assertIsDisplayed() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@AllureId("4395")
|
||||||
|
@DisplayName("Action buttons (main screen): click on buttons with success response")
|
||||||
|
@Test
|
||||||
|
fun clickOnActionButtonsWithSuccessResponseTest() {
|
||||||
|
val tokenTitle = "Ethereum"
|
||||||
|
|
||||||
|
setupHooks().run {
|
||||||
|
step("Open 'Main Screen'") {
|
||||||
|
openMainScreen()
|
||||||
|
}
|
||||||
|
step("Synchronize addresses") {
|
||||||
|
synchronizeAddresses()
|
||||||
|
}
|
||||||
|
step("Assert 'Buy' button is displayed") {
|
||||||
|
onMainScreen { buyButton.assertIsDisplayed() }
|
||||||
|
}
|
||||||
|
step("Click on 'Buy' button") {
|
||||||
|
onMainScreen { buyButton.performClick() }
|
||||||
|
}
|
||||||
|
step("Assert 'Buy' screen title is displayed") {
|
||||||
|
onBuyTokenScreen { topAppBarTitle.assertIsDisplayed() }
|
||||||
|
}
|
||||||
|
step("Assert token with title: '$tokenTitle' is displayed") {
|
||||||
|
onBuyTokenScreen { tokenWithTitleAndFiatAmount(tokenTitle).assertIsDisplayed() }
|
||||||
|
}
|
||||||
|
step("Press 'Back' button") {
|
||||||
|
device.uiDevice.pressBack()
|
||||||
|
}
|
||||||
|
step("Assert 'Swap' button is displayed") {
|
||||||
|
onMainScreen { swapButton.assertIsDisplayed() }
|
||||||
|
}
|
||||||
|
step("Click on 'Swap' button") {
|
||||||
|
onMainScreen { swapButton.performClick() }
|
||||||
|
}
|
||||||
|
step("Click on close button on stories screen") {
|
||||||
|
onSwapStoriesScreen { closeButton.performClick() }
|
||||||
|
}
|
||||||
|
step("Assert 'Swap' token screen title is displayed") {
|
||||||
|
onSwapTokenScreen { title.assertIsDisplayed() }
|
||||||
|
}
|
||||||
|
step("Press 'Back' button") {
|
||||||
|
device.uiDevice.pressBack()
|
||||||
|
}
|
||||||
|
step("Assert 'Sell' button is displayed") {
|
||||||
|
onMainScreen { sellButton.assertIsDisplayed() }
|
||||||
|
}
|
||||||
|
step("Click on 'Sell' button") {
|
||||||
|
onMainScreen { sellButton.performClick() }
|
||||||
|
}
|
||||||
|
step("Assert 'Sell' token screen title is displayed") {
|
||||||
|
onSellScreen { title.assertIsDisplayed() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@AllureId("4396")
|
||||||
|
@DisplayName("Action buttons (main screen): click on buttons without data")
|
||||||
|
@Test
|
||||||
|
fun clickOnActionButtonsWithoutDataTest() {
|
||||||
|
setupHooks(
|
||||||
|
additionalAfterSection = {
|
||||||
|
enableWiFi()
|
||||||
|
enableMobileData()
|
||||||
|
}
|
||||||
|
).run {
|
||||||
|
step("Turn off internet") {
|
||||||
|
disableWiFi()
|
||||||
|
disableMobileData()
|
||||||
|
}
|
||||||
|
step("Open 'Main Screen'") {
|
||||||
|
openMainScreen()
|
||||||
|
}
|
||||||
|
step("Assert 'Buy' button is displayed") {
|
||||||
|
onMainScreen { buyButton.assertIsDisplayed() }
|
||||||
|
}
|
||||||
|
step("Click on 'Buy' button") {
|
||||||
|
onMainScreen { buyButton.performClick() }
|
||||||
|
}
|
||||||
|
step("Check 'Action is unavailable' dialog") {
|
||||||
|
checkActionIsUnavailableDialog()
|
||||||
|
}
|
||||||
|
step("Click on 'Ok' button") {
|
||||||
|
onDialog { okButton.performClick() }
|
||||||
|
}
|
||||||
|
step("Assert 'Swap' button is displayed") {
|
||||||
|
onMainScreen { swapButton.assertIsDisplayed() }
|
||||||
|
}
|
||||||
|
step("Click on 'Swap' button") {
|
||||||
|
onMainScreen { swapButton.performClick() }
|
||||||
|
}
|
||||||
|
step("Check 'Action is unavailable' dialog") {
|
||||||
|
checkActionIsUnavailableDialog()
|
||||||
|
}
|
||||||
|
step("Click on 'Ok' button") {
|
||||||
|
onDialog { okButton.performClick() }
|
||||||
|
}
|
||||||
|
step("Assert 'Sell' button is displayed") {
|
||||||
|
onMainScreen { sellButton.assertIsDisplayed() }
|
||||||
|
}
|
||||||
|
step("Click on 'Sell' button") {
|
||||||
|
onMainScreen { sellButton.performClick() }
|
||||||
|
}
|
||||||
|
step("Check 'Action is unavailable' dialog") {
|
||||||
|
checkActionIsUnavailableDialog()
|
||||||
|
}
|
||||||
|
step("Click on 'Ok' button") {
|
||||||
|
onDialog { okButton.performClick() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@AllureId("4398")
|
||||||
|
@DisplayName("Action buttons (main screen): click on buttons with error response")
|
||||||
|
@Test
|
||||||
|
fun clickOnActionButtonsWithErrorResponseTest() {
|
||||||
|
val scenarioName = "express_api_assets"
|
||||||
|
val scenarioState = "Error"
|
||||||
|
|
||||||
|
setupHooks(
|
||||||
|
additionalAfterSection = {
|
||||||
|
resetWireMockScenarioState(scenarioName)
|
||||||
|
}
|
||||||
|
).run {
|
||||||
|
step("Set WireMock scenario: '$scenarioName' to state: '$scenarioState'") {
|
||||||
|
setWireMockScenarioState(scenarioName, scenarioState)
|
||||||
|
}
|
||||||
|
step("Open 'Main Screen'") {
|
||||||
|
openMainScreen()
|
||||||
|
}
|
||||||
|
step("Assert 'Buy' button is displayed") {
|
||||||
|
onMainScreen { buyButton.assertIsDisplayed() }
|
||||||
|
}
|
||||||
|
step("Click on 'Buy' button") {
|
||||||
|
onMainScreen { buyButton.performClick() }
|
||||||
|
}
|
||||||
|
step("Check 'Action is unavailable' dialog") {
|
||||||
|
checkActionIsUnavailableDialog()
|
||||||
|
}
|
||||||
|
step("Click on 'Ok' button") {
|
||||||
|
onDialog { okButton.performClick() }
|
||||||
|
}
|
||||||
|
step("Assert 'Swap' button is displayed") {
|
||||||
|
onMainScreen { swapButton.assertIsDisplayed() }
|
||||||
|
}
|
||||||
|
step("Click on 'Swap' button") {
|
||||||
|
onMainScreen { swapButton.performClick() }
|
||||||
|
}
|
||||||
|
step("Check 'Action is unavailable' dialog") {
|
||||||
|
checkActionIsUnavailableDialog()
|
||||||
|
}
|
||||||
|
step("Click on 'Ok' button") {
|
||||||
|
onDialog { okButton.performClick() }
|
||||||
|
}
|
||||||
|
step("Assert 'Sell' button is displayed") {
|
||||||
|
onMainScreen { sellButton.assertIsDisplayed() }
|
||||||
|
}
|
||||||
|
step("Click on 'Sell' button") {
|
||||||
|
onMainScreen { sellButton.performClick() }
|
||||||
|
}
|
||||||
|
// ToDo("[REDACTED_JIRA] - add ability to use MoonPay mocks")
|
||||||
|
// step("Check 'Action is unavailable' dialog") {
|
||||||
|
// checkActionIsUnavailableDialog()
|
||||||
|
// }
|
||||||
|
// step("Click on 'Ok' button") {
|
||||||
|
// onDialog { okButton.performClick() }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@AllureId("3642")
|
||||||
|
@DisplayName("Action buttons (main screen): check buttons state")
|
||||||
|
@Test
|
||||||
|
fun checkButtonsStateTest() {
|
||||||
|
val scenarioName = "user_tokens_api"
|
||||||
|
val scenarioState = "EmptyTokensList"
|
||||||
|
|
||||||
|
setupHooks(
|
||||||
|
additionalAfterSection = {
|
||||||
|
resetWireMockScenarioState(scenarioName)
|
||||||
|
}
|
||||||
|
).run {
|
||||||
|
step("Set WireMock scenario: '$scenarioName' to state: '$scenarioState'") {
|
||||||
|
setWireMockScenarioState(scenarioName = scenarioName, state = scenarioState)
|
||||||
|
}
|
||||||
|
step("Open 'Main Screen'") {
|
||||||
|
openMainScreen()
|
||||||
|
}
|
||||||
|
step("Assert action buttons is not enabled") {
|
||||||
|
assertActionButtonsForMultiCurrencyWallet(isEnabled = false)
|
||||||
|
}
|
||||||
|
step("Reset Wiremock scenario: '$scenarioName'") {
|
||||||
|
resetWireMockScenarioState(scenarioName)
|
||||||
|
}
|
||||||
|
step("Perform pull to refresh") {
|
||||||
|
pullToRefresh(steps = 10)
|
||||||
|
waitForIdle()
|
||||||
|
}
|
||||||
|
step("Assert action buttons is enabled") {
|
||||||
|
assertActionButtonsForMultiCurrencyWallet(isEnabled = true)
|
||||||
|
}
|
||||||
|
step("Set WireMock scenario: '$scenarioName' to state: '$scenarioState'") {
|
||||||
|
setWireMockScenarioState(scenarioName = scenarioName, state = scenarioState)
|
||||||
|
}
|
||||||
|
step("Perform pull to refresh") {
|
||||||
|
pullToRefresh(steps = 10)
|
||||||
|
waitForIdle()
|
||||||
|
}
|
||||||
|
step("Assert action buttons is not enabled") {
|
||||||
|
assertActionButtonsForMultiCurrencyWallet(isEnabled = false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,249 @@
|
||||||
|
package com.tangem.tests.send.warnings
|
||||||
|
|
||||||
|
import com.tangem.common.BaseTestCase
|
||||||
|
import com.tangem.common.constants.TestConstants.SOLANA_RECIPIENT_ADDRESS
|
||||||
|
import com.tangem.common.extensions.clickWithAssertion
|
||||||
|
import com.tangem.common.utils.resetWireMockScenarioState
|
||||||
|
import com.tangem.common.utils.setWireMockScenarioState
|
||||||
|
import com.tangem.scenarios.checkSendWarning
|
||||||
|
import com.tangem.scenarios.openMainScreen
|
||||||
|
import com.tangem.scenarios.synchronizeAddresses
|
||||||
|
import com.tangem.screens.onMainScreen
|
||||||
|
import com.tangem.screens.onSendAddressScreen
|
||||||
|
import com.tangem.screens.onSendScreen
|
||||||
|
import com.tangem.screens.onTokenDetailsScreen
|
||||||
|
import com.tangem.wallet.R
|
||||||
|
import dagger.hilt.android.testing.HiltAndroidTest
|
||||||
|
import io.qameta.allure.kotlin.AllureId
|
||||||
|
import io.qameta.allure.kotlin.junit4.DisplayName
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
@HiltAndroidTest
|
||||||
|
class SolanaWarningsTest : BaseTestCase() {
|
||||||
|
private val tokenName = "Solana"
|
||||||
|
private val amountToLeaveLessThanRent = "0.0016941"
|
||||||
|
private val amountToLeaveGreaterThanRent = "0.0000941"
|
||||||
|
private val amountToLeaveRentOnly = "0.00168934"
|
||||||
|
private val userTokensScenarioName = "user_tokens_api"
|
||||||
|
private val userTokensScenarioState = "Solana"
|
||||||
|
private val quotesScenarioName = "quotes_api"
|
||||||
|
private val quotesScenarioState = "Solana"
|
||||||
|
private val rentAmount = "0.000890880"
|
||||||
|
|
||||||
|
private val invalidAmountTitleResId = R.string.send_notification_invalid_amount_title
|
||||||
|
private val invalidAmountMessageResId = R.string.send_notification_invalid_amount_rent_fee
|
||||||
|
|
||||||
|
@AllureId("564")
|
||||||
|
@DisplayName("Warnings: warning is displayed, if after send balance is less than rent amount (SOLANA)")
|
||||||
|
@Test
|
||||||
|
fun warningIsDisplayedWhenLeaveLessThanRent() {
|
||||||
|
setupHooks(
|
||||||
|
additionalAfterSection = {
|
||||||
|
resetWireMockScenarioState(userTokensScenarioName)
|
||||||
|
resetWireMockScenarioState(quotesScenarioName)
|
||||||
|
}
|
||||||
|
).run {
|
||||||
|
step("Set WireMock scenario: '$userTokensScenarioName' to state: '$userTokensScenarioState'") {
|
||||||
|
setWireMockScenarioState(scenarioName = userTokensScenarioName, state = userTokensScenarioState)
|
||||||
|
}
|
||||||
|
step("Set WireMock scenario: '$quotesScenarioName' to state: '$quotesScenarioState'") {
|
||||||
|
setWireMockScenarioState(scenarioName = quotesScenarioName, state = quotesScenarioState)
|
||||||
|
}
|
||||||
|
step("Open 'Main Screen'") {
|
||||||
|
openMainScreen()
|
||||||
|
}
|
||||||
|
step("Synchronize addresses") {
|
||||||
|
synchronizeAddresses()
|
||||||
|
}
|
||||||
|
step("Click on token with name: '$tokenName'") {
|
||||||
|
onMainScreen { tokenWithTitleAndAddress(tokenName).clickWithAssertion() }
|
||||||
|
}
|
||||||
|
step("Click on 'Send' button") {
|
||||||
|
onTokenDetailsScreen { sendButton.performClick() }
|
||||||
|
}
|
||||||
|
step("Type '$amountToLeaveLessThanRent' in input text field") {
|
||||||
|
onSendScreen {
|
||||||
|
amountInputTextField.performClick()
|
||||||
|
amountInputTextField.performTextReplacement(amountToLeaveLessThanRent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
step("Click on 'Next' button") {
|
||||||
|
onSendScreen { nextButton.clickWithAssertion() }
|
||||||
|
}
|
||||||
|
step("Type address in input text field") {
|
||||||
|
onSendAddressScreen { addressTextField.performTextReplacement(SOLANA_RECIPIENT_ADDRESS) }
|
||||||
|
}
|
||||||
|
step("Click on 'Next' button") {
|
||||||
|
onSendAddressScreen { nextButton.clickWithAssertion() }
|
||||||
|
}
|
||||||
|
step("Assert 'Invalid amount warning' is displayed") {
|
||||||
|
checkSendWarning(
|
||||||
|
titleResId = invalidAmountTitleResId,
|
||||||
|
messageResId = invalidAmountMessageResId,
|
||||||
|
amount = rentAmount
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@AllureId("567")
|
||||||
|
@DisplayName("Warnings: warning is not displayed, if after send balance is greater than rent amount (SOLANA)")
|
||||||
|
@Test
|
||||||
|
fun warningIsNotDisplayedWhenLeaveGreaterThanRent() {
|
||||||
|
setupHooks(
|
||||||
|
additionalAfterSection = {
|
||||||
|
resetWireMockScenarioState(userTokensScenarioName)
|
||||||
|
resetWireMockScenarioState(quotesScenarioName)
|
||||||
|
}
|
||||||
|
).run {
|
||||||
|
step("Set WireMock scenario: '$userTokensScenarioName' to state: '$userTokensScenarioState'") {
|
||||||
|
setWireMockScenarioState(scenarioName = userTokensScenarioName, state = userTokensScenarioState)
|
||||||
|
}
|
||||||
|
step("Set WireMock scenario: '$quotesScenarioName' to state: '$quotesScenarioState'") {
|
||||||
|
setWireMockScenarioState(scenarioName = quotesScenarioName, state = quotesScenarioState)
|
||||||
|
}
|
||||||
|
step("Open 'Main Screen'") {
|
||||||
|
openMainScreen()
|
||||||
|
}
|
||||||
|
step("Synchronize addresses") {
|
||||||
|
synchronizeAddresses()
|
||||||
|
}
|
||||||
|
step("Click on token with name: '$tokenName'") {
|
||||||
|
onMainScreen { tokenWithTitleAndAddress(tokenName).clickWithAssertion() }
|
||||||
|
}
|
||||||
|
step("Click on 'Send' button") {
|
||||||
|
onTokenDetailsScreen { sendButton.performClick() }
|
||||||
|
}
|
||||||
|
step("Type '$amountToLeaveGreaterThanRent' in input text field") {
|
||||||
|
onSendScreen {
|
||||||
|
amountInputTextField.performClick()
|
||||||
|
amountInputTextField.performTextReplacement(amountToLeaveGreaterThanRent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
step("Click on 'Next' button") {
|
||||||
|
onSendScreen { nextButton.clickWithAssertion() }
|
||||||
|
}
|
||||||
|
step("Type address in input text field") {
|
||||||
|
onSendAddressScreen { addressTextField.performTextReplacement(SOLANA_RECIPIENT_ADDRESS) }
|
||||||
|
}
|
||||||
|
step("Click on 'Next' button") {
|
||||||
|
onSendAddressScreen { nextButton.clickWithAssertion() }
|
||||||
|
}
|
||||||
|
step("Assert 'Invalid amount warning' is not displayed") {
|
||||||
|
checkSendWarning(
|
||||||
|
titleResId = invalidAmountTitleResId,
|
||||||
|
messageResId = invalidAmountMessageResId,
|
||||||
|
amount = rentAmount,
|
||||||
|
isDisplayed = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@AllureId("566")
|
||||||
|
@DisplayName("Warnings: warning is not displayed, if after send balance is equal to rent amount (SOLANA)")
|
||||||
|
@Test
|
||||||
|
fun warningIsNotDisplayedWhenLeaveOnlyRent() {
|
||||||
|
setupHooks(
|
||||||
|
additionalAfterSection = {
|
||||||
|
resetWireMockScenarioState(userTokensScenarioName)
|
||||||
|
resetWireMockScenarioState(quotesScenarioName)
|
||||||
|
}
|
||||||
|
).run {
|
||||||
|
step("Set WireMock scenario: '$userTokensScenarioName' to state: '$userTokensScenarioState'") {
|
||||||
|
setWireMockScenarioState(scenarioName = userTokensScenarioName, state = userTokensScenarioState)
|
||||||
|
}
|
||||||
|
step("Set WireMock scenario: '$quotesScenarioName' to state: '$quotesScenarioState'") {
|
||||||
|
setWireMockScenarioState(scenarioName = quotesScenarioName, state = quotesScenarioState)
|
||||||
|
}
|
||||||
|
step("Open 'Main Screen'") {
|
||||||
|
openMainScreen()
|
||||||
|
}
|
||||||
|
step("Synchronize addresses") {
|
||||||
|
synchronizeAddresses()
|
||||||
|
}
|
||||||
|
step("Click on token with name: '$tokenName'") {
|
||||||
|
onMainScreen { tokenWithTitleAndAddress(tokenName).clickWithAssertion() }
|
||||||
|
}
|
||||||
|
step("Click on 'Send' button") {
|
||||||
|
onTokenDetailsScreen { sendButton.performClick() }
|
||||||
|
}
|
||||||
|
step("Type '$amountToLeaveRentOnly' in input text field") {
|
||||||
|
onSendScreen {
|
||||||
|
amountInputTextField.performClick()
|
||||||
|
amountInputTextField.performTextReplacement(amountToLeaveRentOnly)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
step("Click on 'Next' button") {
|
||||||
|
onSendScreen { nextButton.clickWithAssertion() }
|
||||||
|
}
|
||||||
|
step("Type address in input text field") {
|
||||||
|
onSendAddressScreen { addressTextField.performTextReplacement(SOLANA_RECIPIENT_ADDRESS) }
|
||||||
|
}
|
||||||
|
step("Click on 'Next' button") {
|
||||||
|
onSendAddressScreen { nextButton.clickWithAssertion() }
|
||||||
|
}
|
||||||
|
step("Assert 'Invalid amount warning' is not displayed") {
|
||||||
|
checkSendWarning(
|
||||||
|
titleResId = invalidAmountTitleResId,
|
||||||
|
messageResId = invalidAmountMessageResId,
|
||||||
|
amount = rentAmount,
|
||||||
|
isDisplayed = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@AllureId("565")
|
||||||
|
@DisplayName("Warnings: warning is not displayed, if after send balance is zero (SOLANA)")
|
||||||
|
@Test
|
||||||
|
fun warningIsNotDisplayedWhenLeaveZeroSol() {
|
||||||
|
setupHooks(
|
||||||
|
additionalAfterSection = {
|
||||||
|
resetWireMockScenarioState(userTokensScenarioName)
|
||||||
|
resetWireMockScenarioState(quotesScenarioName)
|
||||||
|
}
|
||||||
|
).run {
|
||||||
|
step("Set WireMock scenario: '$userTokensScenarioName' to state: '$userTokensScenarioState'") {
|
||||||
|
setWireMockScenarioState(scenarioName = userTokensScenarioName, state = userTokensScenarioState)
|
||||||
|
}
|
||||||
|
step("Set WireMock scenario: '$quotesScenarioName' to state: '$quotesScenarioState'") {
|
||||||
|
setWireMockScenarioState(scenarioName = quotesScenarioName, state = quotesScenarioState)
|
||||||
|
}
|
||||||
|
step("Open 'Main Screen'") {
|
||||||
|
openMainScreen()
|
||||||
|
}
|
||||||
|
step("Synchronize addresses") {
|
||||||
|
synchronizeAddresses()
|
||||||
|
}
|
||||||
|
step("Click on token with name: '$tokenName'") {
|
||||||
|
onMainScreen { tokenWithTitleAndAddress(tokenName).clickWithAssertion() }
|
||||||
|
}
|
||||||
|
step("Click on 'Send' button") {
|
||||||
|
onTokenDetailsScreen { sendButton.performClick() }
|
||||||
|
}
|
||||||
|
step("Type max amount in input text field") {
|
||||||
|
onSendScreen {
|
||||||
|
maxButton.performClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
step("Click on 'Next' button") {
|
||||||
|
onSendScreen { nextButton.clickWithAssertion() }
|
||||||
|
}
|
||||||
|
step("Type address in input text field") {
|
||||||
|
onSendAddressScreen { addressTextField.performTextReplacement(SOLANA_RECIPIENT_ADDRESS) }
|
||||||
|
}
|
||||||
|
step("Click on 'Next' button") {
|
||||||
|
onSendAddressScreen { nextButton.clickWithAssertion() }
|
||||||
|
}
|
||||||
|
step("Assert 'Invalid amount warning' is not displayed") {
|
||||||
|
checkSendWarning(
|
||||||
|
titleResId = invalidAmountTitleResId,
|
||||||
|
messageResId = invalidAmountMessageResId,
|
||||||
|
amount = rentAmount,
|
||||||
|
isDisplayed = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,10 +3,13 @@ package com.tangem.tap.common.analytics
|
||||||
import com.tangem.core.analytics.Analytics
|
import com.tangem.core.analytics.Analytics
|
||||||
import com.tangem.core.analytics.utils.AnalyticsContextProxy
|
import com.tangem.core.analytics.utils.AnalyticsContextProxy
|
||||||
import com.tangem.domain.models.scan.ScanResponse
|
import com.tangem.domain.models.scan.ScanResponse
|
||||||
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
import com.tangem.tap.common.extensions.addContext
|
import com.tangem.tap.common.extensions.addContext
|
||||||
|
import com.tangem.tap.common.extensions.addHotWalletContext
|
||||||
import com.tangem.tap.common.extensions.eraseContext
|
import com.tangem.tap.common.extensions.eraseContext
|
||||||
import com.tangem.tap.common.extensions.removeContext
|
import com.tangem.tap.common.extensions.removeContext
|
||||||
import com.tangem.tap.common.extensions.setContext
|
import com.tangem.tap.common.extensions.setContext
|
||||||
|
import com.tangem.tap.common.extensions.setHotWalletContext
|
||||||
|
|
||||||
/**
|
/**
|
||||||
[REDACTED_AUTHOR]
|
[REDACTED_AUTHOR]
|
||||||
|
|
@ -17,6 +20,14 @@ internal class DefaultAnalyticsContextProxy : AnalyticsContextProxy {
|
||||||
Analytics.setContext(scanResponse)
|
Analytics.setContext(scanResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun addContext(userWallet: UserWallet) {
|
||||||
|
Analytics.addContext(userWallet)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setHotWalletContext() {
|
||||||
|
Analytics.setHotWalletContext()
|
||||||
|
}
|
||||||
|
|
||||||
override fun eraseContext() {
|
override fun eraseContext() {
|
||||||
Analytics.eraseContext()
|
Analytics.eraseContext()
|
||||||
}
|
}
|
||||||
|
|
@ -25,6 +36,10 @@ internal class DefaultAnalyticsContextProxy : AnalyticsContextProxy {
|
||||||
Analytics.addContext(scanResponse)
|
Analytics.addContext(scanResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun addHotWalletContext() {
|
||||||
|
Analytics.addHotWalletContext()
|
||||||
|
}
|
||||||
|
|
||||||
override fun removeContext() {
|
override fun removeContext() {
|
||||||
Analytics.removeContext()
|
Analytics.removeContext()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ class BlockchainApiExceptionEvent(
|
||||||
selectedHost: String,
|
selectedHost: String,
|
||||||
exceptionHost: String,
|
exceptionHost: String,
|
||||||
error: String,
|
error: String,
|
||||||
|
blockchain: String,
|
||||||
) : AnalyticsEvent(
|
) : AnalyticsEvent(
|
||||||
category = "BlockchainSdk",
|
category = "BlockchainSdk",
|
||||||
event = "Exception",
|
event = "Exception",
|
||||||
|
|
@ -13,5 +14,6 @@ class BlockchainApiExceptionEvent(
|
||||||
AnalyticsParam.BLOCKCHAIN_SELECTED_HOST to selectedHost,
|
AnalyticsParam.BLOCKCHAIN_SELECTED_HOST to selectedHost,
|
||||||
AnalyticsParam.BLOCKCHAIN_EXCEPTION_HOST to exceptionHost,
|
AnalyticsParam.BLOCKCHAIN_EXCEPTION_HOST to exceptionHost,
|
||||||
AnalyticsParam.ERROR_DESCRIPTION to error,
|
AnalyticsParam.ERROR_DESCRIPTION to error,
|
||||||
|
AnalyticsParam.BLOCKCHAIN to blockchain,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package com.tangem.tap.common.analytics.handlers
|
package com.tangem.tap.common.analytics.handlers
|
||||||
|
|
||||||
|
import com.tangem.blockchain.common.Blockchain
|
||||||
import com.tangem.blockchain.common.ExceptionHandlerOutput
|
import com.tangem.blockchain.common.ExceptionHandlerOutput
|
||||||
|
import com.tangem.blockchainsdk.utils.toNetworkId
|
||||||
import com.tangem.core.analytics.api.AnalyticsErrorHandler
|
import com.tangem.core.analytics.api.AnalyticsErrorHandler
|
||||||
import com.tangem.tap.common.analytics.events.BlockchainApiExceptionEvent
|
import com.tangem.tap.common.analytics.events.BlockchainApiExceptionEvent
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
@ -8,12 +10,13 @@ import javax.inject.Inject
|
||||||
class BlockchainExceptionHandler @Inject constructor(
|
class BlockchainExceptionHandler @Inject constructor(
|
||||||
private val analyticsErrorHandler: AnalyticsErrorHandler,
|
private val analyticsErrorHandler: AnalyticsErrorHandler,
|
||||||
) : ExceptionHandlerOutput {
|
) : ExceptionHandlerOutput {
|
||||||
override fun handleApiSwitch(currentHost: String, nextHost: String, message: String) {
|
override fun handleApiSwitch(currentHost: String, nextHost: String, message: String, blockchain: Blockchain) {
|
||||||
analyticsErrorHandler.sendErrorEvent(
|
analyticsErrorHandler.sendErrorEvent(
|
||||||
BlockchainApiExceptionEvent(
|
BlockchainApiExceptionEvent(
|
||||||
selectedHost = nextHost,
|
selectedHost = nextHost,
|
||||||
exceptionHost = currentHost,
|
exceptionHost = currentHost,
|
||||||
error = message,
|
error = message,
|
||||||
|
blockchain = blockchain.toNetworkId(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.tangem.tap.common.analytics.paramsInterceptor
|
||||||
|
|
||||||
|
import com.tangem.core.analytics.api.ParamsInterceptor
|
||||||
|
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||||
|
import com.tangem.core.analytics.models.AnalyticsParam
|
||||||
|
|
||||||
|
class HotWalletContextInterceptor(
|
||||||
|
val parent: ParamsInterceptor? = null,
|
||||||
|
) : ParamsInterceptor {
|
||||||
|
|
||||||
|
override fun id(): String = HotWalletContextInterceptor.id()
|
||||||
|
|
||||||
|
override fun canBeAppliedTo(event: AnalyticsEvent): Boolean = true
|
||||||
|
|
||||||
|
override fun intercept(params: MutableMap<String, String>) {
|
||||||
|
params[AnalyticsParam.PRODUCT_TYPE] = "Mobile Wallet"
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun id(): String = HotWalletContextInterceptor::class.java.simpleName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,7 +9,7 @@ import com.tangem.domain.models.scan.ScanResponse
|
||||||
*/
|
*/
|
||||||
class LinkedCardContextInterceptor(
|
class LinkedCardContextInterceptor(
|
||||||
scanResponse: ScanResponse,
|
scanResponse: ScanResponse,
|
||||||
val parent: LinkedCardContextInterceptor? = null,
|
val parent: ParamsInterceptor? = null,
|
||||||
) : ParamsInterceptor {
|
) : ParamsInterceptor {
|
||||||
|
|
||||||
private val contextInterceptor = CardContextInterceptor(scanResponse)
|
private val contextInterceptor = CardContextInterceptor(scanResponse)
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import com.tangem.core.analytics.Analytics
|
||||||
import com.tangem.domain.models.scan.ScanResponse
|
import com.tangem.domain.models.scan.ScanResponse
|
||||||
import com.tangem.domain.models.wallet.UserWallet
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
|
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
|
||||||
|
import com.tangem.tap.common.analytics.paramsInterceptor.HotWalletContextInterceptor
|
||||||
import com.tangem.tap.common.analytics.paramsInterceptor.LinkedCardContextInterceptor
|
import com.tangem.tap.common.analytics.paramsInterceptor.LinkedCardContextInterceptor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -24,19 +25,46 @@ fun Analytics.setContext(scanResponse: ScanResponse) {
|
||||||
|
|
||||||
fun Analytics.setContext(userWallet: UserWallet) {
|
fun Analytics.setContext(userWallet: UserWallet) {
|
||||||
setUserId(userWallet.walletId.stringValue)
|
setUserId(userWallet.walletId.stringValue)
|
||||||
// TODO add product type for hot ([REDACTED_TASK_KEY] [Hot Wallet] Analytics)
|
|
||||||
|
|
||||||
if (userWallet is UserWallet.Cold) {
|
when (userWallet) {
|
||||||
addParamsInterceptor(LinkedCardContextInterceptor(userWallet.scanResponse))
|
is UserWallet.Cold -> {
|
||||||
|
removeParamsInterceptor(HotWalletContextInterceptor.id())
|
||||||
|
addParamsInterceptor(LinkedCardContextInterceptor(userWallet.scanResponse))
|
||||||
|
}
|
||||||
|
is UserWallet.Hot -> {
|
||||||
|
removeParamsInterceptor(LinkedCardContextInterceptor.id())
|
||||||
|
addParamsInterceptor(HotWalletContextInterceptor())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Analytics.setHotWalletContext() {
|
||||||
|
addParamsInterceptor(HotWalletContextInterceptor())
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Erases the context
|
* Erases the context
|
||||||
*/
|
*/
|
||||||
fun Analytics.eraseContext() {
|
fun Analytics.eraseContext() {
|
||||||
clearUserId()
|
clearUserId()
|
||||||
removeParamsInterceptor(LinkedCardContextInterceptor.id())
|
removeParamsInterceptor(LinkedCardContextInterceptor.id())
|
||||||
|
removeParamsInterceptor(HotWalletContextInterceptor.id())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new context and keeps a previous context as the parent of the new one
|
||||||
|
*/
|
||||||
|
fun Analytics.addContext(userWallet: UserWallet) {
|
||||||
|
val currentContext = removeParamsInterceptor(LinkedCardContextInterceptor.id())
|
||||||
|
?: removeParamsInterceptor(HotWalletContextInterceptor.id())
|
||||||
|
|
||||||
|
val newContext = when (userWallet) {
|
||||||
|
is UserWallet.Cold -> LinkedCardContextInterceptor(userWallet.scanResponse, parent = currentContext)
|
||||||
|
is UserWallet.Hot -> HotWalletContextInterceptor(parent = currentContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
setUserId(userWalletId = userWallet.walletId.stringValue)
|
||||||
|
addParamsInterceptor(newContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -48,18 +76,32 @@ fun Analytics.addContext(scanResponse: ScanResponse) {
|
||||||
setUserId(userWalletId.stringValue)
|
setUserId(userWalletId.stringValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
val currentContext = removeParamsInterceptor(LinkedCardContextInterceptor.id()) as? LinkedCardContextInterceptor
|
val currentContext = removeParamsInterceptor(LinkedCardContextInterceptor.id())
|
||||||
|
?: removeParamsInterceptor(HotWalletContextInterceptor.id())
|
||||||
val newContext = LinkedCardContextInterceptor(scanResponse, parent = currentContext)
|
val newContext = LinkedCardContextInterceptor(scanResponse, parent = currentContext)
|
||||||
|
|
||||||
addParamsInterceptor(newContext)
|
addParamsInterceptor(newContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Analytics.addHotWalletContext() {
|
||||||
|
val currentContext = removeParamsInterceptor(LinkedCardContextInterceptor.id()) as? LinkedCardContextInterceptor
|
||||||
|
val newContext = HotWalletContextInterceptor(currentContext)
|
||||||
|
|
||||||
|
addParamsInterceptor(newContext)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the current context and restores the previous one if it was present.
|
* Removes the current context and restores the previous one if it was present.
|
||||||
*/
|
*/
|
||||||
fun Analytics.removeContext() {
|
fun Analytics.removeContext() {
|
||||||
val currentContext = removeParamsInterceptor(LinkedCardContextInterceptor.id()) as? LinkedCardContextInterceptor
|
val currentContext = removeParamsInterceptor(LinkedCardContextInterceptor.id())
|
||||||
val previousContext = currentContext?.parent ?: return
|
?: removeParamsInterceptor(HotWalletContextInterceptor.id())
|
||||||
|
|
||||||
|
val previousContext = when (currentContext) {
|
||||||
|
is LinkedCardContextInterceptor -> currentContext.parent
|
||||||
|
is HotWalletContextInterceptor -> currentContext.parent
|
||||||
|
else -> null
|
||||||
|
} ?: return
|
||||||
|
|
||||||
addParamsInterceptor(previousContext)
|
addParamsInterceptor(previousContext)
|
||||||
}
|
}
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
package com.tangem.tap.di
|
package com.tangem.tap.di
|
||||||
|
|
||||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||||
import com.tangem.datasource.exchangeservice.swap.ExpressServiceLoader
|
|
||||||
import com.tangem.domain.card.ScanCardUseCase
|
import com.tangem.domain.card.ScanCardUseCase
|
||||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||||
import com.tangem.domain.exchange.RampStateManager
|
import com.tangem.domain.exchange.RampStateManager
|
||||||
|
import com.tangem.domain.express.ExpressServiceFetcher
|
||||||
import com.tangem.domain.tokens.GetPolkadotCheckHasImmortalUseCase
|
import com.tangem.domain.tokens.GetPolkadotCheckHasImmortalUseCase
|
||||||
import com.tangem.domain.tokens.GetPolkadotCheckHasResetUseCase
|
import com.tangem.domain.tokens.GetPolkadotCheckHasResetUseCase
|
||||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||||
|
|
@ -46,14 +46,14 @@ internal object ActivityModule {
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideDefaultRampManager(
|
fun provideDefaultRampManager(
|
||||||
appStateHolder: AppStateHolder,
|
appStateHolder: AppStateHolder,
|
||||||
expressServiceLoader: ExpressServiceLoader,
|
expressServiceFetcher: ExpressServiceFetcher,
|
||||||
currenciesRepository: CurrenciesRepository,
|
currenciesRepository: CurrenciesRepository,
|
||||||
excludedBlockchains: ExcludedBlockchains,
|
excludedBlockchains: ExcludedBlockchains,
|
||||||
dispatchers: CoroutineDispatcherProvider,
|
dispatchers: CoroutineDispatcherProvider,
|
||||||
): RampStateManager {
|
): RampStateManager {
|
||||||
return DefaultRampManager(
|
return DefaultRampManager(
|
||||||
sellService = Provider { requireNotNull(appStateHolder.sellService) },
|
sellService = Provider { requireNotNull(appStateHolder.sellService) },
|
||||||
expressServiceLoader = expressServiceLoader,
|
expressServiceFetcher = expressServiceFetcher,
|
||||||
currenciesRepository = currenciesRepository,
|
currenciesRepository = currenciesRepository,
|
||||||
dispatchers = dispatchers,
|
dispatchers = dispatchers,
|
||||||
excludedBlockchains = excludedBlockchains,
|
excludedBlockchains = excludedBlockchains,
|
||||||
|
|
|
||||||
|
|
@ -179,20 +179,6 @@ internal object OnrampDomainModule {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
fun provideGetOnrampV2QuotesUseCase(
|
|
||||||
settingsRepository: SettingsRepository,
|
|
||||||
onrampRepository: OnrampRepository,
|
|
||||||
onrampErrorResolver: OnrampErrorResolver,
|
|
||||||
): GetOnrampV2QuotesUseCase {
|
|
||||||
return GetOnrampV2QuotesUseCase(
|
|
||||||
settingsRepository = settingsRepository,
|
|
||||||
repository = onrampRepository,
|
|
||||||
errorResolver = onrampErrorResolver,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideGetOnrampProviderWithQuoteUseCase(
|
fun provideGetOnrampProviderWithQuoteUseCase(
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,7 @@ import com.tangem.domain.staking.single.SingleYieldBalanceSupplier
|
||||||
import com.tangem.domain.tokens.*
|
import com.tangem.domain.tokens.*
|
||||||
import com.tangem.domain.tokens.operations.BaseCurrencyStatusOperations
|
import com.tangem.domain.tokens.operations.BaseCurrencyStatusOperations
|
||||||
import com.tangem.domain.tokens.operations.CachedCurrenciesStatusesOperations
|
import com.tangem.domain.tokens.operations.CachedCurrenciesStatusesOperations
|
||||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
import com.tangem.domain.tokens.repository.*
|
||||||
import com.tangem.domain.tokens.repository.CurrencyChecksRepository
|
|
||||||
import com.tangem.domain.tokens.repository.PolkadotAccountHealthCheckRepository
|
|
||||||
import com.tangem.domain.tokens.repository.TokenReceiveWarningsViewedRepository
|
|
||||||
import com.tangem.domain.tokens.repository.YieldSupplyWarningsViewedRepository
|
|
||||||
import com.tangem.domain.tokens.wallet.WalletBalanceFetcher
|
import com.tangem.domain.tokens.wallet.WalletBalanceFetcher
|
||||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||||
import com.tangem.tap.domain.tokens.DefaultTokensFeatureToggles
|
import com.tangem.tap.domain.tokens.DefaultTokensFeatureToggles
|
||||||
|
|
@ -60,24 +56,6 @@ internal object TokensDomainModule {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
fun provideFetchTokenListUseCase(
|
|
||||||
currenciesRepository: CurrenciesRepository,
|
|
||||||
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
|
|
||||||
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
|
|
||||||
multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
|
|
||||||
stakingIdFactory: StakingIdFactory,
|
|
||||||
): FetchTokenListUseCase {
|
|
||||||
return FetchTokenListUseCase(
|
|
||||||
currenciesRepository = currenciesRepository,
|
|
||||||
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
|
|
||||||
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
|
|
||||||
multiYieldBalanceFetcher = multiYieldBalanceFetcher,
|
|
||||||
stakingIdFactory = stakingIdFactory,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideFetchPendingTransactionsUseCase(
|
fun provideFetchPendingTransactionsUseCase(
|
||||||
|
|
@ -184,24 +162,6 @@ internal object TokensDomainModule {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
fun provideFetchCardTokenListUseCase(
|
|
||||||
currenciesRepository: CurrenciesRepository,
|
|
||||||
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
|
|
||||||
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
|
|
||||||
multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
|
|
||||||
stakingIdFactory: StakingIdFactory,
|
|
||||||
): FetchCardTokenListUseCase {
|
|
||||||
return FetchCardTokenListUseCase(
|
|
||||||
currenciesRepository = currenciesRepository,
|
|
||||||
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
|
|
||||||
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
|
|
||||||
multiYieldBalanceFetcher = multiYieldBalanceFetcher,
|
|
||||||
stakingIdFactory = stakingIdFactory,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideGetCryptoCurrencyUseCase(
|
fun provideGetCryptoCurrencyUseCase(
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ object BackupWalletMockContent : MockContent {
|
||||||
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
|
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey(
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey(
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/1'/0'/0/0") to ExtendedPublicKey(
|
DerivationPath("m/44'/1'/0'/0/0") to ExtendedPublicKey(
|
||||||
|
|
@ -185,7 +185,7 @@ object BackupWalletMockContent : MockContent {
|
||||||
childNumber = 0,
|
childNumber = 0,
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
depth = 0,
|
depth = 0,
|
||||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
|
|
@ -244,7 +244,7 @@ object BackupWalletMockContent : MockContent {
|
||||||
childNumber = 0,
|
childNumber = 0,
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
depth = 0,
|
depth = 0,
|
||||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ object DevWalletMockContent : MockContent {
|
||||||
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
|
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey(
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey(
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/1'/0'/0/0") to ExtendedPublicKey(
|
DerivationPath("m/44'/1'/0'/0/0") to ExtendedPublicKey(
|
||||||
|
|
@ -185,7 +185,7 @@ object DevWalletMockContent : MockContent {
|
||||||
childNumber = 0,
|
childNumber = 0,
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
depth = 0,
|
depth = 0,
|
||||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
|
|
@ -244,7 +244,7 @@ object DevWalletMockContent : MockContent {
|
||||||
childNumber = 0,
|
childNumber = 0,
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
depth = 0,
|
depth = 0,
|
||||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ object Firmware412MockContent : MockContent {
|
||||||
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
|
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey(
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey(
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/1'/0'/0/0") to ExtendedPublicKey(
|
DerivationPath("m/44'/1'/0'/0/0") to ExtendedPublicKey(
|
||||||
|
|
@ -181,7 +181,7 @@ object Firmware412MockContent : MockContent {
|
||||||
childNumber = 0,
|
childNumber = 0,
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
depth = 0,
|
depth = 0,
|
||||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
|
|
@ -240,7 +240,7 @@ object Firmware412MockContent : MockContent {
|
||||||
childNumber = 0,
|
childNumber = 0,
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
depth = 0,
|
depth = 0,
|
||||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ object RingMockContent : MockContent {
|
||||||
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
|
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey(
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey(
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/1'/0'/0/0") to ExtendedPublicKey(
|
DerivationPath("m/44'/1'/0'/0/0") to ExtendedPublicKey(
|
||||||
|
|
@ -185,7 +185,7 @@ object RingMockContent : MockContent {
|
||||||
childNumber = 0,
|
childNumber = 0,
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
depth = 0,
|
depth = 0,
|
||||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
|
|
@ -244,7 +244,7 @@ object RingMockContent : MockContent {
|
||||||
childNumber = 0,
|
childNumber = 0,
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
depth = 0,
|
depth = 0,
|
||||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ object ShibaMockContent : MockContent {
|
||||||
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
|
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey(
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey(
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/1'/0'/0/0") to ExtendedPublicKey(
|
DerivationPath("m/44'/1'/0'/0/0") to ExtendedPublicKey(
|
||||||
|
|
@ -185,7 +185,7 @@ object ShibaMockContent : MockContent {
|
||||||
childNumber = 0,
|
childNumber = 0,
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
depth = 0,
|
depth = 0,
|
||||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
|
|
@ -244,7 +244,7 @@ object ShibaMockContent : MockContent {
|
||||||
childNumber = 0,
|
childNumber = 0,
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
depth = 0,
|
depth = 0,
|
||||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ object ShibaNoBackupMockContent : MockContent {
|
||||||
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
|
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey(
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey(
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/1'/0'/0/0") to ExtendedPublicKey(
|
DerivationPath("m/44'/1'/0'/0/0") to ExtendedPublicKey(
|
||||||
|
|
@ -185,7 +185,7 @@ object ShibaNoBackupMockContent : MockContent {
|
||||||
childNumber = 0,
|
childNumber = 0,
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
depth = 0,
|
depth = 0,
|
||||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
|
|
@ -244,7 +244,7 @@ object ShibaNoBackupMockContent : MockContent {
|
||||||
childNumber = 0,
|
childNumber = 0,
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
depth = 0,
|
depth = 0,
|
||||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,7 @@ object ShibaNoBackupNoWalletsMockContent : MockContent {
|
||||||
childNumber = 0,
|
childNumber = 0,
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
depth = 0,
|
depth = 0,
|
||||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
|
|
@ -197,7 +197,7 @@ object ShibaNoBackupNoWalletsMockContent : MockContent {
|
||||||
childNumber = 0,
|
childNumber = 0,
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
depth = 0,
|
depth = 0,
|
||||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
|
|
|
||||||
|
|
@ -229,7 +229,7 @@ object Wallet2MockContent : MockContent {
|
||||||
childNumber = 0,
|
childNumber = 0,
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
depth = 0,
|
depth = 0,
|
||||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
|
|
@ -281,7 +281,7 @@ object Wallet2MockContent : MockContent {
|
||||||
childNumber = 0,
|
childNumber = 0,
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
depth = 0,
|
depth = 0,
|
||||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
|
|
|
||||||
|
|
@ -229,7 +229,7 @@ object Wallet2NoBackupMockContent : MockContent {
|
||||||
childNumber = 0,
|
childNumber = 0,
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
depth = 0,
|
depth = 0,
|
||||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
|
|
@ -281,7 +281,7 @@ object Wallet2NoBackupMockContent : MockContent {
|
||||||
childNumber = 0,
|
childNumber = 0,
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
depth = 0,
|
depth = 0,
|
||||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,7 @@ object Wallet2NoBackupNoWalletsMockContent : MockContent {
|
||||||
childNumber = 0,
|
childNumber = 0,
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
depth = 0,
|
depth = 0,
|
||||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
|
|
@ -193,7 +193,7 @@ object Wallet2NoBackupNoWalletsMockContent : MockContent {
|
||||||
childNumber = 0,
|
childNumber = 0,
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
depth = 0,
|
depth = 0,
|
||||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
|
|
|
||||||
|
|
@ -229,7 +229,7 @@ object Wallet2WithSeedPhraseMockContent : MockContent {
|
||||||
childNumber = 0,
|
childNumber = 0,
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
depth = 0,
|
depth = 0,
|
||||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
|
|
@ -281,7 +281,7 @@ object Wallet2WithSeedPhraseMockContent : MockContent {
|
||||||
childNumber = 0,
|
childNumber = 0,
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
depth = 0,
|
depth = 0,
|
||||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ object WalletMockContent : MockContent {
|
||||||
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
|
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey(
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey(
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/1'/0'/0/0") to ExtendedPublicKey(
|
DerivationPath("m/44'/1'/0'/0/0") to ExtendedPublicKey(
|
||||||
|
|
@ -125,10 +125,18 @@ object WalletMockContent : MockContent {
|
||||||
publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62),
|
publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62),
|
||||||
chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41),
|
chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41),
|
||||||
),
|
),
|
||||||
|
DerivationPath("m/1852'/1815'/0'/0/0") to ExtendedPublicKey(
|
||||||
|
publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62),
|
||||||
|
chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41),
|
||||||
|
),
|
||||||
DerivationPath("m/44'/144'/0'/0/0") to ExtendedPublicKey(
|
DerivationPath("m/44'/144'/0'/0/0") to ExtendedPublicKey(
|
||||||
publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62),
|
publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62),
|
||||||
chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41),
|
chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41),
|
||||||
),
|
),
|
||||||
|
DerivationPath("m/44'/501'/0'") to ExtendedPublicKey(
|
||||||
|
publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62),
|
||||||
|
chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
extendedPublicKey = ExtendedPublicKey(
|
extendedPublicKey = ExtendedPublicKey(
|
||||||
publicKey = byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5),
|
publicKey = byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5),
|
||||||
|
|
@ -194,7 +202,7 @@ object WalletMockContent : MockContent {
|
||||||
childNumber = 0,
|
childNumber = 0,
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
depth = 0,
|
depth = 0,
|
||||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
|
|
@ -235,6 +243,13 @@ object WalletMockContent : MockContent {
|
||||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
childNumber = 0,
|
childNumber = 0,
|
||||||
),
|
),
|
||||||
|
DerivationPath("m/44'/148'/0'") to ExtendedPublicKey( // XLM
|
||||||
|
publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62),
|
||||||
|
chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41),
|
||||||
|
depth = 0,
|
||||||
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
|
childNumber = 0,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ByteArrayKey(
|
ByteArrayKey(
|
||||||
|
|
@ -244,14 +259,21 @@ object WalletMockContent : MockContent {
|
||||||
ExtendedPublicKeysMap(
|
ExtendedPublicKeysMap(
|
||||||
mapOf(
|
mapOf(
|
||||||
DerivationPath("m/1852'/1815'/0'/0/0") to ExtendedPublicKey( // cardano
|
DerivationPath("m/1852'/1815'/0'/0/0") to ExtendedPublicKey( // cardano
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
depth = 0,
|
depth = 0,
|
||||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
childNumber = 0,
|
childNumber = 0,
|
||||||
),
|
),
|
||||||
DerivationPath("m/1852'/1815'/0'/2/0") to ExtendedPublicKey( // cardano extended
|
DerivationPath("m/1852'/1815'/0'/2/0") to ExtendedPublicKey( // cardano extended
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
|
depth = 0,
|
||||||
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
|
childNumber = 0,
|
||||||
|
),
|
||||||
|
DerivationPath("m/44'/501'/0'") to ExtendedPublicKey( // Solana
|
||||||
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
depth = 0,
|
depth = 0,
|
||||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
|
|
@ -290,14 +312,14 @@ object WalletMockContent : MockContent {
|
||||||
childNumber = 0,
|
childNumber = 0,
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
depth = 0,
|
depth = 0,
|
||||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
childNumber = 0,
|
childNumber = 0,
|
||||||
),
|
),
|
||||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // xrp
|
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // xrp
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
depth = 0,
|
depth = 0,
|
||||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
|
|
@ -312,19 +334,26 @@ object WalletMockContent : MockContent {
|
||||||
ExtendedPublicKeysMap(
|
ExtendedPublicKeysMap(
|
||||||
mapOf(
|
mapOf(
|
||||||
DerivationPath("m/1852'/1815'/0'/0/0") to ExtendedPublicKey( // cardano
|
DerivationPath("m/1852'/1815'/0'/0/0") to ExtendedPublicKey( // cardano
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
depth = 0,
|
depth = 0,
|
||||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
childNumber = 0,
|
childNumber = 0,
|
||||||
),
|
),
|
||||||
DerivationPath("m/1852'/1815'/0'/2/0") to ExtendedPublicKey( // cardano extended
|
DerivationPath("m/1852'/1815'/0'/2/0") to ExtendedPublicKey( // cardano extended
|
||||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82),
|
||||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||||
depth = 0,
|
depth = 0,
|
||||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
childNumber = 0,
|
childNumber = 0,
|
||||||
),
|
),
|
||||||
|
DerivationPath("m/44'/501'/0'") to ExtendedPublicKey( // Solana
|
||||||
|
publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62),
|
||||||
|
chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41),
|
||||||
|
depth = 0,
|
||||||
|
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||||
|
childNumber = 0,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,11 @@ import arrow.core.raise.either
|
||||||
import arrow.core.raise.ensure
|
import arrow.core.raise.ensure
|
||||||
import arrow.core.right
|
import arrow.core.right
|
||||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||||
import com.tangem.datasource.api.express.models.TangemExpressValues.EMPTY_CONTRACT_ADDRESS_VALUE
|
|
||||||
import com.tangem.datasource.api.express.models.response.Asset
|
|
||||||
import com.tangem.datasource.exchangeservice.swap.ExpressServiceLoader
|
|
||||||
import com.tangem.domain.core.lce.Lce
|
import com.tangem.domain.core.lce.Lce
|
||||||
import com.tangem.domain.exchange.ExpressAvailabilityState
|
import com.tangem.domain.exchange.ExpressAvailabilityState
|
||||||
import com.tangem.domain.exchange.RampStateManager
|
import com.tangem.domain.exchange.RampStateManager
|
||||||
|
import com.tangem.domain.express.ExpressServiceFetcher
|
||||||
|
import com.tangem.domain.express.models.ExpressAsset
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||||
import com.tangem.domain.models.wallet.UserWallet
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
|
|
@ -26,10 +25,9 @@ import com.tangem.utils.isNullOrZero
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.firstOrNull
|
import kotlinx.coroutines.flow.firstOrNull
|
||||||
|
|
||||||
@Suppress("LongParameterList")
|
|
||||||
internal class DefaultRampManager(
|
internal class DefaultRampManager(
|
||||||
private val sellService: Provider<ExchangeService>,
|
private val sellService: Provider<ExchangeService>,
|
||||||
private val expressServiceLoader: ExpressServiceLoader,
|
private val expressServiceFetcher: ExpressServiceFetcher,
|
||||||
private val currenciesRepository: CurrenciesRepository,
|
private val currenciesRepository: CurrenciesRepository,
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
excludedBlockchains: ExcludedBlockchains,
|
excludedBlockchains: ExcludedBlockchains,
|
||||||
|
|
@ -107,7 +105,7 @@ internal class DefaultRampManager(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getExpressInitializationStatus(userWalletId: UserWalletId): Flow<ExchangeServiceInitializationStatus> {
|
override fun getExpressInitializationStatus(userWalletId: UserWalletId): Flow<ExchangeServiceInitializationStatus> {
|
||||||
return expressServiceLoader.getInitializationStatus(userWalletId)
|
return expressServiceFetcher.getInitializationStatus(userWalletId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getSendUnavailabilityReason(
|
override suspend fun getSendUnavailabilityReason(
|
||||||
|
|
@ -151,14 +149,14 @@ internal class DefaultRampManager(
|
||||||
userWalletId: UserWalletId,
|
userWalletId: UserWalletId,
|
||||||
cryptoCurrency: CryptoCurrency,
|
cryptoCurrency: CryptoCurrency,
|
||||||
): ExpressAvailabilityState {
|
): ExpressAvailabilityState {
|
||||||
val asset = expressServiceLoader.getInitializationStatus(userWalletId).firstOrNull()
|
val asset = expressServiceFetcher.getInitializationStatus(userWalletId).firstOrNull()
|
||||||
?: return ExpressAvailabilityState.Loading
|
?: return ExpressAvailabilityState.Loading
|
||||||
return when (asset) {
|
return when (asset) {
|
||||||
is Lce.Error -> ExpressAvailabilityState.Error
|
is Lce.Error -> ExpressAvailabilityState.Error
|
||||||
is Lce.Loading -> ExpressAvailabilityState.Loading
|
is Lce.Loading -> ExpressAvailabilityState.Loading
|
||||||
is Lce.Content -> {
|
is Lce.Content -> {
|
||||||
val foundAsset = asset.getOrNull()?.find { cryptoCurrency.findAssetPredicate(it) }
|
val foundAsset = asset.getOrNull()?.find { cryptoCurrency.findAssetPredicate(assetId = it.id) }
|
||||||
foundAsset?.exchangeAvailable?.toSwapAvailabilityState()
|
foundAsset?.isExchangeAvailable?.toSwapAvailabilityState()
|
||||||
?: ExpressAvailabilityState.AssetNotFound
|
?: ExpressAvailabilityState.AssetNotFound
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -168,15 +166,15 @@ internal class DefaultRampManager(
|
||||||
userWalletId: UserWalletId,
|
userWalletId: UserWalletId,
|
||||||
cryptoCurrency: CryptoCurrency,
|
cryptoCurrency: CryptoCurrency,
|
||||||
): ExpressAvailabilityState {
|
): ExpressAvailabilityState {
|
||||||
val asset = expressServiceLoader.getInitializationStatus(userWalletId).firstOrNull()
|
val asset = expressServiceFetcher.getInitializationStatus(userWalletId).firstOrNull()
|
||||||
?: return ExpressAvailabilityState.Loading
|
?: return ExpressAvailabilityState.Loading
|
||||||
|
|
||||||
return when (asset) {
|
return when (asset) {
|
||||||
is Lce.Error -> ExpressAvailabilityState.Error
|
is Lce.Error -> ExpressAvailabilityState.Error
|
||||||
is Lce.Loading -> ExpressAvailabilityState.Loading
|
is Lce.Loading -> ExpressAvailabilityState.Loading
|
||||||
is Lce.Content -> {
|
is Lce.Content -> {
|
||||||
val foundAsset = asset.getOrNull()?.find { cryptoCurrency.findAssetPredicate(it) }
|
val foundAsset = asset.getOrNull()?.find { cryptoCurrency.findAssetPredicate(assetId = it.id) }
|
||||||
foundAsset?.onrampAvailable?.toOnrampAvailabilityState()
|
foundAsset?.isOnrampAvailable?.toOnrampAvailabilityState()
|
||||||
?: ExpressAvailabilityState.AssetNotFound
|
?: ExpressAvailabilityState.AssetNotFound
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -211,8 +209,13 @@ internal class DefaultRampManager(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CryptoCurrency.findAssetPredicate(asset: Asset): Boolean {
|
private fun CryptoCurrency.findAssetPredicate(assetId: ExpressAsset.ID): Boolean {
|
||||||
val contractAddress = (this as? CryptoCurrency.Token)?.contractAddress ?: EMPTY_CONTRACT_ADDRESS_VALUE
|
val currencyAssedId = ExpressAsset.ID(
|
||||||
return asset.network == network.backendId && asset.contractAddress.equals(contractAddress, ignoreCase = true)
|
networkId = this.network.backendId,
|
||||||
|
contractAddress = (this as? CryptoCurrency.Token)?.contractAddress,
|
||||||
|
)
|
||||||
|
|
||||||
|
return assetId.networkId == currencyAssedId.networkId &&
|
||||||
|
assetId.contractAddress.equals(currencyAssedId.contractAddress, ignoreCase = true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -160,6 +160,6 @@ internal val Blockchain.moonPaySupportedCurrency: MoonPaySupportedCurrency?
|
||||||
Pepecoin, PepecoinTestnet -> null
|
Pepecoin, PepecoinTestnet -> null
|
||||||
Hyperliquid, HyperliquidTestnet -> null
|
Hyperliquid, HyperliquidTestnet -> null
|
||||||
Quai, QuaiTestnet -> null
|
Quai, QuaiTestnet -> null
|
||||||
// Linea, LineaTestnet -> null
|
Linea, LineaTestnet -> null
|
||||||
// ArbitrumNova -> null
|
ArbitrumNova -> null
|
||||||
}
|
}
|
||||||
|
|
@ -36,9 +36,10 @@ import com.tangem.features.send.v2.api.SendComponent
|
||||||
import com.tangem.features.send.v2.api.SendEntryPointComponent
|
import com.tangem.features.send.v2.api.SendEntryPointComponent
|
||||||
import com.tangem.features.staking.api.StakingComponent
|
import com.tangem.features.staking.api.StakingComponent
|
||||||
import com.tangem.features.swap.SwapComponent
|
import com.tangem.features.swap.SwapComponent
|
||||||
import com.tangem.features.tangempay.components.TangemPayDetailsComponent
|
import com.tangem.features.tangempay.components.TangemPayDetailsContainerComponent
|
||||||
import com.tangem.features.tangempay.components.TangemPayOnboardingComponent
|
import com.tangem.features.tangempay.components.TangemPayOnboardingComponent
|
||||||
import com.tangem.features.tangempay.components.TangemPayOnboardingComponent.Params.*
|
import com.tangem.features.tangempay.components.TangemPayOnboardingComponent.Params.ContinueOnboarding
|
||||||
|
import com.tangem.features.tangempay.components.TangemPayOnboardingComponent.Params.Deeplink
|
||||||
import com.tangem.features.tokendetails.TokenDetailsComponent
|
import com.tangem.features.tokendetails.TokenDetailsComponent
|
||||||
import com.tangem.features.wallet.WalletEntryComponent
|
import com.tangem.features.wallet.WalletEntryComponent
|
||||||
import com.tangem.features.walletconnect.components.WalletConnectEntryComponent
|
import com.tangem.features.walletconnect.components.WalletConnectEntryComponent
|
||||||
|
|
@ -100,6 +101,7 @@ internal class ChildFactory @Inject constructor(
|
||||||
private val chooseManagedTokensComponentFactory: ChooseManagedTokensComponent.Factory,
|
private val chooseManagedTokensComponentFactory: ChooseManagedTokensComponent.Factory,
|
||||||
private val createWalletSelectionComponentFactory: CreateWalletSelectionComponent.Factory,
|
private val createWalletSelectionComponentFactory: CreateWalletSelectionComponent.Factory,
|
||||||
private val createWalletStartComponentFactory: CreateWalletStartComponent.Factory,
|
private val createWalletStartComponentFactory: CreateWalletStartComponent.Factory,
|
||||||
|
private val createHardwareWalletComponentFactory: CreateHardwareWalletComponent.Factory,
|
||||||
private val createMobileWalletComponentFactory: CreateMobileWalletComponent.Factory,
|
private val createMobileWalletComponentFactory: CreateMobileWalletComponent.Factory,
|
||||||
private val upgradeWalletComponentFactory: UpgradeWalletComponent.Factory,
|
private val upgradeWalletComponentFactory: UpgradeWalletComponent.Factory,
|
||||||
private val addExistingWalletComponentFactory: AddExistingWalletComponent.Factory,
|
private val addExistingWalletComponentFactory: AddExistingWalletComponent.Factory,
|
||||||
|
|
@ -107,8 +109,9 @@ internal class ChildFactory @Inject constructor(
|
||||||
private val createWalletBackupComponentFactory: CreateWalletBackupComponent.Factory,
|
private val createWalletBackupComponentFactory: CreateWalletBackupComponent.Factory,
|
||||||
private val updateAccessCodeComponentFactory: UpdateAccessCodeComponent.Factory,
|
private val updateAccessCodeComponentFactory: UpdateAccessCodeComponent.Factory,
|
||||||
private val viewPhraseComponentFactory: ViewPhraseComponent.Factory,
|
private val viewPhraseComponentFactory: ViewPhraseComponent.Factory,
|
||||||
|
private val forgetWalletComponentFactory: ForgetWalletComponent.Factory,
|
||||||
private val sendEntryPointComponentFactory: SendEntryPointComponent.Factory,
|
private val sendEntryPointComponentFactory: SendEntryPointComponent.Factory,
|
||||||
private val tangemPayDetailsComponentFactory: TangemPayDetailsComponent.Factory,
|
private val tangemPayDetailsContainerComponentFactory: TangemPayDetailsContainerComponent.Factory,
|
||||||
private val tangemPayOnboardingComponentFactory: TangemPayOnboardingComponent.Factory,
|
private val tangemPayOnboardingComponentFactory: TangemPayOnboardingComponent.Factory,
|
||||||
private val kycComponentFactory: KycComponent.Factory,
|
private val kycComponentFactory: KycComponent.Factory,
|
||||||
private val yieldSupplyPromoComponentFactory: YieldSupplyPromoComponent.Factory,
|
private val yieldSupplyPromoComponentFactory: YieldSupplyPromoComponent.Factory,
|
||||||
|
|
@ -138,7 +141,6 @@ internal class ChildFactory @Inject constructor(
|
||||||
is AppRoute.ManageTokens -> {
|
is AppRoute.ManageTokens -> {
|
||||||
val source = when (route.source) {
|
val source = when (route.source) {
|
||||||
AppRoute.ManageTokens.Source.SETTINGS -> ManageTokensSource.SETTINGS
|
AppRoute.ManageTokens.Source.SETTINGS -> ManageTokensSource.SETTINGS
|
||||||
AppRoute.ManageTokens.Source.ONBOARDING -> ManageTokensSource.ONBOARDING
|
|
||||||
AppRoute.ManageTokens.Source.STORIES -> ManageTokensSource.STORIES
|
AppRoute.ManageTokens.Source.STORIES -> ManageTokensSource.STORIES
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -293,7 +295,7 @@ internal class ChildFactory @Inject constructor(
|
||||||
context = context,
|
context = context,
|
||||||
params = StakingComponent.Params(
|
params = StakingComponent.Params(
|
||||||
userWalletId = route.userWalletId,
|
userWalletId = route.userWalletId,
|
||||||
cryptoCurrencyId = route.cryptoCurrencyId,
|
cryptoCurrency = route.cryptoCurrency,
|
||||||
yieldId = route.yieldId,
|
yieldId = route.yieldId,
|
||||||
),
|
),
|
||||||
componentFactory = stakingComponentFactory,
|
componentFactory = stakingComponentFactory,
|
||||||
|
|
@ -496,6 +498,13 @@ internal class ChildFactory @Inject constructor(
|
||||||
componentFactory = createWalletSelectionComponentFactory,
|
componentFactory = createWalletSelectionComponentFactory,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
is AppRoute.CreateHardwareWallet -> {
|
||||||
|
createComponentChild(
|
||||||
|
context = context,
|
||||||
|
params = Unit,
|
||||||
|
componentFactory = createHardwareWalletComponentFactory,
|
||||||
|
)
|
||||||
|
}
|
||||||
is AppRoute.CreateMobileWallet -> {
|
is AppRoute.CreateMobileWallet -> {
|
||||||
createComponentChild(
|
createComponentChild(
|
||||||
context = context,
|
context = context,
|
||||||
|
|
@ -555,6 +564,15 @@ internal class ChildFactory @Inject constructor(
|
||||||
componentFactory = viewPhraseComponentFactory,
|
componentFactory = viewPhraseComponentFactory,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
is AppRoute.ForgetWallet -> {
|
||||||
|
createComponentChild(
|
||||||
|
context = context,
|
||||||
|
params = ForgetWalletComponent.Params(
|
||||||
|
userWalletId = route.userWalletId,
|
||||||
|
),
|
||||||
|
componentFactory = forgetWalletComponentFactory,
|
||||||
|
)
|
||||||
|
}
|
||||||
is AppRoute.SendEntryPoint -> {
|
is AppRoute.SendEntryPoint -> {
|
||||||
createComponentChild(
|
createComponentChild(
|
||||||
context = context,
|
context = context,
|
||||||
|
|
@ -604,8 +622,11 @@ internal class ChildFactory @Inject constructor(
|
||||||
is AppRoute.TangemPayDetails -> {
|
is AppRoute.TangemPayDetails -> {
|
||||||
createComponentChild(
|
createComponentChild(
|
||||||
context = context,
|
context = context,
|
||||||
params = TangemPayDetailsComponent.Params(config = route.config),
|
params = TangemPayDetailsContainerComponent.Params(
|
||||||
componentFactory = tangemPayDetailsComponentFactory,
|
userWalletId = route.userWalletId,
|
||||||
|
config = route.config,
|
||||||
|
),
|
||||||
|
componentFactory = tangemPayDetailsContainerComponentFactory,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is AppRoute.TangemPayOnboarding -> {
|
is AppRoute.TangemPayOnboarding -> {
|
||||||
|
|
|
||||||
|
|
@ -126,9 +126,12 @@ sealed class AppRoute(val path: String) : Route {
|
||||||
val portfolioId: PortfolioId? = null,
|
val portfolioId: PortfolioId? = null,
|
||||||
) : AppRoute(path = "${source.name.lowercase()}/manage_tokens/${portfolioId?.stringValue}") {
|
) : AppRoute(path = "${source.name.lowercase()}/manage_tokens/${portfolioId?.stringValue}") {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Source of launching the screen.
|
||||||
|
* ManageTokens screen launched from Onboarding by another route. See `OnboardingRoute.ManageTokens`.
|
||||||
|
*/
|
||||||
enum class Source {
|
enum class Source {
|
||||||
STORIES,
|
STORIES,
|
||||||
ONBOARDING,
|
|
||||||
SETTINGS,
|
SETTINGS,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -192,9 +195,9 @@ sealed class AppRoute(val path: String) : Route {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Staking(
|
data class Staking(
|
||||||
val userWalletId: UserWalletId,
|
val userWalletId: UserWalletId,
|
||||||
val cryptoCurrencyId: CryptoCurrency.ID,
|
val cryptoCurrency: CryptoCurrency,
|
||||||
val yieldId: String,
|
val yieldId: String,
|
||||||
) : AppRoute(path = "/staking/${userWalletId.stringValue}/${cryptoCurrencyId.value}/$yieldId")
|
) : AppRoute(path = "/staking/${userWalletId.stringValue}/${cryptoCurrency.id.value}/$yieldId")
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class PushNotification(
|
data class PushNotification(
|
||||||
|
|
@ -322,6 +325,9 @@ sealed class AppRoute(val path: String) : Route {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
object CreateHardwareWallet : AppRoute(path = "/create_hardware_wallet")
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
object CreateMobileWallet : AppRoute(path = "/create_mobile_wallet")
|
object CreateMobileWallet : AppRoute(path = "/create_mobile_wallet")
|
||||||
|
|
||||||
|
|
@ -353,6 +359,11 @@ sealed class AppRoute(val path: String) : Route {
|
||||||
val userWalletId: UserWalletId,
|
val userWalletId: UserWalletId,
|
||||||
) : AppRoute(path = "/view_seed_phrase/${userWalletId.stringValue}")
|
) : AppRoute(path = "/view_seed_phrase/${userWalletId.stringValue}")
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ForgetWallet(
|
||||||
|
val userWalletId: UserWalletId,
|
||||||
|
) : AppRoute(path = "/forget_wallet/${userWalletId.stringValue}")
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class SendEntryPoint(
|
data class SendEntryPoint(
|
||||||
val userWalletId: UserWalletId,
|
val userWalletId: UserWalletId,
|
||||||
|
|
@ -383,8 +394,9 @@ sealed class AppRoute(val path: String) : Route {
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class TangemPayDetails(
|
data class TangemPayDetails(
|
||||||
|
val userWalletId: UserWalletId,
|
||||||
val config: TangemPayDetailsConfig,
|
val config: TangemPayDetailsConfig,
|
||||||
) : AppRoute(path = "/tangem_pay_details")
|
) : AppRoute(path = "/tangem_pay_details/${userWalletId.stringValue}")
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class TangemPayOnboarding(
|
data class TangemPayOnboarding(
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package com.tangem.common.ui.account
|
||||||
import com.tangem.common.ui.R
|
import com.tangem.common.ui.R
|
||||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||||
import com.tangem.core.ui.components.token.state.TokenItemState.FiatAmountState
|
import com.tangem.core.ui.components.token.state.TokenItemState.FiatAmountState
|
||||||
|
import com.tangem.core.ui.components.token.state.TokenItemState.Subtitle2State
|
||||||
import com.tangem.core.ui.extensions.pluralReference
|
import com.tangem.core.ui.extensions.pluralReference
|
||||||
import com.tangem.core.ui.extensions.wrappedList
|
import com.tangem.core.ui.extensions.wrappedList
|
||||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||||
|
|
@ -56,8 +57,8 @@ class AccountCryptoPortfolioItemStateConverter(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Account.CryptoPortfolio.mapToLoadingState(): TokenItemState.Loading {
|
private fun Account.CryptoPortfolio.mapToLoadingState(): TokenItemState.Content {
|
||||||
return TokenItemState.Loading(
|
return TokenItemState.Content(
|
||||||
id = account.accountId.value,
|
id = account.accountId.value,
|
||||||
iconState = AccountIconItemStateConverter.convert(account),
|
iconState = AccountIconItemStateConverter.convert(account),
|
||||||
titleState = TokenItemState.TitleState.Content(
|
titleState = TokenItemState.TitleState.Content(
|
||||||
|
|
@ -71,6 +72,10 @@ class AccountCryptoPortfolioItemStateConverter(
|
||||||
),
|
),
|
||||||
isAvailable = false,
|
isAvailable = false,
|
||||||
),
|
),
|
||||||
|
fiatAmountState = FiatAmountState.Loading,
|
||||||
|
subtitle2State = Subtitle2State.Loading,
|
||||||
|
onItemLongClick = null,
|
||||||
|
onItemClick = onItemClick?.let { onItemClick -> { onItemClick(account) } },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.testTag
|
import androidx.compose.ui.platform.testTag
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
@ -29,6 +30,7 @@ fun AccountTitle(
|
||||||
accountTitleUM: AccountTitleUM,
|
accountTitleUM: AccountTitleUM,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
textStyle: TextStyle = TangemTheme.typography.subtitle2,
|
textStyle: TextStyle = TangemTheme.typography.subtitle2,
|
||||||
|
textColor: Color = TangemTheme.colors.text.tertiary,
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
|
@ -40,19 +42,20 @@ fun AccountTitle(
|
||||||
Text(
|
Text(
|
||||||
text = accountTitleUM.prefixText.resolveReference(),
|
text = accountTitleUM.prefixText.resolveReference(),
|
||||||
style = textStyle,
|
style = textStyle,
|
||||||
color = TangemTheme.colors.text.tertiary,
|
color = textColor,
|
||||||
)
|
)
|
||||||
AccountLabel(
|
AccountLabel(
|
||||||
name = accountTitleUM.name,
|
name = accountTitleUM.name,
|
||||||
icon = accountTitleUM.icon,
|
icon = accountTitleUM.icon,
|
||||||
iconSize = AccountIconSize.ExtraSmall,
|
iconSize = AccountIconSize.ExtraSmall,
|
||||||
nameStyle = textStyle,
|
nameStyle = textStyle,
|
||||||
|
nameColor = textColor,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is AccountTitleUM.Text -> Text(
|
is AccountTitleUM.Text -> Text(
|
||||||
text = accountTitleUM.title.resolveReference(),
|
text = accountTitleUM.title.resolveReference(),
|
||||||
style = textStyle,
|
style = textStyle,
|
||||||
color = TangemTheme.colors.text.tertiary,
|
color = textColor,
|
||||||
modifier = Modifier.testTag(SendScreenTestTags.AMOUNT_CONTAINER_TITLE),
|
modifier = Modifier.testTag(SendScreenTestTags.AMOUNT_CONTAINER_TITLE),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package com.tangem.core.analytics.utils
|
package com.tangem.core.analytics.utils
|
||||||
|
|
||||||
import com.tangem.domain.models.scan.ScanResponse
|
import com.tangem.domain.models.scan.ScanResponse
|
||||||
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
|
|
||||||
/**
|
/**
|
||||||
[REDACTED_AUTHOR]
|
[REDACTED_AUTHOR]
|
||||||
|
|
@ -9,9 +10,15 @@ interface AnalyticsContextProxy {
|
||||||
|
|
||||||
fun setContext(scanResponse: ScanResponse)
|
fun setContext(scanResponse: ScanResponse)
|
||||||
|
|
||||||
|
fun addContext(userWallet: UserWallet)
|
||||||
|
|
||||||
|
fun setHotWalletContext()
|
||||||
|
|
||||||
fun eraseContext()
|
fun eraseContext()
|
||||||
|
|
||||||
fun addContext(scanResponse: ScanResponse)
|
fun addContext(scanResponse: ScanResponse)
|
||||||
|
|
||||||
|
fun addHotWalletContext()
|
||||||
|
|
||||||
fun removeContext()
|
fun removeContext()
|
||||||
}
|
}
|
||||||
|
|
@ -30,9 +30,5 @@
|
||||||
{
|
{
|
||||||
"name": "zklink",
|
"name": "zklink",
|
||||||
"version": "undefined"
|
"version": "undefined"
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "scroll",
|
|
||||||
"version": "undefined"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
package com.tangem.datasource.api.express.models
|
|
||||||
|
|
||||||
object TangemExpressValues {
|
|
||||||
const val EMPTY_CONTRACT_ADDRESS_VALUE = "0"
|
|
||||||
}
|
|
||||||
|
|
@ -21,3 +21,17 @@ data class GetWalletAccountsResponse(
|
||||||
@Json(name = "totalAccounts") val totalAccounts: Int,
|
@Json(name = "totalAccounts") val totalAccounts: Int,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Flattens the tokens from all wallet accounts into a single list */
|
||||||
|
fun GetWalletAccountsResponse.flattenTokens(): List<UserTokensResponse.Token> {
|
||||||
|
return accounts.flatMap { it.tokens.orEmpty() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Converts the [GetWalletAccountsResponse] into a [UserTokensResponse] */
|
||||||
|
fun GetWalletAccountsResponse.toUserTokensResponse(): UserTokensResponse {
|
||||||
|
return UserTokensResponse(
|
||||||
|
group = wallet.group,
|
||||||
|
sort = wallet.sort,
|
||||||
|
tokens = flattenTokens(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
package com.tangem.datasource.di.exchangeservice
|
|
||||||
|
|
||||||
import com.tangem.datasource.exchangeservice.swap.DefaultExpressServiceLoader
|
|
||||||
import com.tangem.datasource.exchangeservice.swap.ExpressServiceLoader
|
|
||||||
import dagger.Binds
|
|
||||||
import dagger.Module
|
|
||||||
import dagger.hilt.InstallIn
|
|
||||||
import dagger.hilt.components.SingletonComponent
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Module
|
|
||||||
@InstallIn(SingletonComponent::class)
|
|
||||||
internal interface ExchangeServiceLoaderModule {
|
|
||||||
|
|
||||||
@Binds
|
|
||||||
@Singleton
|
|
||||||
fun bindExpressServiceLoader(defaultExpressServiceLoader: DefaultExpressServiceLoader): ExpressServiceLoader
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
package com.tangem.datasource.exchangeservice.swap
|
|
||||||
|
|
||||||
import com.tangem.datasource.api.express.models.request.LeastTokenInfo
|
|
||||||
import com.tangem.datasource.api.express.models.response.Asset
|
|
||||||
import com.tangem.domain.core.lce.Lce
|
|
||||||
import com.tangem.domain.models.wallet.UserWallet
|
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Express service loader
|
|
||||||
*
|
|
||||||
[REDACTED_AUTHOR]
|
|
||||||
*/
|
|
||||||
interface ExpressServiceLoader {
|
|
||||||
|
|
||||||
/** Update service using [userWallet] and [userTokens] */
|
|
||||||
suspend fun update(userWallet: UserWallet, userTokens: List<LeastTokenInfo>)
|
|
||||||
|
|
||||||
/** Get initialization status by [userWalletId] */
|
|
||||||
fun getInitializationStatus(userWalletId: UserWalletId): Flow<Lce<Throwable, List<Asset>>>
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
package com.tangem.core.ui.components.feature
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.tangem.core.ui.R
|
||||||
|
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||||
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
|
import com.tangem.core.ui.res.TangemThemePreview
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun FeatureBlock(title: String, description: String, iconRes: Int, modifier: Modifier = Modifier) {
|
||||||
|
Row(
|
||||||
|
modifier = modifier,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 12.dp),
|
||||||
|
painter = painterResource(iconRes),
|
||||||
|
contentDescription = null,
|
||||||
|
tint = TangemTheme.colors.icon.primary1,
|
||||||
|
)
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 8.dp),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
style = TangemTheme.typography.subtitle1,
|
||||||
|
color = TangemTheme.colors.text.primary1,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(top = 4.dp),
|
||||||
|
text = description,
|
||||||
|
style = TangemTheme.typography.body2,
|
||||||
|
color = TangemTheme.colors.text.secondary,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(showBackground = true, widthDp = 360)
|
||||||
|
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
@Composable
|
||||||
|
private fun PreviewFeatureBlock() {
|
||||||
|
TangemThemePreview {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.background(TangemTheme.colors.background.primary)
|
||||||
|
.padding(16.dp),
|
||||||
|
) {
|
||||||
|
FeatureBlock(
|
||||||
|
title = stringResourceSafe(R.string.backup_info_save_title),
|
||||||
|
description = stringResourceSafe(R.string.backup_info_save_description, "12"),
|
||||||
|
iconRes = R.drawable.ic_lock_24,
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
|
FeatureBlock(
|
||||||
|
title = stringResourceSafe(R.string.backup_info_keep_title),
|
||||||
|
description = stringResourceSafe(R.string.backup_info_keep_description),
|
||||||
|
iconRes = R.drawable.ic_settings_24,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -21,7 +21,7 @@ import com.tangem.core.ui.utils.getGreyScaleColorFilter
|
||||||
* @param onImageError composable to show if image loading failed
|
* @param onImageError composable to show if image loading failed
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
internal fun InputRowAsyncImage(
|
fun InputRowAsyncImage(
|
||||||
imageUrl: String,
|
imageUrl: String,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
isGrayscale: Boolean = false,
|
isGrayscale: Boolean = false,
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ fun SelectorRowItem(
|
||||||
val textStyle = if (isSelected && showSelectedAppearance) {
|
val textStyle = if (isSelected && showSelectedAppearance) {
|
||||||
TangemTheme.typography.subtitle2
|
TangemTheme.typography.subtitle2
|
||||||
} else {
|
} else {
|
||||||
TangemTheme.typography.body2
|
TangemTheme.typography.body1
|
||||||
}
|
}
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.tangem.core.ui.extensions
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for keeping themed color reference from app theme.
|
||||||
|
*
|
||||||
|
* It necessary to use [Immutable] annotation for runtime stability.
|
||||||
|
*
|
||||||
|
* @property value color provider from theme
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
data class ColorReference(val value: @Composable () -> Color)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a [ColorReference] using a themed color from the app theme with a lambda.
|
||||||
|
*
|
||||||
|
* @param value The color provider from theme.
|
||||||
|
* @return A [ColorReference] representing the themed color.
|
||||||
|
*/
|
||||||
|
fun themedColor(value: @Composable () -> Color): ColorReference {
|
||||||
|
return ColorReference(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves [ColorReference] to [Color]
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun ColorReference.resolveReference(): Color {
|
||||||
|
return value()
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package com.tangem.core.ui.test
|
package com.tangem.core.ui.test
|
||||||
|
|
||||||
object BaseAmountBlockTestTags {
|
object BaseAmountBlockTestTags {
|
||||||
const val PRIMARY_AMOUNT = "STAKING_SEND_DETAILS_SCREEN_PRIMARY_AMOUNT"
|
const val PRIMARY_AMOUNT = "SEND_DETAILS_SCREEN_PRIMARY_AMOUNT"
|
||||||
const val SECONDARY_AMOUNT = "TAKING_SEND_DETAILS_SCREEN_SECONDARY_AMOUNT"
|
const val SECONDARY_AMOUNT = "SEND_DETAILS_SCREEN_SECONDARY_AMOUNT"
|
||||||
}
|
}
|
||||||
15
core/ui/src/main/res/drawable/ic_attention_72.xml
Normal file
15
core/ui/src/main/res/drawable/ic_attention_72.xml
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="72dp"
|
||||||
|
android:height="72dp"
|
||||||
|
android:viewportWidth="72"
|
||||||
|
android:viewportHeight="72">
|
||||||
|
<path
|
||||||
|
android:pathData="M69.781,45.692L69.79,45.709C71.126,48.06 72,50.879 72,53.877C72,57.927 70.612,62.075 67.529,65.265C64.394,68.508 60.145,70.096 55.73,70.096H16.27C11.855,70.096 7.606,68.508 4.471,65.265C1.388,62.075 0,57.927 0,53.877C0,51.094 0.713,48.238 2.266,45.611L21.966,11.287C25.02,5.881 30.514,3.086 36,3.086C41.553,3.086 46.97,5.934 50.018,11.26C50.02,11.263 50.022,11.266 50.023,11.269L69.781,45.692ZM41.932,15.893L61.688,50.313C62.324,51.433 62.68,52.681 62.68,53.877C62.68,57.772 60.058,60.777 55.73,60.777H16.27C11.942,60.777 9.32,57.772 9.32,53.877C9.32,52.681 9.625,51.459 10.312,50.313L30.068,15.893C31.367,13.577 33.658,12.406 36,12.406C38.342,12.406 40.608,13.577 41.932,15.893Z"
|
||||||
|
android:strokeAlpha="0.12"
|
||||||
|
android:fillColor="#FF3333"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:fillAlpha="0.12"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M36,9.771C39.182,9.771 42.273,11.382 44.047,14.484L44.049,14.486L63.49,48.36C64.308,49.799 64.8,51.456 64.8,53.126C64.8,55.617 63.955,57.976 62.262,59.728C60.554,61.494 58.159,62.462 55.414,62.462H16.586C13.842,62.462 11.446,61.494 9.738,59.728C8.045,57.976 7.2,55.617 7.2,53.126C7.2,51.514 7.613,49.864 8.526,48.331L27.948,14.493C29.705,11.368 32.836,9.771 36,9.771ZM36,12.318C33.695,12.318 31.441,13.471 30.163,15.751L10.725,49.618C10.048,50.745 9.747,51.949 9.747,53.126C9.747,56.958 12.328,59.914 16.586,59.914H55.414C59.673,59.914 62.253,56.958 62.253,53.126C62.253,51.949 61.902,50.72 61.275,49.618L41.837,15.751C40.534,13.471 38.305,12.318 36,12.318ZM34.984,45.657C35.942,45.657 36.718,46.433 36.718,47.391C36.718,48.348 35.942,49.124 34.984,49.124C34.027,49.124 33.251,48.348 33.251,47.391C33.251,46.433 34.027,45.657 34.984,45.657ZM34.984,25.845C35.805,25.845 36.471,26.51 36.471,27.331V42.189C36.471,43.01 35.805,43.676 34.984,43.676C34.164,43.676 33.499,43.01 33.499,42.189V27.331C33.499,26.511 34.164,25.845 34.984,25.845Z"
|
||||||
|
android:fillColor="#FF3333"/>
|
||||||
|
</vector>
|
||||||
|
|
@ -27,6 +27,7 @@ dependencies {
|
||||||
api(projects.domain.common)
|
api(projects.domain.common)
|
||||||
api(projects.domain.models)
|
api(projects.domain.models)
|
||||||
api(projects.domain.tokens)
|
api(projects.domain.tokens)
|
||||||
|
api(projects.domain.wallets)
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region Project - Data
|
// region Project - Data
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.tangem.data.account.di
|
||||||
|
|
||||||
|
import com.tangem.data.account.producer.DefaultSingleAccountProducer
|
||||||
|
import com.tangem.domain.account.producer.SingleAccountProducer
|
||||||
|
import dagger.Binds
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
internal interface SingleAccountProducerFactoryModule {
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@Singleton
|
||||||
|
fun bindSingleAccountProducerFactory(impl: DefaultSingleAccountProducer.Factory): SingleAccountProducer.Factory
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.tangem.data.account.di
|
||||||
|
|
||||||
|
import com.tangem.domain.account.producer.SingleAccountProducer
|
||||||
|
import com.tangem.domain.account.supplier.SingleAccountSupplier
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
internal object SingleAccountSupplierModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideSingleAccountSupplier(factory: SingleAccountProducer.Factory): SingleAccountSupplier {
|
||||||
|
return object : SingleAccountSupplier(
|
||||||
|
factory = factory,
|
||||||
|
keyCreator = { "single_account_${it.accountId.value}" },
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,7 +4,6 @@ import com.tangem.data.account.store.AccountsResponseStore
|
||||||
import com.tangem.data.account.store.AccountsResponseStoreFactory
|
import com.tangem.data.account.store.AccountsResponseStoreFactory
|
||||||
import com.tangem.data.account.utils.DefaultWalletAccountsResponseFactory
|
import com.tangem.data.account.utils.DefaultWalletAccountsResponseFactory
|
||||||
import com.tangem.data.account.utils.assignTokens
|
import com.tangem.data.account.utils.assignTokens
|
||||||
import com.tangem.data.account.utils.toUserTokensResponse
|
|
||||||
import com.tangem.data.common.account.WalletAccountsFetcher
|
import com.tangem.data.common.account.WalletAccountsFetcher
|
||||||
import com.tangem.data.common.account.WalletAccountsSaver
|
import com.tangem.data.common.account.WalletAccountsSaver
|
||||||
import com.tangem.data.common.api.safeApiCall
|
import com.tangem.data.common.api.safeApiCall
|
||||||
|
|
@ -19,6 +18,7 @@ import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||||
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
||||||
import com.tangem.datasource.api.tangemTech.models.account.SaveWalletAccountsResponse
|
import com.tangem.datasource.api.tangemTech.models.account.SaveWalletAccountsResponse
|
||||||
import com.tangem.datasource.api.tangemTech.models.account.WalletAccountDTO
|
import com.tangem.datasource.api.tangemTech.models.account.WalletAccountDTO
|
||||||
|
import com.tangem.datasource.api.tangemTech.models.account.toUserTokensResponse
|
||||||
import com.tangem.datasource.utils.getSyncOrNull
|
import com.tangem.datasource.utils.getSyncOrNull
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
|
|
@ -51,18 +51,25 @@ internal class DefaultWalletAccountsFetcher @Inject constructor(
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
) : WalletAccountsFetcher, WalletAccountsSaver {
|
) : WalletAccountsFetcher, WalletAccountsSaver {
|
||||||
|
|
||||||
override suspend fun fetch(userWalletId: UserWalletId) {
|
override suspend fun fetch(userWalletId: UserWalletId): GetWalletAccountsResponse {
|
||||||
val savedAccountsResponse = getAccountsResponseStore(userWalletId = userWalletId).getSyncOrNull()
|
val savedAccountsResponse = getAccountsResponseStore(userWalletId = userWalletId).getSyncOrNull()
|
||||||
val accountsResponse = fetchWalletAccounts(userWalletId, savedAccountsResponse)
|
val accountsResponse = fetchWalletAccounts(userWalletId, savedAccountsResponse)
|
||||||
?: return
|
|
||||||
|
|
||||||
if (accountsResponse.accounts.isEmpty()) {
|
return when {
|
||||||
initializeAccounts(userWalletId, accountsResponse)
|
accountsResponse.accounts.isEmpty() -> {
|
||||||
} else if (accountsResponse.unassignedTokens.isNotEmpty()) {
|
initializeAccounts(userWalletId, accountsResponse)
|
||||||
assignTokens(userWalletId, accountsResponse)
|
}
|
||||||
|
accountsResponse.unassignedTokens.isNotEmpty() -> {
|
||||||
|
assignTokens(userWalletId, accountsResponse)
|
||||||
|
}
|
||||||
|
else -> accountsResponse
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun getSaved(userWalletId: UserWalletId): GetWalletAccountsResponse? {
|
||||||
|
return getAccountsResponseStore(userWalletId = userWalletId).getSyncOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun store(userWalletId: UserWalletId, response: GetWalletAccountsResponse) {
|
override suspend fun store(userWalletId: UserWalletId, response: GetWalletAccountsResponse) {
|
||||||
val store = getAccountsResponseStore(userWalletId = userWalletId)
|
val store = getAccountsResponseStore(userWalletId = userWalletId)
|
||||||
|
|
||||||
|
|
@ -115,7 +122,7 @@ internal class DefaultWalletAccountsFetcher @Inject constructor(
|
||||||
private suspend fun fetchWalletAccounts(
|
private suspend fun fetchWalletAccounts(
|
||||||
userWalletId: UserWalletId,
|
userWalletId: UserWalletId,
|
||||||
savedAccountsResponse: GetWalletAccountsResponse?,
|
savedAccountsResponse: GetWalletAccountsResponse?,
|
||||||
): GetWalletAccountsResponse? {
|
): GetWalletAccountsResponse {
|
||||||
return safeApiCall(
|
return safeApiCall(
|
||||||
call = {
|
call = {
|
||||||
val apiResponse = withContext(dispatchers.io) {
|
val apiResponse = withContext(dispatchers.io) {
|
||||||
|
|
@ -145,7 +152,10 @@ internal class DefaultWalletAccountsFetcher @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun initializeAccounts(userWalletId: UserWalletId, accountsResponse: GetWalletAccountsResponse) {
|
private suspend fun initializeAccounts(
|
||||||
|
userWalletId: UserWalletId,
|
||||||
|
accountsResponse: GetWalletAccountsResponse,
|
||||||
|
): GetWalletAccountsResponse {
|
||||||
val response = defaultWalletAccountsResponseFactory.create(
|
val response = defaultWalletAccountsResponseFactory.create(
|
||||||
userWalletId = userWalletId,
|
userWalletId = userWalletId,
|
||||||
userTokensResponse = UserTokensResponse(
|
userTokensResponse = UserTokensResponse(
|
||||||
|
|
@ -156,14 +166,17 @@ internal class DefaultWalletAccountsFetcher @Inject constructor(
|
||||||
)
|
)
|
||||||
|
|
||||||
userTokensSaver.push(userWalletId = userWalletId, response = response.toUserTokensResponse())
|
userTokensSaver.push(userWalletId = userWalletId, response = response.toUserTokensResponse())
|
||||||
val syncedResponse = push(userWalletId = userWalletId, accounts = response.accounts)
|
val syncedResponse = push(userWalletId = userWalletId, accounts = response.accounts) ?: response
|
||||||
|
|
||||||
if (syncedResponse != null) {
|
store(userWalletId = userWalletId, response = syncedResponse)
|
||||||
store(userWalletId = userWalletId, response = syncedResponse)
|
|
||||||
}
|
return syncedResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun assignTokens(userWalletId: UserWalletId, accountsResponse: GetWalletAccountsResponse) {
|
private suspend fun assignTokens(
|
||||||
|
userWalletId: UserWalletId,
|
||||||
|
accountsResponse: GetWalletAccountsResponse,
|
||||||
|
): GetWalletAccountsResponse {
|
||||||
val accountsResponseWithTokens = accountsResponse.assignTokens(userWalletId)
|
val accountsResponseWithTokens = accountsResponse.assignTokens(userWalletId)
|
||||||
|
|
||||||
store(userWalletId = userWalletId, response = accountsResponseWithTokens)
|
store(userWalletId = userWalletId, response = accountsResponseWithTokens)
|
||||||
|
|
@ -172,6 +185,8 @@ internal class DefaultWalletAccountsFetcher @Inject constructor(
|
||||||
userWalletId = userWalletId,
|
userWalletId = userWalletId,
|
||||||
response = accountsResponseWithTokens.toUserTokensResponse(),
|
response = accountsResponseWithTokens.toUserTokensResponse(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return accountsResponseWithTokens
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getETag(userWalletId: UserWalletId): String? {
|
private suspend fun getETag(userWalletId: UserWalletId): String? {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package com.tangem.data.account.fetcher
|
package com.tangem.data.account.fetcher
|
||||||
|
|
||||||
import com.tangem.data.account.utils.DefaultWalletAccountsResponseFactory
|
import com.tangem.data.account.utils.DefaultWalletAccountsResponseFactory
|
||||||
import com.tangem.data.account.utils.toUserTokensResponse
|
|
||||||
import com.tangem.data.common.currency.UserTokensResponseAccountIdEnricher
|
import com.tangem.data.common.currency.UserTokensResponseAccountIdEnricher
|
||||||
import com.tangem.data.common.currency.UserTokensSaver
|
import com.tangem.data.common.currency.UserTokensSaver
|
||||||
import com.tangem.datasource.api.common.response.ApiResponseError
|
import com.tangem.datasource.api.common.response.ApiResponseError
|
||||||
|
|
@ -10,6 +9,7 @@ import com.tangem.datasource.api.common.response.isNetworkError
|
||||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||||
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
||||||
import com.tangem.datasource.api.tangemTech.models.account.WalletAccountDTO
|
import com.tangem.datasource.api.tangemTech.models.account.WalletAccountDTO
|
||||||
|
import com.tangem.datasource.api.tangemTech.models.account.toUserTokensResponse
|
||||||
import com.tangem.datasource.local.token.UserTokensResponseStore
|
import com.tangem.datasource.local.token.UserTokensResponseStore
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
@ -49,11 +49,13 @@ internal class FetchWalletAccountsErrorHandler @Inject constructor(
|
||||||
savedAccountsResponse: GetWalletAccountsResponse?,
|
savedAccountsResponse: GetWalletAccountsResponse?,
|
||||||
pushWalletAccounts: suspend (UserWalletId, List<WalletAccountDTO>) -> GetWalletAccountsResponse?,
|
pushWalletAccounts: suspend (UserWalletId, List<WalletAccountDTO>) -> GetWalletAccountsResponse?,
|
||||||
storeWalletAccounts: suspend (UserWalletId, GetWalletAccountsResponse) -> Unit,
|
storeWalletAccounts: suspend (UserWalletId, GetWalletAccountsResponse) -> Unit,
|
||||||
): GetWalletAccountsResponse? {
|
): GetWalletAccountsResponse {
|
||||||
val isResponseUpToDate = error.isNetworkError(code = Code.NOT_MODIFIED)
|
val isResponseUpToDate = error.isNetworkError(code = Code.NOT_MODIFIED)
|
||||||
if (isResponseUpToDate) {
|
if (isResponseUpToDate) {
|
||||||
Timber.e("ETag is up to date, no need to update accounts for wallet: $userWalletId")
|
Timber.e("ETag is up to date, no need to update accounts for wallet: $userWalletId")
|
||||||
return savedAccountsResponse
|
return requireNotNull(savedAccountsResponse) {
|
||||||
|
"Saved accounts response is null for wallet: $userWalletId"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var response = savedAccountsResponse ?: createDefaultResponse(userWalletId)
|
var response = savedAccountsResponse ?: createDefaultResponse(userWalletId)
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ package com.tangem.data.account.producer
|
||||||
import arrow.core.Option
|
import arrow.core.Option
|
||||||
import arrow.core.some
|
import arrow.core.some
|
||||||
import com.tangem.data.account.store.AccountsResponseStoreFactory
|
import com.tangem.data.account.store.AccountsResponseStoreFactory
|
||||||
import com.tangem.data.account.utils.toUserTokensResponse
|
|
||||||
import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory
|
import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory
|
||||||
|
import com.tangem.datasource.api.tangemTech.models.account.toUserTokensResponse
|
||||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import com.tangem.domain.models.wallet.isMultiCurrency
|
import com.tangem.domain.models.wallet.isMultiCurrency
|
||||||
|
|
@ -54,6 +54,7 @@ internal class AccountListCryptoCurrenciesProducer @AssistedInject constructor(
|
||||||
).toSet()
|
).toSet()
|
||||||
}
|
}
|
||||||
.onEmpty { emit(emptySet()) }
|
.onEmpty { emit(emptySet()) }
|
||||||
|
.distinctUntilChanged()
|
||||||
.flowOn(dispatchers.default)
|
.flowOn(dispatchers.default)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
package com.tangem.data.account.producer
|
||||||
|
|
||||||
|
import arrow.core.Option
|
||||||
|
import arrow.core.none
|
||||||
|
import com.tangem.domain.account.producer.SingleAccountListProducer
|
||||||
|
import com.tangem.domain.account.producer.SingleAccountProducer
|
||||||
|
import com.tangem.domain.account.supplier.SingleAccountListSupplier
|
||||||
|
import com.tangem.domain.models.account.Account
|
||||||
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
|
import dagger.assisted.Assisted
|
||||||
|
import dagger.assisted.AssistedFactory
|
||||||
|
import dagger.assisted.AssistedInject
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
import kotlinx.coroutines.flow.mapNotNull
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation of [SingleAccountProducer] that produces a flow of [Account.CryptoPortfolio]
|
||||||
|
* for a single account identified by [SingleAccountProducer.Params.accountId].
|
||||||
|
*
|
||||||
|
* It uses [SingleAccountListSupplier] to get the list of accounts and filters it to find the
|
||||||
|
* specific account.
|
||||||
|
*
|
||||||
|
* @property params Parameters containing the account ID for which the portfolio is produced.
|
||||||
|
* @property singleAccountListSupplier Supplier to get the list of accounts.
|
||||||
|
* @property dispatchers Coroutine dispatcher provider for managing threading.
|
||||||
|
*/
|
||||||
|
internal class DefaultSingleAccountProducer @AssistedInject constructor(
|
||||||
|
@Assisted val params: SingleAccountProducer.Params,
|
||||||
|
private val singleAccountListSupplier: SingleAccountListSupplier,
|
||||||
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
|
) : SingleAccountProducer {
|
||||||
|
|
||||||
|
override val fallback: Option<Account.CryptoPortfolio>
|
||||||
|
get() = none()
|
||||||
|
|
||||||
|
override fun produce(): Flow<Account.CryptoPortfolio> {
|
||||||
|
return singleAccountListSupplier(
|
||||||
|
params = SingleAccountListProducer.Params(userWalletId = params.accountId.userWalletId),
|
||||||
|
)
|
||||||
|
.mapNotNull { accountList ->
|
||||||
|
accountList.accounts.firstOrNull {
|
||||||
|
it is Account.CryptoPortfolio && params.accountId == it.accountId
|
||||||
|
} as? Account.CryptoPortfolio
|
||||||
|
}
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.flowOn(dispatchers.default)
|
||||||
|
}
|
||||||
|
|
||||||
|
@AssistedFactory
|
||||||
|
interface Factory : SingleAccountProducer.Factory {
|
||||||
|
override fun create(params: SingleAccountProducer.Params): DefaultSingleAccountProducer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,13 +9,13 @@ import com.tangem.data.account.store.AccountsResponseStore
|
||||||
import com.tangem.data.account.store.AccountsResponseStoreFactory
|
import com.tangem.data.account.store.AccountsResponseStoreFactory
|
||||||
import com.tangem.data.account.store.ArchivedAccountsStore
|
import com.tangem.data.account.store.ArchivedAccountsStore
|
||||||
import com.tangem.data.account.store.ArchivedAccountsStoreFactory
|
import com.tangem.data.account.store.ArchivedAccountsStoreFactory
|
||||||
import com.tangem.data.account.utils.toUserTokensResponse
|
|
||||||
import com.tangem.data.common.account.WalletAccountsSaver
|
import com.tangem.data.common.account.WalletAccountsSaver
|
||||||
import com.tangem.data.common.cache.etag.ETagsStore
|
import com.tangem.data.common.cache.etag.ETagsStore
|
||||||
import com.tangem.data.common.currency.UserTokensSaver
|
import com.tangem.data.common.currency.UserTokensSaver
|
||||||
import com.tangem.datasource.api.common.response.getOrThrow
|
import com.tangem.datasource.api.common.response.getOrThrow
|
||||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||||
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
||||||
|
import com.tangem.datasource.api.tangemTech.models.account.toUserTokensResponse
|
||||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||||
import com.tangem.datasource.utils.getSyncOrNull
|
import com.tangem.datasource.utils.getSyncOrNull
|
||||||
import com.tangem.domain.account.models.AccountList
|
import com.tangem.domain.account.models.AccountList
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,11 @@ import com.tangem.blockchain.common.Blockchain
|
||||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||||
import com.tangem.data.account.store.AccountsResponseStoreFactory
|
import com.tangem.data.account.store.AccountsResponseStoreFactory
|
||||||
import com.tangem.data.account.utils.assignTokens
|
import com.tangem.data.account.utils.assignTokens
|
||||||
import com.tangem.data.account.utils.toUserTokensResponse
|
|
||||||
import com.tangem.data.common.currency.UserTokensSaver
|
import com.tangem.data.common.currency.UserTokensSaver
|
||||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||||
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
||||||
import com.tangem.datasource.api.tangemTech.models.account.WalletAccountDTO
|
import com.tangem.datasource.api.tangemTech.models.account.WalletAccountDTO
|
||||||
|
import com.tangem.datasource.api.tangemTech.models.account.toUserTokensResponse
|
||||||
import com.tangem.datasource.utils.getSyncOrNull
|
import com.tangem.datasource.utils.getSyncOrNull
|
||||||
import com.tangem.domain.account.tokens.MainAccountTokensMigration
|
import com.tangem.domain.account.tokens.MainAccountTokensMigration
|
||||||
import com.tangem.domain.models.account.DerivationIndex
|
import com.tangem.domain.models.account.DerivationIndex
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,16 @@
|
||||||
package com.tangem.data.account.utils
|
package com.tangem.data.account.utils
|
||||||
|
|
||||||
import com.tangem.data.account.converter.CryptoPortfolioConverter
|
import com.tangem.data.account.converter.CryptoPortfolioConverter
|
||||||
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
|
||||||
import com.tangem.data.common.currency.UserTokensResponseFactory
|
import com.tangem.data.common.currency.UserTokensResponseFactory
|
||||||
|
import com.tangem.data.common.network.NetworkFactory
|
||||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||||
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
||||||
import com.tangem.datasource.api.tangemTech.models.account.WalletAccountDTO
|
import com.tangem.datasource.api.tangemTech.models.account.WalletAccountDTO
|
||||||
import com.tangem.domain.account.models.AccountList
|
import com.tangem.domain.account.models.AccountList
|
||||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||||
import com.tangem.domain.models.account.Account
|
import com.tangem.domain.models.account.Account
|
||||||
|
import com.tangem.domain.models.account.AccountId
|
||||||
|
import com.tangem.domain.models.account.DerivationIndex
|
||||||
import com.tangem.domain.models.wallet.UserWallet
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
@ -19,7 +21,7 @@ import javax.inject.Inject
|
||||||
* @property userWalletsListRepository repository to get user wallet information
|
* @property userWalletsListRepository repository to get user wallet information
|
||||||
* @property cryptoPortfolioCF converter factory to convert crypto portfolio accounts
|
* @property cryptoPortfolioCF converter factory to convert crypto portfolio accounts
|
||||||
* @property userTokensResponseFactory factory to create [UserTokensResponse]
|
* @property userTokensResponseFactory factory to create [UserTokensResponse]
|
||||||
* @property cardCryptoCurrencyFactory factory to get default coins for multi-currency wallet
|
* @property networkFactory factory to create network derivation path
|
||||||
*
|
*
|
||||||
[REDACTED_AUTHOR]
|
[REDACTED_AUTHOR]
|
||||||
*/
|
*/
|
||||||
|
|
@ -27,7 +29,7 @@ internal class DefaultWalletAccountsResponseFactory @Inject constructor(
|
||||||
private val userWalletsListRepository: UserWalletsListRepository,
|
private val userWalletsListRepository: UserWalletsListRepository,
|
||||||
private val cryptoPortfolioCF: CryptoPortfolioConverter.Factory,
|
private val cryptoPortfolioCF: CryptoPortfolioConverter.Factory,
|
||||||
private val userTokensResponseFactory: UserTokensResponseFactory,
|
private val userTokensResponseFactory: UserTokensResponseFactory,
|
||||||
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
private val networkFactory: NetworkFactory,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun create(userWalletId: UserWalletId, userTokensResponse: UserTokensResponse?): GetWalletAccountsResponse {
|
suspend fun create(userWalletId: UserWalletId, userTokensResponse: UserTokensResponse?): GetWalletAccountsResponse {
|
||||||
|
|
@ -59,10 +61,12 @@ internal class DefaultWalletAccountsResponseFactory @Inject constructor(
|
||||||
private fun UserTokensResponse?.orDefault(userWallet: UserWallet?): UserTokensResponse {
|
private fun UserTokensResponse?.orDefault(userWallet: UserWallet?): UserTokensResponse {
|
||||||
if (this != null) return this
|
if (this != null) return this
|
||||||
|
|
||||||
return userTokensResponseFactory.createUserTokensResponse(
|
return userTokensResponseFactory.createDefaultResponse(
|
||||||
currencies = userWallet?.let(cardCryptoCurrencyFactory::createDefaultCoinsForMultiCurrencyWallet).orEmpty(),
|
userWallet = userWallet,
|
||||||
isGroupedByNetwork = false,
|
networkFactory = networkFactory,
|
||||||
isSortedByBalance = false,
|
accountId = userWallet?.let {
|
||||||
|
AccountId.forCryptoPortfolio(userWalletId = it.walletId, derivationIndex = DerivationIndex.Main)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -6,20 +6,6 @@ import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResp
|
||||||
import com.tangem.datasource.api.tangemTech.models.account.WalletAccountDTO
|
import com.tangem.datasource.api.tangemTech.models.account.WalletAccountDTO
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
|
||||||
/** Flattens the tokens from all wallet accounts into a single list */
|
|
||||||
internal fun GetWalletAccountsResponse.flattenTokens(): List<UserTokensResponse.Token> {
|
|
||||||
return accounts.flatMap { it.tokens.orEmpty() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Converts the [GetWalletAccountsResponse] into a [UserTokensResponse] */
|
|
||||||
internal fun GetWalletAccountsResponse.toUserTokensResponse(): UserTokensResponse {
|
|
||||||
return UserTokensResponse(
|
|
||||||
group = wallet.group,
|
|
||||||
sort = wallet.sort,
|
|
||||||
tokens = flattenTokens(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assigns tokens from a [UserTokensResponse] to the wallet accounts in the [GetWalletAccountsResponse]
|
* Assigns tokens from a [UserTokensResponse] to the wallet accounts in the [GetWalletAccountsResponse]
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,8 @@ class DefaultSingleAccountListFetcherTest {
|
||||||
// Arrange
|
// Arrange
|
||||||
val params = SingleAccountListFetcher.Params(userWalletId = userWalletId)
|
val params = SingleAccountListFetcher.Params(userWalletId = userWalletId)
|
||||||
|
|
||||||
|
coEvery { walletAccountsFetcher.fetch(userWalletId) } returns mockk()
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
val actual = fetcher.invoke(params)
|
val actual = fetcher.invoke(params)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -199,7 +199,7 @@ class DefaultWalletAccountsFetcherTest {
|
||||||
@Test
|
@Test
|
||||||
fun `fetch should call error handler when getWalletAccounts returns error`() = runTest {
|
fun `fetch should call error handler when getWalletAccounts returns error`() = runTest {
|
||||||
// Arrange
|
// Arrange
|
||||||
val savedAccountsResponse = null
|
val savedAccountsResponse = createGetWalletAccountsResponse(userWalletId)
|
||||||
val apiError = ApiResponse.Error(ApiResponseError.NetworkException())
|
val apiError = ApiResponse.Error(ApiResponseError.NetworkException())
|
||||||
|
|
||||||
accountsResponseStoreFlow.value = savedAccountsResponse
|
accountsResponseStoreFlow.value = savedAccountsResponse
|
||||||
|
|
@ -212,7 +212,7 @@ class DefaultWalletAccountsFetcherTest {
|
||||||
fetchWalletAccountsErrorHandler.handle(
|
fetchWalletAccountsErrorHandler.handle(
|
||||||
error = apiError.cause,
|
error = apiError.cause,
|
||||||
userWalletId = userWalletId,
|
userWalletId = userWalletId,
|
||||||
savedAccountsResponse = null,
|
savedAccountsResponse = savedAccountsResponse,
|
||||||
pushWalletAccounts = any(),
|
pushWalletAccounts = any(),
|
||||||
storeWalletAccounts = any(),
|
storeWalletAccounts = any(),
|
||||||
)
|
)
|
||||||
|
|
@ -231,7 +231,7 @@ class DefaultWalletAccountsFetcherTest {
|
||||||
fetchWalletAccountsErrorHandler.handle(
|
fetchWalletAccountsErrorHandler.handle(
|
||||||
error = apiError.cause,
|
error = apiError.cause,
|
||||||
userWalletId = userWalletId,
|
userWalletId = userWalletId,
|
||||||
savedAccountsResponse = null,
|
savedAccountsResponse = savedAccountsResponse,
|
||||||
pushWalletAccounts = any(),
|
pushWalletAccounts = any(),
|
||||||
storeWalletAccounts = any(),
|
storeWalletAccounts = any(),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@ package com.tangem.data.account.fetcher
|
||||||
import com.tangem.data.account.converter.createGetWalletAccountsResponse
|
import com.tangem.data.account.converter.createGetWalletAccountsResponse
|
||||||
import com.tangem.data.account.converter.createWalletAccountDTO
|
import com.tangem.data.account.converter.createWalletAccountDTO
|
||||||
import com.tangem.data.account.utils.DefaultWalletAccountsResponseFactory
|
import com.tangem.data.account.utils.DefaultWalletAccountsResponseFactory
|
||||||
import com.tangem.data.account.utils.toUserTokensResponse
|
|
||||||
import com.tangem.data.common.currency.UserTokensSaver
|
import com.tangem.data.common.currency.UserTokensSaver
|
||||||
import com.tangem.datasource.api.common.response.ApiResponseError
|
import com.tangem.datasource.api.common.response.ApiResponseError
|
||||||
import com.tangem.datasource.api.common.response.ApiResponseError.HttpException.Code
|
import com.tangem.datasource.api.common.response.ApiResponseError.HttpException.Code
|
||||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||||
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
||||||
import com.tangem.datasource.api.tangemTech.models.account.WalletAccountDTO
|
import com.tangem.datasource.api.tangemTech.models.account.WalletAccountDTO
|
||||||
|
import com.tangem.datasource.api.tangemTech.models.account.toUserTokensResponse
|
||||||
import com.tangem.datasource.local.token.UserTokensResponseStore
|
import com.tangem.datasource.local.token.UserTokensResponseStore
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import io.mockk.clearMocks
|
import io.mockk.clearMocks
|
||||||
|
|
@ -53,6 +53,8 @@ class FetchWalletAccountsErrorHandlerTest {
|
||||||
@Test
|
@Test
|
||||||
fun `does not update accounts when response is up to date`() = runTest {
|
fun `does not update accounts when response is up to date`() = runTest {
|
||||||
// Arrange
|
// Arrange
|
||||||
|
val response = createGetWalletAccountsResponse(userWalletId)
|
||||||
|
|
||||||
val error = ApiResponseError.HttpException(
|
val error = ApiResponseError.HttpException(
|
||||||
code = Code.NOT_MODIFIED,
|
code = Code.NOT_MODIFIED,
|
||||||
message = "Not Modified",
|
message = "Not Modified",
|
||||||
|
|
@ -63,7 +65,7 @@ class FetchWalletAccountsErrorHandlerTest {
|
||||||
handler.handle(
|
handler.handle(
|
||||||
error = error,
|
error = error,
|
||||||
userWalletId = userWalletId,
|
userWalletId = userWalletId,
|
||||||
savedAccountsResponse = null,
|
savedAccountsResponse = response,
|
||||||
pushWalletAccounts = pushWalletAccounts,
|
pushWalletAccounts = pushWalletAccounts,
|
||||||
storeWalletAccounts = storeWalletAccounts,
|
storeWalletAccounts = storeWalletAccounts,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,10 @@ import com.tangem.data.account.converter.createWalletAccountDTO
|
||||||
import com.tangem.data.account.store.AccountsResponseStore
|
import com.tangem.data.account.store.AccountsResponseStore
|
||||||
import com.tangem.data.account.store.AccountsResponseStoreFactory
|
import com.tangem.data.account.store.AccountsResponseStoreFactory
|
||||||
import com.tangem.data.account.tokens.DefaultMainAccountTokensMigration
|
import com.tangem.data.account.tokens.DefaultMainAccountTokensMigration
|
||||||
import com.tangem.data.account.utils.toUserTokensResponse
|
|
||||||
import com.tangem.data.common.currency.UserTokensSaver
|
import com.tangem.data.common.currency.UserTokensSaver
|
||||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||||
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
||||||
|
import com.tangem.datasource.api.tangemTech.models.account.toUserTokensResponse
|
||||||
import com.tangem.domain.models.account.AccountId
|
import com.tangem.domain.models.account.AccountId
|
||||||
import com.tangem.domain.models.account.DerivationIndex
|
import com.tangem.domain.models.account.DerivationIndex
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ package com.tangem.data.account.utils
|
||||||
import com.google.common.truth.Truth
|
import com.google.common.truth.Truth
|
||||||
import com.tangem.data.account.converter.CryptoPortfolioConverter
|
import com.tangem.data.account.converter.CryptoPortfolioConverter
|
||||||
import com.tangem.data.account.converter.createWalletAccountDTO
|
import com.tangem.data.account.converter.createWalletAccountDTO
|
||||||
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
|
||||||
import com.tangem.data.common.currency.UserTokensResponseFactory
|
import com.tangem.data.common.currency.UserTokensResponseFactory
|
||||||
|
import com.tangem.data.common.network.NetworkFactory
|
||||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||||
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
||||||
import com.tangem.domain.account.models.AccountList
|
import com.tangem.domain.account.models.AccountList
|
||||||
|
|
@ -27,13 +27,13 @@ class DefaultWalletAccountsResponseFactoryTest {
|
||||||
private val cryptoPortfolioCF = mockk<CryptoPortfolioConverter.Factory>()
|
private val cryptoPortfolioCF = mockk<CryptoPortfolioConverter.Factory>()
|
||||||
private val cryptoPortfolioConverter = mockk<CryptoPortfolioConverter>()
|
private val cryptoPortfolioConverter = mockk<CryptoPortfolioConverter>()
|
||||||
private val userTokensResponseFactory = mockk<UserTokensResponseFactory>()
|
private val userTokensResponseFactory = mockk<UserTokensResponseFactory>()
|
||||||
private val cardCryptoCurrencyFactory = mockk<CardCryptoCurrencyFactory>()
|
private val networkFactory = mockk<NetworkFactory>()
|
||||||
|
|
||||||
private val factory = DefaultWalletAccountsResponseFactory(
|
private val factory = DefaultWalletAccountsResponseFactory(
|
||||||
userWalletsListRepository = userWalletsListRepository,
|
userWalletsListRepository = userWalletsListRepository,
|
||||||
cryptoPortfolioCF = cryptoPortfolioCF,
|
cryptoPortfolioCF = cryptoPortfolioCF,
|
||||||
userTokensResponseFactory = userTokensResponseFactory,
|
userTokensResponseFactory = userTokensResponseFactory,
|
||||||
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
|
networkFactory = networkFactory,
|
||||||
)
|
)
|
||||||
|
|
||||||
private val userWalletId = UserWalletId("011")
|
private val userWalletId = UserWalletId("011")
|
||||||
|
|
@ -50,7 +50,7 @@ class DefaultWalletAccountsResponseFactoryTest {
|
||||||
cryptoPortfolioCF,
|
cryptoPortfolioCF,
|
||||||
cryptoPortfolioConverter,
|
cryptoPortfolioConverter,
|
||||||
userTokensResponseFactory,
|
userTokensResponseFactory,
|
||||||
cardCryptoCurrencyFactory,
|
networkFactory,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,10 +65,10 @@ class DefaultWalletAccountsResponseFactoryTest {
|
||||||
|
|
||||||
coEvery { userWalletsListRepository.userWalletsSync() } returns emptyList()
|
coEvery { userWalletsListRepository.userWalletsSync() } returns emptyList()
|
||||||
every {
|
every {
|
||||||
userTokensResponseFactory.createUserTokensResponse(
|
userTokensResponseFactory.createDefaultResponse(
|
||||||
currencies = emptyList(),
|
userWallet = null,
|
||||||
isGroupedByNetwork = false,
|
networkFactory = networkFactory,
|
||||||
isSortedByBalance = false,
|
accountId = null,
|
||||||
)
|
)
|
||||||
} returns userTokensResponse
|
} returns userTokensResponse
|
||||||
|
|
||||||
|
|
@ -89,10 +89,10 @@ class DefaultWalletAccountsResponseFactoryTest {
|
||||||
|
|
||||||
coVerifyOrder {
|
coVerifyOrder {
|
||||||
userWalletsListRepository.userWalletsSync()
|
userWalletsListRepository.userWalletsSync()
|
||||||
userTokensResponseFactory.createUserTokensResponse(
|
userTokensResponseFactory.createDefaultResponse(
|
||||||
currencies = emptyList(),
|
userWallet = null,
|
||||||
isGroupedByNetwork = false,
|
networkFactory = networkFactory,
|
||||||
isSortedByBalance = false,
|
accountId = null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -104,27 +104,24 @@ class DefaultWalletAccountsResponseFactoryTest {
|
||||||
every { walletId } returns userWalletId
|
every { walletId } returns userWalletId
|
||||||
}
|
}
|
||||||
|
|
||||||
val defaultCoins = listOf(mockk<CryptoCurrency.Coin>())
|
val accounts = AccountList.empty(userWallet.walletId).accounts
|
||||||
|
.filterIsInstance<Account.CryptoPortfolio>()
|
||||||
|
|
||||||
coEvery { userWalletsListRepository.userWalletsSync() } returns listOf(userWallet)
|
coEvery { userWalletsListRepository.userWalletsSync() } returns listOf(userWallet)
|
||||||
every { cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyWallet(userWallet) } returns defaultCoins
|
|
||||||
|
|
||||||
val defaultResponse = UserTokensResponse(
|
val defaultResponse = UserTokensResponse(
|
||||||
group = UserTokensResponse.GroupType.NETWORK,
|
group = UserTokensResponse.GroupType.NETWORK,
|
||||||
sort = UserTokensResponse.SortType.BALANCE,
|
sort = UserTokensResponse.SortType.BALANCE,
|
||||||
tokens = listOf(mockk(relaxed = true)),
|
tokens = listOf(mockk(relaxed = true)),
|
||||||
)
|
)
|
||||||
|
|
||||||
every {
|
every {
|
||||||
userTokensResponseFactory.createUserTokensResponse(
|
userTokensResponseFactory.createDefaultResponse(
|
||||||
currencies = defaultCoins,
|
userWallet = userWallet,
|
||||||
isGroupedByNetwork = false,
|
networkFactory = networkFactory,
|
||||||
isSortedByBalance = false,
|
accountId = accounts.first().accountId,
|
||||||
)
|
)
|
||||||
} returns defaultResponse
|
} returns defaultResponse
|
||||||
|
|
||||||
val accounts = AccountList.empty(userWallet.walletId).accounts
|
|
||||||
.filterIsInstance<Account.CryptoPortfolio>()
|
|
||||||
|
|
||||||
val accountsDTO = createWalletAccountDTO(userWalletId)
|
val accountsDTO = createWalletAccountDTO(userWalletId)
|
||||||
every { cryptoPortfolioConverter.convertListBack(accounts) } returns listOf(accountsDTO)
|
every { cryptoPortfolioConverter.convertListBack(accounts) } returns listOf(accountsDTO)
|
||||||
|
|
||||||
|
|
@ -147,11 +144,10 @@ class DefaultWalletAccountsResponseFactoryTest {
|
||||||
coVerifyOrder {
|
coVerifyOrder {
|
||||||
userWalletsListRepository.userWalletsSync()
|
userWalletsListRepository.userWalletsSync()
|
||||||
cryptoPortfolioConverter.convertListBack(accounts)
|
cryptoPortfolioConverter.convertListBack(accounts)
|
||||||
cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyWallet(userWallet)
|
userTokensResponseFactory.createDefaultResponse(
|
||||||
userTokensResponseFactory.createUserTokensResponse(
|
userWallet = userWallet,
|
||||||
currencies = defaultCoins,
|
networkFactory = networkFactory,
|
||||||
isGroupedByNetwork = false,
|
accountId = accounts.first().accountId,
|
||||||
isSortedByBalance = false,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -162,22 +158,26 @@ class DefaultWalletAccountsResponseFactoryTest {
|
||||||
val userWallet = mockk<UserWallet>(relaxed = true) {
|
val userWallet = mockk<UserWallet>(relaxed = true) {
|
||||||
every { walletId } returns userWalletId
|
every { walletId } returns userWalletId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val accounts = AccountList.empty(userWallet.walletId).accounts
|
||||||
|
.filterIsInstance<Account.CryptoPortfolio>()
|
||||||
|
|
||||||
coEvery { userWalletsListRepository.userWalletsSync() } returns listOf(userWallet)
|
coEvery { userWalletsListRepository.userWalletsSync() } returns listOf(userWallet)
|
||||||
every { cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyWallet(userWallet) } returns emptyList()
|
|
||||||
val defaultResponse = UserTokensResponse(
|
val defaultResponse = UserTokensResponse(
|
||||||
group = UserTokensResponse.GroupType.NETWORK,
|
group = UserTokensResponse.GroupType.NETWORK,
|
||||||
sort = UserTokensResponse.SortType.BALANCE,
|
sort = UserTokensResponse.SortType.BALANCE,
|
||||||
tokens = emptyList(),
|
tokens = emptyList(),
|
||||||
)
|
)
|
||||||
|
|
||||||
every {
|
every {
|
||||||
userTokensResponseFactory.createUserTokensResponse(
|
userTokensResponseFactory.createDefaultResponse(
|
||||||
currencies = emptyList(),
|
userWallet = userWallet,
|
||||||
isGroupedByNetwork = false,
|
networkFactory = networkFactory,
|
||||||
isSortedByBalance = false,
|
accountId = accounts.first().accountId,
|
||||||
)
|
)
|
||||||
} returns defaultResponse
|
} returns defaultResponse
|
||||||
val accounts = AccountList.empty(userWallet.walletId).accounts
|
|
||||||
.filterIsInstance<Account.CryptoPortfolio>()
|
|
||||||
every { cryptoPortfolioConverter.convertListBack(accounts) } returns emptyList()
|
every { cryptoPortfolioConverter.convertListBack(accounts) } returns emptyList()
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import com.google.common.truth.Truth
|
||||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||||
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
||||||
import com.tangem.datasource.api.tangemTech.models.account.WalletAccountDTO
|
import com.tangem.datasource.api.tangemTech.models.account.WalletAccountDTO
|
||||||
|
import com.tangem.datasource.api.tangemTech.models.account.flattenTokens
|
||||||
|
import com.tangem.datasource.api.tangemTech.models.account.toUserTokensResponse
|
||||||
import com.tangem.domain.models.account.AccountId
|
import com.tangem.domain.models.account.AccountId
|
||||||
import com.tangem.domain.models.account.DerivationIndex
|
import com.tangem.domain.models.account.DerivationIndex
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.tangem.data.common.account
|
package com.tangem.data.common.account
|
||||||
|
|
||||||
|
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -11,5 +12,8 @@ interface WalletAccountsFetcher {
|
||||||
|
|
||||||
/** Fetch wallet accounts by [userWalletId] */
|
/** Fetch wallet accounts by [userWalletId] */
|
||||||
@Throws
|
@Throws
|
||||||
suspend fun fetch(userWalletId: UserWalletId)
|
suspend fun fetch(userWalletId: UserWalletId): GetWalletAccountsResponse
|
||||||
|
|
||||||
|
/** Get saved wallet accounts by [userWalletId] */
|
||||||
|
suspend fun getSaved(userWalletId: UserWalletId): GetWalletAccountsResponse?
|
||||||
}
|
}
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
package com.tangem.data.common.currency
|
package com.tangem.data.common.currency
|
||||||
|
|
||||||
import com.tangem.blockchain.common.Blockchain
|
|
||||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||||
import com.tangem.blockchainsdk.utils.toBlockchain
|
import com.tangem.blockchainsdk.utils.toBlockchain
|
||||||
import com.tangem.blockchainsdk.utils.toNetworkId
|
import com.tangem.blockchainsdk.utils.toNetworkId
|
||||||
|
import com.tangem.data.common.account.WalletAccountsFetcher
|
||||||
|
import com.tangem.data.common.tokens.getDefaultWalletBlockchains
|
||||||
|
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||||
|
import com.tangem.datasource.api.tangemTech.models.account.toUserTokensResponse
|
||||||
import com.tangem.datasource.local.token.UserTokensResponseStore
|
import com.tangem.datasource.local.token.UserTokensResponseStore
|
||||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||||
import com.tangem.domain.card.common.TapWorkarounds.isTestCard
|
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||||
import com.tangem.domain.card.common.util.cardTypesResolver
|
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||||
import com.tangem.domain.demo.models.DemoConfig
|
import com.tangem.domain.demo.models.DemoConfig
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
|
|
@ -23,10 +26,13 @@ import com.tangem.domain.models.wallet.isMultiCurrency
|
||||||
* @property userWalletsStore user wallets store
|
* @property userWalletsStore user wallets store
|
||||||
* @property userTokensResponseStore user tokens response store
|
* @property userTokensResponseStore user tokens response store
|
||||||
*/
|
*/
|
||||||
|
@Suppress("LongParameterList")
|
||||||
internal class DefaultCardCryptoCurrencyFactory(
|
internal class DefaultCardCryptoCurrencyFactory(
|
||||||
private val demoConfig: DemoConfig,
|
private val demoConfig: DemoConfig,
|
||||||
private val excludedBlockchains: ExcludedBlockchains,
|
private val excludedBlockchains: ExcludedBlockchains,
|
||||||
private val userWalletsStore: UserWalletsStore,
|
private val userWalletsStore: UserWalletsStore,
|
||||||
|
private val accountsFeatureToggles: AccountsFeatureToggles,
|
||||||
|
private val walletAccountsFetcher: WalletAccountsFetcher,
|
||||||
private val userTokensResponseStore: UserTokensResponseStore,
|
private val userTokensResponseStore: UserTokensResponseStore,
|
||||||
private val responseCryptoCurrenciesFactory: ResponseCryptoCurrenciesFactory,
|
private val responseCryptoCurrenciesFactory: ResponseCryptoCurrenciesFactory,
|
||||||
) : CardCryptoCurrencyFactory {
|
) : CardCryptoCurrencyFactory {
|
||||||
|
|
@ -99,25 +105,7 @@ internal class DefaultCardCryptoCurrencyFactory(
|
||||||
override fun createDefaultCoinsForMultiCurrencyWallet(userWallet: UserWallet): List<CryptoCurrency.Coin> {
|
override fun createDefaultCoinsForMultiCurrencyWallet(userWallet: UserWallet): List<CryptoCurrency.Coin> {
|
||||||
require(userWallet.isMultiCurrency) { "It isn't multi-currency wallet" }
|
require(userWallet.isMultiCurrency) { "It isn't multi-currency wallet" }
|
||||||
|
|
||||||
val blockchains = when (userWallet) {
|
val blockchains = getDefaultWalletBlockchains(userWallet, demoConfig)
|
||||||
is UserWallet.Cold -> {
|
|
||||||
val card = userWallet.scanResponse.card
|
|
||||||
|
|
||||||
var blockchainsInternal = if (demoConfig.isDemoCardId(card.cardId)) {
|
|
||||||
demoConfig.demoBlockchains
|
|
||||||
} else {
|
|
||||||
listOf(Blockchain.Bitcoin, Blockchain.Ethereum)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (card.isTestCard) {
|
|
||||||
blockchainsInternal = blockchainsInternal.mapNotNull { it.getTestnetVersion() }
|
|
||||||
}
|
|
||||||
|
|
||||||
blockchainsInternal
|
|
||||||
}
|
|
||||||
|
|
||||||
is UserWallet.Hot -> listOf(Blockchain.Bitcoin, Blockchain.Ethereum)
|
|
||||||
}
|
|
||||||
|
|
||||||
return blockchains.mapNotNull {
|
return blockchains.mapNotNull {
|
||||||
cryptoCurrencyFactory.createCoin(
|
cryptoCurrencyFactory.createCoin(
|
||||||
|
|
@ -152,7 +140,7 @@ internal class DefaultCardCryptoCurrencyFactory(
|
||||||
userWallet: UserWallet,
|
userWallet: UserWallet,
|
||||||
networks: Set<Network>,
|
networks: Set<Network>,
|
||||||
): Map<Network, List<CryptoCurrency>> {
|
): Map<Network, List<CryptoCurrency>> {
|
||||||
val response = userTokensResponseStore.getSyncOrNull(userWalletId = userWallet.walletId)
|
val response = getUserTokensResponse(userWalletId = userWallet.walletId)
|
||||||
?: return emptyMap()
|
?: return emptyMap()
|
||||||
|
|
||||||
val existingNetworkWithCurrencies = responseCryptoCurrenciesFactory.createCurrencies(
|
val existingNetworkWithCurrencies = responseCryptoCurrenciesFactory.createCurrencies(
|
||||||
|
|
@ -170,7 +158,7 @@ internal class DefaultCardCryptoCurrencyFactory(
|
||||||
userWallet: UserWallet,
|
userWallet: UserWallet,
|
||||||
rawIds: Set<Network.RawID>,
|
rawIds: Set<Network.RawID>,
|
||||||
): Map<Network.RawID, List<CryptoCurrency>> {
|
): Map<Network.RawID, List<CryptoCurrency>> {
|
||||||
val response = userTokensResponseStore.getSyncOrNull(userWalletId = userWallet.walletId)
|
val response = getUserTokensResponse(userWalletId = userWallet.walletId)
|
||||||
?: return emptyMap()
|
?: return emptyMap()
|
||||||
|
|
||||||
val networkIds = rawIds.map { it.toBlockchain().toNetworkId() }
|
val networkIds = rawIds.map { it.toBlockchain().toNetworkId() }
|
||||||
|
|
@ -182,6 +170,14 @@ internal class DefaultCardCryptoCurrencyFactory(
|
||||||
.groupBy { it.network.id.rawId }
|
.groupBy { it.network.id.rawId }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun getUserTokensResponse(userWalletId: UserWalletId): UserTokensResponse? {
|
||||||
|
return if (accountsFeatureToggles.isFeatureEnabled) {
|
||||||
|
walletAccountsFetcher.getSaved(userWalletId)?.toUserTokensResponse()
|
||||||
|
} else {
|
||||||
|
userTokensResponseStore.getSyncOrNull(userWalletId = userWalletId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun getSingleWalletCurrencies(userWallet: UserWallet.Cold): SingleWalletCurrencies {
|
private fun getSingleWalletCurrencies(userWallet: UserWallet.Cold): SingleWalletCurrencies {
|
||||||
val resolver = userWallet.cardTypesResolver
|
val resolver = userWallet.cardTypesResolver
|
||||||
val blockchain = resolver.getBlockchain()
|
val blockchain = resolver.getBlockchain()
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,15 @@
|
||||||
package com.tangem.data.common.currency
|
package com.tangem.data.common.currency
|
||||||
|
|
||||||
|
import com.tangem.blockchainsdk.utils.toCoinId
|
||||||
|
import com.tangem.blockchainsdk.utils.toNetworkId
|
||||||
|
import com.tangem.data.common.network.NetworkFactory
|
||||||
|
import com.tangem.data.common.tokens.getDefaultWalletBlockchains
|
||||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||||
|
import com.tangem.domain.demo.models.DemoConfig
|
||||||
import com.tangem.domain.models.account.AccountId
|
import com.tangem.domain.models.account.AccountId
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
|
import com.tangem.domain.wallets.derivations.derivationStyleProvider
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
// TODO: [REDACTED_JIRA]
|
// TODO: [REDACTED_JIRA]
|
||||||
|
|
@ -43,4 +50,38 @@ class UserTokensResponseFactory @Inject constructor() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun createDefaultResponse(
|
||||||
|
userWallet: UserWallet?,
|
||||||
|
networkFactory: NetworkFactory,
|
||||||
|
accountId: AccountId? = null,
|
||||||
|
): UserTokensResponse {
|
||||||
|
val tokens = userWallet?.let {
|
||||||
|
getDefaultWalletBlockchains(userWallet = it, demoConfig = DemoConfig())
|
||||||
|
.map { blockchain ->
|
||||||
|
val derivationPath = networkFactory.createDerivationPath(
|
||||||
|
blockchain = blockchain,
|
||||||
|
extraDerivationPath = null,
|
||||||
|
cardDerivationStyleProvider = userWallet.derivationStyleProvider,
|
||||||
|
).value
|
||||||
|
|
||||||
|
UserTokensResponse.Token(
|
||||||
|
id = blockchain.toCoinId(),
|
||||||
|
accountId = accountId?.value,
|
||||||
|
networkId = blockchain.toNetworkId(),
|
||||||
|
derivationPath = derivationPath,
|
||||||
|
name = blockchain.getCoinName(),
|
||||||
|
symbol = blockchain.currency,
|
||||||
|
decimals = blockchain.decimals(),
|
||||||
|
contractAddress = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return UserTokensResponse(
|
||||||
|
group = UserTokensResponse.GroupType.NONE,
|
||||||
|
sort = UserTokensResponse.SortType.MANUAL,
|
||||||
|
tokens = tokens.orEmpty(),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package com.tangem.data.common.di
|
package com.tangem.data.common.di
|
||||||
|
|
||||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||||
|
import com.tangem.data.common.account.WalletAccountsFetcher
|
||||||
import com.tangem.data.common.cache.etag.DefaultETagsStore
|
import com.tangem.data.common.cache.etag.DefaultETagsStore
|
||||||
import com.tangem.data.common.cache.etag.ETagsStore
|
import com.tangem.data.common.cache.etag.ETagsStore
|
||||||
import com.tangem.data.common.currency.*
|
import com.tangem.data.common.currency.*
|
||||||
|
|
@ -30,6 +31,8 @@ internal object DataCommonModule {
|
||||||
fun provideCardCryptoCurrencyFactory(
|
fun provideCardCryptoCurrencyFactory(
|
||||||
excludedBlockchains: ExcludedBlockchains,
|
excludedBlockchains: ExcludedBlockchains,
|
||||||
userWalletsStore: UserWalletsStore,
|
userWalletsStore: UserWalletsStore,
|
||||||
|
accountsFeatureToggles: AccountsFeatureToggles,
|
||||||
|
walletAccountsFetcher: WalletAccountsFetcher,
|
||||||
userTokensResponseStore: UserTokensResponseStore,
|
userTokensResponseStore: UserTokensResponseStore,
|
||||||
responseCryptoCurrenciesFactory: ResponseCryptoCurrenciesFactory,
|
responseCryptoCurrenciesFactory: ResponseCryptoCurrenciesFactory,
|
||||||
): CardCryptoCurrencyFactory {
|
): CardCryptoCurrencyFactory {
|
||||||
|
|
@ -37,6 +40,8 @@ internal object DataCommonModule {
|
||||||
demoConfig = DemoConfig(),
|
demoConfig = DemoConfig(),
|
||||||
excludedBlockchains = excludedBlockchains,
|
excludedBlockchains = excludedBlockchains,
|
||||||
userWalletsStore = userWalletsStore,
|
userWalletsStore = userWalletsStore,
|
||||||
|
accountsFeatureToggles = accountsFeatureToggles,
|
||||||
|
walletAccountsFetcher = walletAccountsFetcher,
|
||||||
userTokensResponseStore = userTokensResponseStore,
|
userTokensResponseStore = userTokensResponseStore,
|
||||||
responseCryptoCurrenciesFactory = responseCryptoCurrenciesFactory,
|
responseCryptoCurrenciesFactory = responseCryptoCurrenciesFactory,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,10 @@ import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||||
import com.tangem.blockchainsdk.utils.toBlockchain
|
import com.tangem.blockchainsdk.utils.toBlockchain
|
||||||
import com.tangem.blockchainsdk.utils.toNetworkId
|
import com.tangem.blockchainsdk.utils.toNetworkId
|
||||||
import com.tangem.domain.card.common.extensions.canHandleToken
|
import com.tangem.domain.card.common.extensions.canHandleToken
|
||||||
import com.tangem.domain.wallets.derivations.derivationStyleProvider
|
|
||||||
import com.tangem.domain.models.network.Network
|
import com.tangem.domain.models.network.Network
|
||||||
import com.tangem.domain.models.wallet.UserWallet
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
import com.tangem.domain.wallets.derivations.DerivationStyleProvider
|
import com.tangem.domain.wallets.derivations.DerivationStyleProvider
|
||||||
|
import com.tangem.domain.wallets.derivations.derivationStyleProvider
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
@ -130,7 +130,7 @@ class NetworkFactory @Inject constructor(
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createDerivationPath(
|
fun createDerivationPath(
|
||||||
blockchain: Blockchain,
|
blockchain: Blockchain,
|
||||||
extraDerivationPath: String?,
|
extraDerivationPath: String?,
|
||||||
cardDerivationStyleProvider: DerivationStyleProvider?,
|
cardDerivationStyleProvider: DerivationStyleProvider?,
|
||||||
|
|
@ -326,8 +326,8 @@ class NetworkFactory @Inject constructor(
|
||||||
Blockchain.Pepecoin, Blockchain.PepecoinTestnet,
|
Blockchain.Pepecoin, Blockchain.PepecoinTestnet,
|
||||||
Blockchain.Hyperliquid, Blockchain.HyperliquidTestnet,
|
Blockchain.Hyperliquid, Blockchain.HyperliquidTestnet,
|
||||||
Blockchain.Quai, Blockchain.QuaiTestnet,
|
Blockchain.Quai, Blockchain.QuaiTestnet,
|
||||||
// Blockchain.Linea, Blockchain.LineaTestnet,
|
Blockchain.Linea, Blockchain.LineaTestnet,
|
||||||
// Blockchain.ArbitrumNova,
|
Blockchain.ArbitrumNova,
|
||||||
-> Network.TransactionExtrasType.NONE
|
-> Network.TransactionExtrasType.NONE
|
||||||
// endregion
|
// endregion
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.tangem.data.common.tokens
|
||||||
|
|
||||||
|
import com.tangem.blockchain.common.Blockchain
|
||||||
|
import com.tangem.domain.card.common.TapWorkarounds.isTestCard
|
||||||
|
import com.tangem.domain.demo.models.DemoConfig
|
||||||
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default blockchains for the multi-currency wallet.
|
||||||
|
*
|
||||||
|
* @param userWallet The user's wallet, which can be either a cold or hot wallet.
|
||||||
|
* @param demoConfig Configuration for demo cards, which may specify different default blockchains.
|
||||||
|
*/
|
||||||
|
fun getDefaultWalletBlockchains(userWallet: UserWallet, demoConfig: DemoConfig): Collection<Blockchain> {
|
||||||
|
return when (userWallet) {
|
||||||
|
is UserWallet.Cold -> {
|
||||||
|
val card = userWallet.scanResponse.card
|
||||||
|
|
||||||
|
var blockchainsInternal = if (demoConfig.isDemoCardId(card.cardId)) {
|
||||||
|
demoConfig.demoBlockchains
|
||||||
|
} else {
|
||||||
|
listOf(Blockchain.Bitcoin, Blockchain.Ethereum)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (card.isTestCard) {
|
||||||
|
blockchainsInternal = blockchainsInternal.mapNotNull { it.getTestnetVersion() }
|
||||||
|
}
|
||||||
|
|
||||||
|
blockchainsInternal
|
||||||
|
}
|
||||||
|
is UserWallet.Hot -> listOf(Blockchain.Bitcoin, Blockchain.Ethereum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,10 +9,12 @@ import com.tangem.common.test.domain.card.MockScanResponseFactory
|
||||||
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
|
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
|
||||||
import com.tangem.common.test.domain.wallet.MockUserWalletFactory
|
import com.tangem.common.test.domain.wallet.MockUserWalletFactory
|
||||||
import com.tangem.common.test.utils.ProvideTestModels
|
import com.tangem.common.test.utils.ProvideTestModels
|
||||||
|
import com.tangem.data.common.account.WalletAccountsFetcher
|
||||||
import com.tangem.data.common.network.NetworkFactory
|
import com.tangem.data.common.network.NetworkFactory
|
||||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||||
import com.tangem.datasource.local.token.UserTokensResponseStore
|
import com.tangem.datasource.local.token.UserTokensResponseStore
|
||||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||||
|
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||||
import com.tangem.domain.card.common.util.cardTypesResolver
|
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||||
import com.tangem.domain.card.configs.GenericCardConfig
|
import com.tangem.domain.card.configs.GenericCardConfig
|
||||||
import com.tangem.domain.demo.models.DemoConfig
|
import com.tangem.domain.demo.models.DemoConfig
|
||||||
|
|
@ -37,6 +39,8 @@ internal class DefaultCardCryptoCurrencyFactoryTest {
|
||||||
private val userWalletsStore: UserWalletsStore = mockk()
|
private val userWalletsStore: UserWalletsStore = mockk()
|
||||||
private val userTokensResponseStore: UserTokensResponseStore = mockk()
|
private val userTokensResponseStore: UserTokensResponseStore = mockk()
|
||||||
private val excludedBlockchains = ExcludedBlockchains()
|
private val excludedBlockchains = ExcludedBlockchains()
|
||||||
|
private val accountsFeatureToggles = mockk<AccountsFeatureToggles>()
|
||||||
|
private val walletAccountsFetcher = mockk<WalletAccountsFetcher>()
|
||||||
|
|
||||||
private val factory = DefaultCardCryptoCurrencyFactory(
|
private val factory = DefaultCardCryptoCurrencyFactory(
|
||||||
demoConfig = DemoConfig(),
|
demoConfig = DemoConfig(),
|
||||||
|
|
@ -46,6 +50,8 @@ internal class DefaultCardCryptoCurrencyFactoryTest {
|
||||||
responseCryptoCurrenciesFactory = ResponseCryptoCurrenciesFactory(
|
responseCryptoCurrenciesFactory = ResponseCryptoCurrenciesFactory(
|
||||||
networkFactory = NetworkFactory(excludedBlockchains = excludedBlockchains),
|
networkFactory = NetworkFactory(excludedBlockchains = excludedBlockchains),
|
||||||
),
|
),
|
||||||
|
accountsFeatureToggles = accountsFeatureToggles,
|
||||||
|
walletAccountsFetcher = walletAccountsFetcher,
|
||||||
)
|
)
|
||||||
|
|
||||||
private val cryptoCurrencyFactory = MockCryptoCurrencyFactory()
|
private val cryptoCurrencyFactory = MockCryptoCurrencyFactory()
|
||||||
|
|
@ -57,7 +63,7 @@ internal class DefaultCardCryptoCurrencyFactoryTest {
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
fun init() {
|
fun init() {
|
||||||
clearMocks(userWalletsStore, userTokensResponseStore, iconUri)
|
clearMocks(userWalletsStore, userTokensResponseStore, accountsFeatureToggles, walletAccountsFetcher, iconUri)
|
||||||
|
|
||||||
mockkStatic(Uri::class)
|
mockkStatic(Uri::class)
|
||||||
every { Uri.parse(any()) } returns iconUri
|
every { Uri.parse(any()) } returns iconUri
|
||||||
|
|
@ -75,6 +81,7 @@ internal class DefaultCardCryptoCurrencyFactoryTest {
|
||||||
val userTokensResponse = model.userTokensResponse
|
val userTokensResponse = model.userTokensResponse
|
||||||
val network = ethereum.network
|
val network = ethereum.network
|
||||||
|
|
||||||
|
every { accountsFeatureToggles.isFeatureEnabled } returns false
|
||||||
coEvery { userWalletsStore.getSyncStrict(key = userWallet.walletId) } returns userWallet
|
coEvery { userWalletsStore.getSyncStrict(key = userWallet.walletId) } returns userWallet
|
||||||
coEvery { userTokensResponseStore.getSyncOrNull(userWallet.walletId) } returns userTokensResponse
|
coEvery { userTokensResponseStore.getSyncOrNull(userWallet.walletId) } returns userTokensResponse
|
||||||
|
|
||||||
|
|
@ -236,6 +243,7 @@ internal class DefaultCardCryptoCurrencyFactoryTest {
|
||||||
val networks = setOf(ethereum.network, bitcoin.network)
|
val networks = setOf(ethereum.network, bitcoin.network)
|
||||||
val userTokensResponse = model.userTokensResponse
|
val userTokensResponse = model.userTokensResponse
|
||||||
|
|
||||||
|
every { accountsFeatureToggles.isFeatureEnabled } returns false
|
||||||
coEvery { userTokensResponseStore.getSyncOrNull(userWallet.walletId) } returns userTokensResponse
|
coEvery { userTokensResponseStore.getSyncOrNull(userWallet.walletId) } returns userTokensResponse
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,23 @@
|
||||||
package com.tangem.datasource.exchangeservice.swap
|
package com.tangem.data.express
|
||||||
|
|
||||||
|
import arrow.core.Either
|
||||||
|
import arrow.core.raise.either
|
||||||
|
import com.tangem.data.express.converter.ExpressAssetConverter
|
||||||
import com.tangem.datasource.api.common.response.getOrThrow
|
import com.tangem.datasource.api.common.response.getOrThrow
|
||||||
import com.tangem.datasource.api.express.TangemExpressApi
|
import com.tangem.datasource.api.express.TangemExpressApi
|
||||||
import com.tangem.datasource.api.express.models.request.AssetsRequestBody
|
import com.tangem.datasource.api.express.models.request.AssetsRequestBody
|
||||||
import com.tangem.datasource.api.express.models.request.LeastTokenInfo
|
import com.tangem.datasource.api.express.models.request.LeastTokenInfo
|
||||||
import com.tangem.datasource.api.express.models.response.Asset
|
|
||||||
import com.tangem.datasource.exchangeservice.swap.ExpressUtils.getRefCode
|
import com.tangem.datasource.exchangeservice.swap.ExpressUtils.getRefCode
|
||||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||||
import com.tangem.datasource.local.token.ExpressAssetsStore
|
import com.tangem.datasource.local.token.ExpressAssetsStore
|
||||||
|
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||||
import com.tangem.domain.core.lce.Lce
|
import com.tangem.domain.core.lce.Lce
|
||||||
|
import com.tangem.domain.core.utils.catchOn
|
||||||
import com.tangem.domain.core.utils.lceContent
|
import com.tangem.domain.core.utils.lceContent
|
||||||
import com.tangem.domain.core.utils.lceError
|
import com.tangem.domain.core.utils.lceError
|
||||||
import com.tangem.domain.core.utils.lceLoading
|
import com.tangem.domain.core.utils.lceLoading
|
||||||
|
import com.tangem.domain.express.ExpressServiceFetcher
|
||||||
|
import com.tangem.domain.express.models.ExpressAsset
|
||||||
import com.tangem.domain.models.wallet.UserWallet
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
|
|
@ -19,56 +25,72 @@ import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
typealias InitializationStatusFlow = MutableStateFlow<Lce<Throwable, List<Asset>>>
|
typealias InitializationStatusFlow = MutableStateFlow<Lce<Throwable, List<ExpressAsset>>>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default implementation of [ExpressServiceLoader]
|
* Default implementation of [ExpressServiceFetcher]
|
||||||
*
|
*
|
||||||
* @property tangemExpressApi express api
|
* @property tangemExpressApi express api
|
||||||
* @property expressAssetsStore local storage
|
* @property expressAssetsStore local storage
|
||||||
*
|
*
|
||||||
[REDACTED_AUTHOR]
|
[REDACTED_AUTHOR]
|
||||||
*/
|
*/
|
||||||
internal class DefaultExpressServiceLoader @Inject constructor(
|
internal class DefaultExpressServiceFetcher @Inject constructor(
|
||||||
private val tangemExpressApi: TangemExpressApi,
|
private val tangemExpressApi: TangemExpressApi,
|
||||||
private val expressAssetsStore: ExpressAssetsStore,
|
private val expressAssetsStore: ExpressAssetsStore,
|
||||||
private val appPreferencesStore: AppPreferencesStore,
|
private val appPreferencesStore: AppPreferencesStore,
|
||||||
|
private val userWalletsStore: UserWalletsStore,
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
) : ExpressServiceLoader {
|
) : ExpressServiceFetcher {
|
||||||
|
|
||||||
private val initializationStatuses =
|
private val initializationStatuses =
|
||||||
MutableStateFlow<Map<UserWalletId, InitializationStatusFlow>>(value = emptyMap())
|
MutableStateFlow<Map<UserWalletId, InitializationStatusFlow>>(value = emptyMap())
|
||||||
|
|
||||||
override suspend fun update(userWallet: UserWallet, userTokens: List<LeastTokenInfo>) {
|
override suspend fun fetch(userWalletId: UserWalletId, assetIds: Set<ExpressAsset.ID>): Either<Throwable, Unit> =
|
||||||
withContext(dispatchers.io) {
|
either {
|
||||||
|
val userWallet = arrow.core.raise.catch(
|
||||||
|
block = { userWalletsStore.getSyncStrict(userWalletId) },
|
||||||
|
catch = ::raise,
|
||||||
|
)
|
||||||
|
|
||||||
|
fetch(userWallet = userWallet, assetIds = assetIds).bind()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun fetch(userWallet: UserWallet, assetIds: Set<ExpressAsset.ID>): Either<Throwable, Unit> {
|
||||||
|
return Either.catchOn(dispatchers.io) {
|
||||||
val initializationStatus = getInitializationStatusInternal(userWallet.walletId)
|
val initializationStatus = getInitializationStatusInternal(userWallet.walletId)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (userTokens.isNotEmpty()) {
|
if (assetIds.isNotEmpty()) {
|
||||||
|
val tokenList = assetIds.map {
|
||||||
|
LeastTokenInfo(contractAddress = it.contractAddress, network = it.networkId)
|
||||||
|
}
|
||||||
|
|
||||||
val response = tangemExpressApi.getAssets(
|
val response = tangemExpressApi.getAssets(
|
||||||
userWalletId = userWallet.walletId.stringValue,
|
userWalletId = userWallet.walletId.stringValue,
|
||||||
refCode = getRefCode(userWallet, appPreferencesStore),
|
refCode = getRefCode(userWallet, appPreferencesStore),
|
||||||
body = AssetsRequestBody(tokensList = userTokens),
|
body = AssetsRequestBody(tokensList = tokenList),
|
||||||
).getOrThrow()
|
).getOrThrow()
|
||||||
|
|
||||||
expressAssetsStore.store(userWallet.walletId, response)
|
expressAssetsStore.store(userWallet.walletId, response)
|
||||||
|
|
||||||
initializationStatus.update { response.lceContent() }
|
val expressAssets = ExpressAssetConverter.convertList(response)
|
||||||
|
initializationStatus.update { expressAssets.lceContent() }
|
||||||
}
|
}
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
if (expressAssetsStore.getSyncOrNull(userWallet.walletId) == null) {
|
if (expressAssetsStore.getSyncOrNull(userWallet.walletId) == null) {
|
||||||
initializationStatus.update { e.lceError() }
|
initializationStatus.update { e.lceError() }
|
||||||
}
|
}
|
||||||
Timber.e(e, "Unable to fetch assets for: ${userWallet.walletId.stringValue}")
|
Timber.e(e, "Unable to fetch assets for: ${userWallet.walletId.stringValue}")
|
||||||
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getInitializationStatus(userWalletId: UserWalletId): Flow<Lce<Throwable, List<Asset>>> {
|
override fun getInitializationStatus(userWalletId: UserWalletId): Flow<Lce<Throwable, List<ExpressAsset>>> {
|
||||||
return flow { getInitializationStatusInternal(userWalletId).collect { emit(it) } }
|
return flow { getInitializationStatusInternal(userWalletId).collect { emit(it) } }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -77,7 +99,7 @@ internal class DefaultExpressServiceLoader @Inject constructor(
|
||||||
val initializationStatus = initializationStatuses.value[userWalletId]
|
val initializationStatus = initializationStatuses.value[userWalletId]
|
||||||
if (initializationStatus != null) return initializationStatus
|
if (initializationStatus != null) return initializationStatus
|
||||||
|
|
||||||
val cached = expressAssetsStore.getSyncOrNull(userWalletId)
|
val cached = expressAssetsStore.getSyncOrNull(userWalletId)?.let(ExpressAssetConverter::convertList)
|
||||||
val default: InitializationStatusFlow = MutableStateFlow(value = cached?.lceContent() ?: lceLoading())
|
val default: InitializationStatusFlow = MutableStateFlow(value = cached?.lceContent() ?: lceLoading())
|
||||||
|
|
||||||
initializationStatuses.update { statuses ->
|
initializationStatuses.update { statuses ->
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.tangem.data.express.converter
|
||||||
|
|
||||||
|
import com.tangem.datasource.api.express.models.response.Asset
|
||||||
|
import com.tangem.domain.express.models.ExpressAsset
|
||||||
|
import com.tangem.utils.converter.Converter
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an [Asset] from the data layer to an [ExpressAsset] in the domain layer.
|
||||||
|
*
|
||||||
|
[REDACTED_AUTHOR]
|
||||||
|
*/
|
||||||
|
internal object ExpressAssetConverter : Converter<Asset, ExpressAsset> {
|
||||||
|
|
||||||
|
override fun convert(value: Asset): ExpressAsset {
|
||||||
|
return ExpressAsset(
|
||||||
|
id = ExpressAsset.ID(
|
||||||
|
networkId = value.network,
|
||||||
|
contractAddress = value.contractAddress,
|
||||||
|
),
|
||||||
|
isExchangeAvailable = value.exchangeAvailable,
|
||||||
|
isOnrampAvailable = value.onrampAvailable,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ package com.tangem.data.express.di
|
||||||
|
|
||||||
import com.squareup.moshi.Moshi
|
import com.squareup.moshi.Moshi
|
||||||
import com.tangem.data.express.DefaultExpressRepository
|
import com.tangem.data.express.DefaultExpressRepository
|
||||||
|
import com.tangem.data.express.DefaultExpressServiceFetcher
|
||||||
import com.tangem.data.express.converter.ExpressErrorConverter
|
import com.tangem.data.express.converter.ExpressErrorConverter
|
||||||
import com.tangem.data.express.error.DefaultExpressErrorResolver
|
import com.tangem.data.express.error.DefaultExpressErrorResolver
|
||||||
import com.tangem.datasource.api.express.TangemExpressApi
|
import com.tangem.datasource.api.express.TangemExpressApi
|
||||||
|
|
@ -10,6 +11,7 @@ import com.tangem.datasource.di.NetworkMoshi
|
||||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||||
import com.tangem.domain.express.ExpressErrorResolver
|
import com.tangem.domain.express.ExpressErrorResolver
|
||||||
import com.tangem.domain.express.ExpressRepository
|
import com.tangem.domain.express.ExpressRepository
|
||||||
|
import com.tangem.domain.express.ExpressServiceFetcher
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
|
|
@ -43,4 +45,10 @@ internal object ExpressDataModule {
|
||||||
dispatchers = dispatchers,
|
dispatchers = dispatchers,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideExpressServiceFetcher(impl: DefaultExpressServiceFetcher): ExpressServiceFetcher {
|
||||||
|
return impl
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -238,7 +238,7 @@ internal class DefaultCustomTokensRepository(
|
||||||
"User tokens not found for user wallet [$userWalletId] while removing currency"
|
"User tokens not found for user wallet [$userWalletId] while removing currency"
|
||||||
}
|
}
|
||||||
|
|
||||||
val token = userTokensResponseFactory.createResponseToken(cryptoCurrency)
|
val token = userTokensResponseFactory.createResponseToken(currency = cryptoCurrency, accountId = null)
|
||||||
userTokensSaver.storeAndPush(
|
userTokensSaver.storeAndPush(
|
||||||
userWalletId = userWalletId,
|
userWalletId = userWalletId,
|
||||||
response = storedCurrencies.copy(tokens = storedCurrencies.tokens.filterNot { it == token }),
|
response = storedCurrencies.copy(tokens = storedCurrencies.tokens.filterNot { it == token }),
|
||||||
|
|
@ -249,6 +249,27 @@ internal class DefaultCustomTokensRepository(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun convertToCryptoCurrency(
|
||||||
|
userWalletId: UserWalletId,
|
||||||
|
currency: ManagedCryptoCurrency.Custom,
|
||||||
|
): CryptoCurrency {
|
||||||
|
return when (currency) {
|
||||||
|
is ManagedCryptoCurrency.Custom.Coin -> createCoin(
|
||||||
|
userWalletId = userWalletId,
|
||||||
|
networkId = currency.network.id,
|
||||||
|
derivationPath = currency.network.derivationPath,
|
||||||
|
)
|
||||||
|
is ManagedCryptoCurrency.Custom.Token -> cryptoCurrencyFactory.createToken(
|
||||||
|
network = currency.network,
|
||||||
|
rawId = currency.currencyId.rawCurrencyId,
|
||||||
|
name = currency.name,
|
||||||
|
symbol = currency.symbol,
|
||||||
|
decimals = currency.decimals,
|
||||||
|
contractAddress = currency.contractAddress,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun getSupportedNetworks(userWalletId: UserWalletId): List<Network> = withContext(dispatchers.io) {
|
override suspend fun getSupportedNetworks(userWalletId: UserWalletId): List<Network> = withContext(dispatchers.io) {
|
||||||
val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) {
|
val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) {
|
||||||
"User wallet [$userWalletId] not found while getting supported networks"
|
"User wallet [$userWalletId] not found while getting supported networks"
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||||
import com.tangem.blockchainsdk.utils.toBlockchain
|
import com.tangem.blockchainsdk.utils.toBlockchain
|
||||||
import com.tangem.blockchainsdk.utils.toNetworkId
|
import com.tangem.blockchainsdk.utils.toNetworkId
|
||||||
|
import com.tangem.data.common.account.WalletAccountsFetcher
|
||||||
import com.tangem.data.common.api.safeApiCall
|
import com.tangem.data.common.api.safeApiCall
|
||||||
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
||||||
import com.tangem.data.common.currency.UserTokensResponseFactory
|
import com.tangem.data.common.currency.UserTokensResponseFactory
|
||||||
|
|
@ -21,15 +22,13 @@ import com.tangem.datasource.local.config.testnet.TestnetTokensStorage
|
||||||
import com.tangem.datasource.local.token.UserTokensResponseStore
|
import com.tangem.datasource.local.token.UserTokensResponseStore
|
||||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||||
import com.tangem.domain.card.common.TapWorkarounds.isTestCard
|
import com.tangem.domain.card.common.TapWorkarounds.isTestCard
|
||||||
import com.tangem.domain.card.common.extensions.canHandleBlockchain
|
import com.tangem.domain.card.common.extensions.*
|
||||||
import com.tangem.domain.card.common.extensions.canHandleToken
|
|
||||||
import com.tangem.domain.card.common.extensions.hotWalletExcludedBlockchains
|
|
||||||
import com.tangem.domain.card.common.extensions.supportedBlockchains
|
|
||||||
import com.tangem.domain.card.common.extensions.supportedTokens
|
|
||||||
import com.tangem.domain.card.common.util.cardTypesResolver
|
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||||
import com.tangem.domain.managetokens.model.*
|
import com.tangem.domain.managetokens.model.*
|
||||||
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency.SourceNetwork
|
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency.SourceNetwork
|
||||||
import com.tangem.domain.managetokens.repository.ManageTokensRepository
|
import com.tangem.domain.managetokens.repository.ManageTokensRepository
|
||||||
|
import com.tangem.domain.models.account.AccountId
|
||||||
|
import com.tangem.domain.models.account.DerivationIndex
|
||||||
import com.tangem.domain.models.network.Network
|
import com.tangem.domain.models.network.Network
|
||||||
import com.tangem.domain.models.wallet.UserWallet
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
|
@ -40,7 +39,7 @@ import com.tangem.pagination.fetcher.LimitOffsetBatchFetcher.Request
|
||||||
import com.tangem.pagination.toBatchFlow
|
import com.tangem.pagination.toBatchFlow
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
|
|
||||||
@Suppress("LongParameterList")
|
@Suppress("LongParameterList", "LargeClass")
|
||||||
internal class DefaultManageTokensRepository(
|
internal class DefaultManageTokensRepository(
|
||||||
private val tangemTechApi: TangemTechApi,
|
private val tangemTechApi: TangemTechApi,
|
||||||
private val userWalletsStore: UserWalletsStore,
|
private val userWalletsStore: UserWalletsStore,
|
||||||
|
|
@ -51,6 +50,7 @@ internal class DefaultManageTokensRepository(
|
||||||
private val excludedBlockchains: ExcludedBlockchains,
|
private val excludedBlockchains: ExcludedBlockchains,
|
||||||
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
|
private val walletAccountsFetcher: WalletAccountsFetcher,
|
||||||
networkFactory: NetworkFactory,
|
networkFactory: NetworkFactory,
|
||||||
) : ManageTokensRepository {
|
) : ManageTokensRepository {
|
||||||
|
|
||||||
|
|
@ -94,7 +94,6 @@ internal class DefaultManageTokensRepository(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@Suppress("ComplexCondition")
|
|
||||||
private suspend fun fetchCurrencies(
|
private suspend fun fetchCurrencies(
|
||||||
userWallet: UserWallet?,
|
userWallet: UserWallet?,
|
||||||
request: Request<ManageTokensListConfig>,
|
request: Request<ManageTokensListConfig>,
|
||||||
|
|
@ -128,20 +127,22 @@ internal class DefaultManageTokensRepository(
|
||||||
)
|
)
|
||||||
|
|
||||||
val tokensResponse = request.params.userWalletId?.let { userWalletId ->
|
val tokensResponse = request.params.userWalletId?.let { userWalletId ->
|
||||||
if (loadUserTokensFromRemote && userWallet != null) {
|
when (val params = request.params) {
|
||||||
safeApiCall({ tangemTechApi.getUserTokens(userWalletId.stringValue).bind() }) {
|
is ManageTokensListConfig.Account -> {
|
||||||
// save tokens response only if loadUserTokensFromRemote is true and it means onboarding call
|
fetchUserTokens(userWallet, userWalletId, params, loadUserTokensFromRemote)
|
||||||
createAndSaveDefaultUserTokensResponse(userWallet = userWallet)
|
}
|
||||||
|
is ManageTokensListConfig.Wallet -> {
|
||||||
|
fetchUserTokensLegacy(userWallet, userWalletId, loadUserTokensFromRemote)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
getSavedUserTokensResponseSync(userWalletId)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val items = if (isFirstBatchFetching &&
|
|
||||||
|
val isCreateWithCustom = isFirstBatchFetching &&
|
||||||
tokensResponse != null &&
|
tokensResponse != null &&
|
||||||
userWallet != null &&
|
userWallet != null &&
|
||||||
query == null
|
query == null
|
||||||
) {
|
|
||||||
|
val items = if (isCreateWithCustom) {
|
||||||
managedCryptoCurrencyFactory.createWithCustomTokens(
|
managedCryptoCurrencyFactory.createWithCustomTokens(
|
||||||
coinsResponse = updatedCoinsResponse,
|
coinsResponse = updatedCoinsResponse,
|
||||||
tokensResponse = tokensResponse,
|
tokensResponse = tokensResponse,
|
||||||
|
|
@ -162,6 +163,56 @@ internal class DefaultManageTokensRepository(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun fetchUserTokens(
|
||||||
|
userWallet: UserWallet?,
|
||||||
|
userWalletId: UserWalletId,
|
||||||
|
params: ManageTokensListConfig.Account,
|
||||||
|
loadUserTokensFromRemote: Boolean,
|
||||||
|
): UserTokensResponse? {
|
||||||
|
val accountId = when {
|
||||||
|
params.accountId == null -> {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
loadUserTokensFromRemote -> {
|
||||||
|
AccountId.forCryptoPortfolio(userWalletId = userWalletId, derivationIndex = DerivationIndex.Main)
|
||||||
|
}
|
||||||
|
else -> requireNotNull(params.accountId)
|
||||||
|
}
|
||||||
|
|
||||||
|
val response = if (loadUserTokensFromRemote && userWallet != null) {
|
||||||
|
runCatching { walletAccountsFetcher.fetch(userWalletId = userWallet.walletId) }.getOrNull()
|
||||||
|
} else {
|
||||||
|
walletAccountsFetcher.getSaved(userWalletId)
|
||||||
|
}
|
||||||
|
|
||||||
|
val account = response?.accounts?.firstOrNull { it.id == accountId.value }
|
||||||
|
?: return null
|
||||||
|
|
||||||
|
return UserTokensResponse(
|
||||||
|
group = response.wallet.group,
|
||||||
|
sort = response.wallet.sort,
|
||||||
|
tokens = account.tokens.orEmpty(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun fetchUserTokensLegacy(
|
||||||
|
userWallet: UserWallet?,
|
||||||
|
userWalletId: UserWalletId,
|
||||||
|
loadUserTokensFromRemote: Boolean,
|
||||||
|
): UserTokensResponse? {
|
||||||
|
return if (loadUserTokensFromRemote && userWallet != null) {
|
||||||
|
safeApiCall(
|
||||||
|
call = { tangemTechApi.getUserTokens(userWalletId.stringValue).bind() },
|
||||||
|
onError = {
|
||||||
|
// save tokens response only if loadUserTokensFromRemote is true and it means onboarding call
|
||||||
|
createAndSaveDefaultUserTokensResponse(userWallet = userWallet)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
getSavedUserTokensResponseSync(userWalletId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun createAndSaveDefaultUserTokensResponse(userWallet: UserWallet): UserTokensResponse {
|
private suspend fun createAndSaveDefaultUserTokensResponse(userWallet: UserWallet): UserTokensResponse {
|
||||||
val userTokensResponse = createDefaultUserTokensResponse(userWallet)
|
val userTokensResponse = createDefaultUserTokensResponse(userWallet)
|
||||||
userTokenSaver.store(userWallet.walletId, userTokensResponse, useEnricher = false)
|
userTokenSaver.store(userWallet.walletId, userTokensResponse, useEnricher = false)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package com.tangem.data.managetokens.di
|
package com.tangem.data.managetokens.di
|
||||||
|
|
||||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||||
|
import com.tangem.data.common.account.WalletAccountsFetcher
|
||||||
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
||||||
import com.tangem.data.common.currency.UserTokensSaver
|
import com.tangem.data.common.currency.UserTokensSaver
|
||||||
import com.tangem.data.common.network.NetworkFactory
|
import com.tangem.data.common.network.NetworkFactory
|
||||||
|
|
@ -38,6 +39,7 @@ internal object ManageTokensDataModule {
|
||||||
excludedBlockchains: ExcludedBlockchains,
|
excludedBlockchains: ExcludedBlockchains,
|
||||||
cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
||||||
networkFactory: NetworkFactory,
|
networkFactory: NetworkFactory,
|
||||||
|
walletAccountsFetcher: WalletAccountsFetcher,
|
||||||
): ManageTokensRepository {
|
): ManageTokensRepository {
|
||||||
return DefaultManageTokensRepository(
|
return DefaultManageTokensRepository(
|
||||||
tangemTechApi = tangemTechApi,
|
tangemTechApi = tangemTechApi,
|
||||||
|
|
@ -50,6 +52,7 @@ internal object ManageTokensDataModule {
|
||||||
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
|
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
|
||||||
networkFactory = networkFactory,
|
networkFactory = networkFactory,
|
||||||
dispatchers = dispatchers,
|
dispatchers = dispatchers,
|
||||||
|
walletAccountsFetcher = walletAccountsFetcher,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ dependencies {
|
||||||
implementation(projects.domain.walletManager)
|
implementation(projects.domain.walletManager)
|
||||||
implementation(projects.domain.appTheme.models)
|
implementation(projects.domain.appTheme.models)
|
||||||
implementation(projects.domain.models)
|
implementation(projects.domain.models)
|
||||||
|
implementation(projects.domain.express.models)
|
||||||
|
|
||||||
// region DI
|
// region DI
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ import com.tangem.data.onramp.converters.error.OnrampErrorConverter
|
||||||
import com.tangem.datasource.api.common.response.ApiResponseError
|
import com.tangem.datasource.api.common.response.ApiResponseError
|
||||||
import com.tangem.datasource.api.common.response.getOrThrow
|
import com.tangem.datasource.api.common.response.getOrThrow
|
||||||
import com.tangem.datasource.api.express.TangemExpressApi
|
import com.tangem.datasource.api.express.TangemExpressApi
|
||||||
import com.tangem.datasource.api.express.models.TangemExpressValues
|
|
||||||
import com.tangem.datasource.api.express.models.response.ExchangeProvider
|
import com.tangem.datasource.api.express.models.response.ExchangeProvider
|
||||||
import com.tangem.datasource.api.express.models.response.ExchangeProviderType
|
import com.tangem.datasource.api.express.models.response.ExchangeProviderType
|
||||||
import com.tangem.datasource.api.express.models.response.ExpressErrorResponse
|
import com.tangem.datasource.api.express.models.response.ExpressErrorResponse
|
||||||
|
|
@ -38,6 +37,7 @@ import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||||
import com.tangem.datasource.local.preferences.utils.getObject
|
import com.tangem.datasource.local.preferences.utils.getObject
|
||||||
import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull
|
import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull
|
||||||
import com.tangem.datasource.local.preferences.utils.storeObject
|
import com.tangem.datasource.local.preferences.utils.storeObject
|
||||||
|
import com.tangem.domain.express.models.ExpressAsset
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import com.tangem.domain.models.wallet.UserWallet
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
|
@ -560,7 +560,7 @@ internal class DefaultOnrampRepository(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CryptoCurrency.getContractAddress(): String = when (this) {
|
private fun CryptoCurrency.getContractAddress(): String = when (this) {
|
||||||
is CryptoCurrency.Coin -> TangemExpressValues.EMPTY_CONTRACT_ADDRESS_VALUE
|
is CryptoCurrency.Coin -> ExpressAsset.EMPTY_CONTRACT_ADDRESS_VALUE
|
||||||
is CryptoCurrency.Token -> this.contractAddress
|
is CryptoCurrency.Token -> this.contractAddress
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -160,7 +160,7 @@ public val Blockchain.mercuryoNetwork: String?
|
||||||
Blockchain.Pepecoin, Blockchain.PepecoinTestnet -> null
|
Blockchain.Pepecoin, Blockchain.PepecoinTestnet -> null
|
||||||
Blockchain.Hyperliquid, Blockchain.HyperliquidTestnet -> null
|
Blockchain.Hyperliquid, Blockchain.HyperliquidTestnet -> null
|
||||||
Blockchain.Quai, Blockchain.QuaiTestnet -> null
|
Blockchain.Quai, Blockchain.QuaiTestnet -> null
|
||||||
// Blockchain.Linea, Blockchain.LineaTestnet -> null
|
Blockchain.Linea, Blockchain.LineaTestnet -> null
|
||||||
// Blockchain.ArbitrumNova -> null
|
Blockchain.ArbitrumNova -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -28,6 +28,7 @@ dependencies {
|
||||||
implementation(projects.domain.card)
|
implementation(projects.domain.card)
|
||||||
implementation(projects.domain.core)
|
implementation(projects.domain.core)
|
||||||
implementation(projects.domain.demo)
|
implementation(projects.domain.demo)
|
||||||
|
implementation(projects.domain.express)
|
||||||
implementation(projects.domain.legacy)
|
implementation(projects.domain.legacy)
|
||||||
implementation(projects.domain.models)
|
implementation(projects.domain.models)
|
||||||
implementation(projects.domain.staking)
|
implementation(projects.domain.staking)
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,13 @@
|
||||||
package com.tangem.data.tokens
|
package com.tangem.data.tokens
|
||||||
|
|
||||||
import arrow.core.Either
|
import arrow.core.Either
|
||||||
|
import arrow.core.right
|
||||||
import com.tangem.data.common.account.WalletAccountsFetcher
|
import com.tangem.data.common.account.WalletAccountsFetcher
|
||||||
|
import com.tangem.datasource.api.tangemTech.models.account.flattenTokens
|
||||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||||
import com.tangem.domain.core.utils.catchOn
|
import com.tangem.domain.core.utils.catchOn
|
||||||
|
import com.tangem.domain.express.ExpressServiceFetcher
|
||||||
|
import com.tangem.domain.express.models.ExpressAsset
|
||||||
import com.tangem.domain.models.wallet.UserWallet
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
import com.tangem.domain.models.wallet.isMultiCurrency
|
import com.tangem.domain.models.wallet.isMultiCurrency
|
||||||
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesFetcher
|
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesFetcher
|
||||||
|
|
@ -15,6 +19,7 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
*
|
*
|
||||||
* @property userWalletsStore [UserWallet]'s store
|
* @property userWalletsStore [UserWallet]'s store
|
||||||
* @property walletAccountsFetcher instance of [WalletAccountsFetcher] to fetch accounts for a multi wallet
|
* @property walletAccountsFetcher instance of [WalletAccountsFetcher] to fetch accounts for a multi wallet
|
||||||
|
* @property expressServiceFetcher fetcher of express service
|
||||||
* @property dispatchers dispatchers
|
* @property dispatchers dispatchers
|
||||||
*
|
*
|
||||||
[REDACTED_AUTHOR]
|
[REDACTED_AUTHOR]
|
||||||
|
|
@ -22,14 +27,26 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
internal class AccountListCryptoCurrenciesFetcher(
|
internal class AccountListCryptoCurrenciesFetcher(
|
||||||
private val userWalletsStore: UserWalletsStore,
|
private val userWalletsStore: UserWalletsStore,
|
||||||
private val walletAccountsFetcher: WalletAccountsFetcher,
|
private val walletAccountsFetcher: WalletAccountsFetcher,
|
||||||
|
private val expressServiceFetcher: ExpressServiceFetcher,
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
) : MultiWalletCryptoCurrenciesFetcher {
|
) : MultiWalletCryptoCurrenciesFetcher {
|
||||||
|
|
||||||
override suspend fun invoke(params: Params) = Either.catchOn(dispatchers.default) {
|
override suspend fun invoke(params: Params): Either<Throwable, Unit> {
|
||||||
val userWallet = userWalletsStore.getSyncStrict(key = params.userWalletId)
|
return Either.catchOn(dispatchers.default) {
|
||||||
|
val userWallet = userWalletsStore.getSyncStrict(key = params.userWalletId)
|
||||||
|
|
||||||
if (!userWallet.isMultiCurrency) error("${this::class.simpleName} supports only multi-currency wallet")
|
if (!userWallet.isMultiCurrency) error("${this::class.simpleName} supports only multi-currency wallet")
|
||||||
|
|
||||||
walletAccountsFetcher.fetch(userWalletId = params.userWalletId)
|
val response = walletAccountsFetcher.fetch(userWalletId = params.userWalletId)
|
||||||
|
|
||||||
|
expressServiceFetcher.fetch(
|
||||||
|
userWallet = userWallet,
|
||||||
|
assetIds = response.flattenTokens().mapTo(hashSetOf()) {
|
||||||
|
ExpressAsset.ID(networkId = it.networkId, contractAddress = it.contractAddress)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
Unit.right()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7,15 +7,14 @@ import com.tangem.data.common.currency.UserTokensResponseFactory
|
||||||
import com.tangem.data.common.currency.UserTokensSaver
|
import com.tangem.data.common.currency.UserTokensSaver
|
||||||
import com.tangem.data.tokens.utils.CustomTokensMerger
|
import com.tangem.data.tokens.utils.CustomTokensMerger
|
||||||
import com.tangem.datasource.api.common.response.ApiResponseError
|
import com.tangem.datasource.api.common.response.ApiResponseError
|
||||||
import com.tangem.datasource.api.express.models.TangemExpressValues.EMPTY_CONTRACT_ADDRESS_VALUE
|
|
||||||
import com.tangem.datasource.api.express.models.request.LeastTokenInfo
|
|
||||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||||
import com.tangem.datasource.exchangeservice.swap.ExpressServiceLoader
|
|
||||||
import com.tangem.datasource.local.token.UserTokensResponseStore
|
import com.tangem.datasource.local.token.UserTokensResponseStore
|
||||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||||
import com.tangem.domain.core.utils.catchOn
|
import com.tangem.domain.core.utils.catchOn
|
||||||
import com.tangem.domain.demo.models.DemoConfig
|
import com.tangem.domain.demo.models.DemoConfig
|
||||||
|
import com.tangem.domain.express.ExpressServiceFetcher
|
||||||
|
import com.tangem.domain.express.models.ExpressAsset
|
||||||
import com.tangem.domain.models.wallet.UserWallet
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
import com.tangem.domain.models.wallet.isMultiCurrency
|
import com.tangem.domain.models.wallet.isMultiCurrency
|
||||||
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesFetcher
|
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesFetcher
|
||||||
|
|
@ -32,7 +31,7 @@ import timber.log.Timber
|
||||||
* @property userTokensResponseStore store of [UserTokensResponse]
|
* @property userTokensResponseStore store of [UserTokensResponse]
|
||||||
* @property userTokensSaver user tokens saver
|
* @property userTokensSaver user tokens saver
|
||||||
* @property cardCryptoCurrencyFactory factory for creating crypto currencies for specified card
|
* @property cardCryptoCurrencyFactory factory for creating crypto currencies for specified card
|
||||||
* @property expressServiceLoader express service loader
|
* @property expressServiceFetcher express service loader
|
||||||
* @property dispatchers dispatchers
|
* @property dispatchers dispatchers
|
||||||
*
|
*
|
||||||
[REDACTED_AUTHOR]
|
[REDACTED_AUTHOR]
|
||||||
|
|
@ -46,7 +45,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcher(
|
||||||
private val userTokensResponseStore: UserTokensResponseStore,
|
private val userTokensResponseStore: UserTokensResponseStore,
|
||||||
private val userTokensSaver: UserTokensSaver,
|
private val userTokensSaver: UserTokensSaver,
|
||||||
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
||||||
private val expressServiceLoader: ExpressServiceLoader,
|
private val expressServiceFetcher: ExpressServiceFetcher,
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
) : MultiWalletCryptoCurrenciesFetcher {
|
) : MultiWalletCryptoCurrenciesFetcher {
|
||||||
|
|
||||||
|
|
@ -109,14 +108,14 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcher(
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun fetchExpressAssetsByNetworkIds(userWallet: UserWallet, userTokens: UserTokensResponse) {
|
private suspend fun fetchExpressAssetsByNetworkIds(userWallet: UserWallet, userTokens: UserTokensResponse) {
|
||||||
val tokens = userTokens.tokens.map { token ->
|
val tokens = userTokens.tokens.mapTo(hashSetOf()) { token ->
|
||||||
LeastTokenInfo(
|
ExpressAsset.ID(
|
||||||
contractAddress = token.contractAddress ?: EMPTY_CONTRACT_ADDRESS_VALUE,
|
networkId = token.networkId,
|
||||||
network = token.networkId,
|
contractAddress = token.contractAddress,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
expressServiceLoader.update(userWallet = userWallet, userTokens = tokens)
|
expressServiceFetcher.fetch(userWallet = userWallet, assetIds = tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createDefaultUserTokensResponse(userWallet: UserWallet): UserTokensResponse {
|
private fun createDefaultUserTokensResponse(userWallet: UserWallet): UserTokensResponse {
|
||||||
|
|
@ -124,6 +123,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcher(
|
||||||
currencies = cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyWallet(userWallet),
|
currencies = cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyWallet(userWallet),
|
||||||
isGroupedByNetwork = false,
|
isGroupedByNetwork = false,
|
||||||
isSortedByBalance = false,
|
isSortedByBalance = false,
|
||||||
|
accountId = null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7,11 +7,11 @@ import com.tangem.data.tokens.AccountListCryptoCurrenciesFetcher
|
||||||
import com.tangem.data.tokens.DefaultMultiWalletCryptoCurrenciesFetcher
|
import com.tangem.data.tokens.DefaultMultiWalletCryptoCurrenciesFetcher
|
||||||
import com.tangem.data.tokens.utils.CustomTokensMerger
|
import com.tangem.data.tokens.utils.CustomTokensMerger
|
||||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||||
import com.tangem.datasource.exchangeservice.swap.ExpressServiceLoader
|
|
||||||
import com.tangem.datasource.local.token.UserTokensResponseStore
|
import com.tangem.datasource.local.token.UserTokensResponseStore
|
||||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||||
import com.tangem.domain.demo.models.DemoConfig
|
import com.tangem.domain.demo.models.DemoConfig
|
||||||
|
import com.tangem.domain.express.ExpressServiceFetcher
|
||||||
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesFetcher
|
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesFetcher
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
|
|
@ -33,7 +33,7 @@ internal class MultiWalletCryptoCurrenciesFetcherModule {
|
||||||
userTokensResponseStore: UserTokensResponseStore,
|
userTokensResponseStore: UserTokensResponseStore,
|
||||||
userTokensSaver: UserTokensSaver,
|
userTokensSaver: UserTokensSaver,
|
||||||
cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
||||||
expressServiceLoader: ExpressServiceLoader,
|
expressServiceFetcher: ExpressServiceFetcher,
|
||||||
walletAccountsFetcher: WalletAccountsFetcher,
|
walletAccountsFetcher: WalletAccountsFetcher,
|
||||||
dispatchers: CoroutineDispatcherProvider,
|
dispatchers: CoroutineDispatcherProvider,
|
||||||
): MultiWalletCryptoCurrenciesFetcher {
|
): MultiWalletCryptoCurrenciesFetcher {
|
||||||
|
|
@ -41,6 +41,7 @@ internal class MultiWalletCryptoCurrenciesFetcherModule {
|
||||||
AccountListCryptoCurrenciesFetcher(
|
AccountListCryptoCurrenciesFetcher(
|
||||||
userWalletsStore = userWalletsStore,
|
userWalletsStore = userWalletsStore,
|
||||||
walletAccountsFetcher = walletAccountsFetcher,
|
walletAccountsFetcher = walletAccountsFetcher,
|
||||||
|
expressServiceFetcher = expressServiceFetcher,
|
||||||
dispatchers = dispatchers,
|
dispatchers = dispatchers,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -56,7 +57,7 @@ internal class MultiWalletCryptoCurrenciesFetcherModule {
|
||||||
userTokensResponseStore = userTokensResponseStore,
|
userTokensResponseStore = userTokensResponseStore,
|
||||||
userTokensSaver = userTokensSaver,
|
userTokensSaver = userTokensSaver,
|
||||||
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
|
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
|
||||||
expressServiceLoader = expressServiceLoader,
|
expressServiceFetcher = expressServiceFetcher,
|
||||||
dispatchers = dispatchers,
|
dispatchers = dispatchers,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,22 +5,14 @@ import com.tangem.data.common.cache.CacheRegistry
|
||||||
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
||||||
import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory
|
import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory
|
||||||
import com.tangem.data.common.currency.UserTokensSaver
|
import com.tangem.data.common.currency.UserTokensSaver
|
||||||
import com.tangem.data.tokens.repository.DefaultCurrenciesRepository
|
import com.tangem.data.tokens.repository.*
|
||||||
import com.tangem.data.tokens.repository.DefaultCurrencyChecksRepository
|
|
||||||
import com.tangem.data.tokens.repository.DefaultPolkadotAccountHealthCheckRepository
|
|
||||||
import com.tangem.data.tokens.repository.DefaultTokenReceiveWarningsViewedRepository
|
|
||||||
import com.tangem.data.tokens.repository.DefaultYieldSupplyWarningsViewedRepository
|
|
||||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||||
import com.tangem.datasource.exchangeservice.swap.ExpressServiceLoader
|
|
||||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||||
import com.tangem.datasource.local.token.TokenReceiveWarningActionStore
|
import com.tangem.datasource.local.token.TokenReceiveWarningActionStore
|
||||||
import com.tangem.datasource.local.token.UserTokensResponseStore
|
import com.tangem.datasource.local.token.UserTokensResponseStore
|
||||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
import com.tangem.domain.express.ExpressServiceFetcher
|
||||||
import com.tangem.domain.tokens.repository.CurrencyChecksRepository
|
import com.tangem.domain.tokens.repository.*
|
||||||
import com.tangem.domain.tokens.repository.PolkadotAccountHealthCheckRepository
|
|
||||||
import com.tangem.domain.tokens.repository.TokenReceiveWarningsViewedRepository
|
|
||||||
import com.tangem.domain.tokens.repository.YieldSupplyWarningsViewedRepository
|
|
||||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
|
|
@ -42,7 +34,7 @@ internal object TokensDataModule {
|
||||||
walletManagersFacade: WalletManagersFacade,
|
walletManagersFacade: WalletManagersFacade,
|
||||||
cacheRegistry: CacheRegistry,
|
cacheRegistry: CacheRegistry,
|
||||||
dispatchers: CoroutineDispatcherProvider,
|
dispatchers: CoroutineDispatcherProvider,
|
||||||
expressServiceLoader: ExpressServiceLoader,
|
expressServiceFetcher: ExpressServiceFetcher,
|
||||||
excludedBlockchains: ExcludedBlockchains,
|
excludedBlockchains: ExcludedBlockchains,
|
||||||
cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
||||||
tokensSaver: UserTokensSaver,
|
tokensSaver: UserTokensSaver,
|
||||||
|
|
@ -54,7 +46,7 @@ internal object TokensDataModule {
|
||||||
walletManagersFacade = walletManagersFacade,
|
walletManagersFacade = walletManagersFacade,
|
||||||
cacheRegistry = cacheRegistry,
|
cacheRegistry = cacheRegistry,
|
||||||
userTokensResponseStore = userTokensResponseStore,
|
userTokensResponseStore = userTokensResponseStore,
|
||||||
expressServiceLoader = expressServiceLoader,
|
expressServiceFetcher = expressServiceFetcher,
|
||||||
dispatchers = dispatchers,
|
dispatchers = dispatchers,
|
||||||
excludedBlockchains = excludedBlockchains,
|
excludedBlockchains = excludedBlockchains,
|
||||||
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
|
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
|
||||||
|
|
|
||||||
|
|
@ -10,17 +10,16 @@ import com.tangem.data.common.currency.*
|
||||||
import com.tangem.data.tokens.utils.CustomTokensMerger
|
import com.tangem.data.tokens.utils.CustomTokensMerger
|
||||||
import com.tangem.datasource.api.common.response.ApiResponseError
|
import com.tangem.datasource.api.common.response.ApiResponseError
|
||||||
import com.tangem.datasource.api.common.response.getOrThrow
|
import com.tangem.datasource.api.common.response.getOrThrow
|
||||||
import com.tangem.datasource.api.express.models.TangemExpressValues.EMPTY_CONTRACT_ADDRESS_VALUE
|
|
||||||
import com.tangem.datasource.api.express.models.request.LeastTokenInfo
|
|
||||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||||
import com.tangem.datasource.exchangeservice.swap.ExpressServiceLoader
|
|
||||||
import com.tangem.datasource.local.token.UserTokensResponseStore
|
import com.tangem.datasource.local.token.UserTokensResponseStore
|
||||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||||
import com.tangem.domain.card.CardTypesResolver
|
import com.tangem.domain.card.CardTypesResolver
|
||||||
import com.tangem.domain.card.common.util.cardTypesResolver
|
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||||
import com.tangem.domain.core.error.DataError
|
import com.tangem.domain.core.error.DataError
|
||||||
import com.tangem.domain.demo.models.DemoConfig
|
import com.tangem.domain.demo.models.DemoConfig
|
||||||
|
import com.tangem.domain.express.ExpressServiceFetcher
|
||||||
|
import com.tangem.domain.express.models.ExpressAsset
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||||
import com.tangem.domain.models.network.Network
|
import com.tangem.domain.models.network.Network
|
||||||
|
|
@ -40,7 +39,7 @@ internal class DefaultCurrenciesRepository(
|
||||||
private val userWalletsStore: UserWalletsStore,
|
private val userWalletsStore: UserWalletsStore,
|
||||||
private val walletManagersFacade: WalletManagersFacade,
|
private val walletManagersFacade: WalletManagersFacade,
|
||||||
private val cacheRegistry: CacheRegistry,
|
private val cacheRegistry: CacheRegistry,
|
||||||
private val expressServiceLoader: ExpressServiceLoader,
|
private val expressServiceFetcher: ExpressServiceFetcher,
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
||||||
private val userTokensSaver: UserTokensSaver,
|
private val userTokensSaver: UserTokensSaver,
|
||||||
|
|
@ -74,26 +73,6 @@ internal class DefaultCurrenciesRepository(
|
||||||
userTokensSaver.storeAndPush(userWalletId, response)
|
userTokensSaver.storeAndPush(userWalletId, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun saveCurrenciesLocal(userWalletId: UserWalletId, currencies: List<CryptoCurrency>) {
|
|
||||||
withContext(dispatchers.io) {
|
|
||||||
val savedResponse = requireNotNull(
|
|
||||||
value = getSavedUserTokensResponseSync(key = userWalletId),
|
|
||||||
lazyMessage = { "Saved tokens empty. Can not perform add currencies action." },
|
|
||||||
)
|
|
||||||
|
|
||||||
val updatedResponse = savedResponse.copy(
|
|
||||||
tokens = currencies.map(userTokensResponseFactory::createResponseToken),
|
|
||||||
)
|
|
||||||
|
|
||||||
userTokensSaver.store(userWalletId = userWalletId, response = updatedResponse)
|
|
||||||
|
|
||||||
fetchExpressAssetsByNetworkIds(
|
|
||||||
userWallet = userWalletsStore.getSyncStrict(key = userWalletId),
|
|
||||||
userTokens = updatedResponse,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun addCurrenciesCache(
|
override suspend fun addCurrenciesCache(
|
||||||
userWalletId: UserWalletId,
|
userWalletId: UserWalletId,
|
||||||
currencies: List<CryptoCurrency>,
|
currencies: List<CryptoCurrency>,
|
||||||
|
|
@ -667,16 +646,17 @@ internal class DefaultCurrenciesRepository(
|
||||||
return demoConfig.isDemoCardId(userWallet.cardId) && response == null
|
return demoConfig.isDemoCardId(userWallet.cardId) && response == null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO [REDACTED_JIRA]
|
||||||
private suspend fun fetchExpressAssetsByNetworkIds(userWallet: UserWallet, userTokens: UserTokensResponse) {
|
private suspend fun fetchExpressAssetsByNetworkIds(userWallet: UserWallet, userTokens: UserTokensResponse) {
|
||||||
val tokens = userTokens.tokens.map { token ->
|
val tokens = userTokens.tokens.mapTo(hashSetOf()) { token ->
|
||||||
LeastTokenInfo(
|
ExpressAsset.ID(
|
||||||
contractAddress = token.contractAddress ?: EMPTY_CONTRACT_ADDRESS_VALUE,
|
networkId = token.networkId,
|
||||||
network = token.networkId,
|
contractAddress = token.contractAddress,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
coroutineScope {
|
coroutineScope {
|
||||||
launch { expressServiceLoader.update(userWallet, tokens) }
|
launch { expressServiceFetcher.fetch(userWallet, tokens) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -685,11 +665,11 @@ internal class DefaultCurrenciesRepository(
|
||||||
cryptoCurrencies: List<CryptoCurrency>,
|
cryptoCurrencies: List<CryptoCurrency>,
|
||||||
refresh: Boolean = false,
|
refresh: Boolean = false,
|
||||||
) {
|
) {
|
||||||
val tokens = cryptoCurrencies.map { currency ->
|
val tokens = cryptoCurrencies.mapTo(hashSetOf()) { currency ->
|
||||||
val tokenCurrency = currency as? CryptoCurrency.Token
|
val tokenCurrency = currency as? CryptoCurrency.Token
|
||||||
LeastTokenInfo(
|
ExpressAsset.ID(
|
||||||
contractAddress = tokenCurrency?.contractAddress ?: EMPTY_CONTRACT_ADDRESS_VALUE,
|
networkId = currency.network.backendId,
|
||||||
network = currency.network.backendId,
|
contractAddress = tokenCurrency?.contractAddress,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
cacheRegistry.invokeOnExpire(
|
cacheRegistry.invokeOnExpire(
|
||||||
|
|
@ -697,7 +677,7 @@ internal class DefaultCurrenciesRepository(
|
||||||
skipCache = refresh,
|
skipCache = refresh,
|
||||||
block = {
|
block = {
|
||||||
coroutineScope {
|
coroutineScope {
|
||||||
launch { expressServiceLoader.update(userWallet, tokens) }
|
launch { expressServiceFetcher.fetch(userWallet, tokens) }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -731,6 +711,7 @@ internal class DefaultCurrenciesRepository(
|
||||||
),
|
),
|
||||||
isGroupedByNetwork = false,
|
isGroupedByNetwork = false,
|
||||||
isSortedByBalance = false,
|
isSortedByBalance = false,
|
||||||
|
accountId = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun ensureIsCorrectUserWallet(userWalletId: UserWalletId, isMultiCurrencyWalletExpected: Boolean) {
|
private fun ensureIsCorrectUserWallet(userWalletId: UserWalletId, isMultiCurrencyWalletExpected: Boolean) {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
package com.tangem.data.tokens
|
package com.tangem.data.tokens
|
||||||
|
|
||||||
import arrow.core.left
|
import arrow.core.left
|
||||||
|
import arrow.core.right
|
||||||
import com.tangem.common.test.utils.assertEither
|
import com.tangem.common.test.utils.assertEither
|
||||||
import com.tangem.common.test.utils.assertEitherRight
|
import com.tangem.common.test.utils.assertEitherRight
|
||||||
import com.tangem.data.common.account.WalletAccountsFetcher
|
import com.tangem.data.common.account.WalletAccountsFetcher
|
||||||
|
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
||||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||||
|
import com.tangem.domain.express.ExpressServiceFetcher
|
||||||
import com.tangem.domain.models.wallet.UserWallet
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import com.tangem.domain.models.wallet.isMultiCurrency
|
import com.tangem.domain.models.wallet.isMultiCurrency
|
||||||
|
|
@ -21,11 +24,13 @@ internal class AccountListCryptoCurrenciesFetcherTest {
|
||||||
|
|
||||||
private val userWalletsStore: UserWalletsStore = mockk(relaxUnitFun = true)
|
private val userWalletsStore: UserWalletsStore = mockk(relaxUnitFun = true)
|
||||||
private val walletAccountsFetcher: WalletAccountsFetcher = mockk(relaxUnitFun = true)
|
private val walletAccountsFetcher: WalletAccountsFetcher = mockk(relaxUnitFun = true)
|
||||||
|
private val expressServiceFetcher: ExpressServiceFetcher = mockk()
|
||||||
private val dispatchers = TestingCoroutineDispatcherProvider()
|
private val dispatchers = TestingCoroutineDispatcherProvider()
|
||||||
|
|
||||||
private val fetcher = AccountListCryptoCurrenciesFetcher(
|
private val fetcher = AccountListCryptoCurrenciesFetcher(
|
||||||
userWalletsStore = userWalletsStore,
|
userWalletsStore = userWalletsStore,
|
||||||
walletAccountsFetcher = walletAccountsFetcher,
|
walletAccountsFetcher = walletAccountsFetcher,
|
||||||
|
expressServiceFetcher = expressServiceFetcher,
|
||||||
dispatchers = dispatchers,
|
dispatchers = dispatchers,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -59,8 +64,11 @@ internal class AccountListCryptoCurrenciesFetcherTest {
|
||||||
// Arrange
|
// Arrange
|
||||||
val params = MultiWalletCryptoCurrenciesFetcher.Params(userWalletId = userWalletId)
|
val params = MultiWalletCryptoCurrenciesFetcher.Params(userWalletId = userWalletId)
|
||||||
val mockUserWallet = mockk<UserWallet> { every { isMultiCurrency } returns true }
|
val mockUserWallet = mockk<UserWallet> { every { isMultiCurrency } returns true }
|
||||||
|
val response = mockk<GetWalletAccountsResponse>(relaxed = true)
|
||||||
|
|
||||||
every { userWalletsStore.getSyncStrict(key = params.userWalletId) } returns mockUserWallet
|
every { userWalletsStore.getSyncStrict(key = params.userWalletId) } returns mockUserWallet
|
||||||
|
coEvery { walletAccountsFetcher.fetch(userWalletId = params.userWalletId) } returns response
|
||||||
|
coEvery { expressServiceFetcher.fetch(userWallet = mockUserWallet, assetIds = emptySet()) } returns Unit.right()
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
val actual = fetcher(params)
|
val actual = fetcher(params)
|
||||||
|
|
@ -71,6 +79,7 @@ internal class AccountListCryptoCurrenciesFetcherTest {
|
||||||
coVerify(ordering = Ordering.SEQUENCE) {
|
coVerify(ordering = Ordering.SEQUENCE) {
|
||||||
userWalletsStore.getSyncStrict(key = params.userWalletId)
|
userWalletsStore.getSyncStrict(key = params.userWalletId)
|
||||||
walletAccountsFetcher.fetch(userWalletId = params.userWalletId)
|
walletAccountsFetcher.fetch(userWalletId = params.userWalletId)
|
||||||
|
expressServiceFetcher.fetch(userWallet = mockUserWallet, assetIds = emptySet())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,14 +11,13 @@ import com.tangem.data.common.currency.UserTokensSaver
|
||||||
import com.tangem.data.tokens.utils.CustomTokensMerger
|
import com.tangem.data.tokens.utils.CustomTokensMerger
|
||||||
import com.tangem.datasource.api.common.response.ApiResponse
|
import com.tangem.datasource.api.common.response.ApiResponse
|
||||||
import com.tangem.datasource.api.common.response.ApiResponseError
|
import com.tangem.datasource.api.common.response.ApiResponseError
|
||||||
import com.tangem.datasource.api.express.models.TangemExpressValues.EMPTY_CONTRACT_ADDRESS_VALUE
|
|
||||||
import com.tangem.datasource.api.express.models.request.LeastTokenInfo
|
|
||||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||||
import com.tangem.datasource.exchangeservice.swap.ExpressServiceLoader
|
|
||||||
import com.tangem.datasource.local.token.UserTokensResponseStore
|
import com.tangem.datasource.local.token.UserTokensResponseStore
|
||||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||||
import com.tangem.domain.demo.models.DemoConfig
|
import com.tangem.domain.demo.models.DemoConfig
|
||||||
|
import com.tangem.domain.express.ExpressServiceFetcher
|
||||||
|
import com.tangem.domain.express.models.ExpressAsset
|
||||||
import com.tangem.domain.models.wallet.UserWallet
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import com.tangem.domain.models.wallet.isMultiCurrency
|
import com.tangem.domain.models.wallet.isMultiCurrency
|
||||||
|
|
@ -45,7 +44,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
||||||
private val userTokensResponseStore: UserTokensResponseStore = mockk(relaxUnitFun = true)
|
private val userTokensResponseStore: UserTokensResponseStore = mockk(relaxUnitFun = true)
|
||||||
private val userTokensSaver: UserTokensSaver = mockk(relaxUnitFun = true)
|
private val userTokensSaver: UserTokensSaver = mockk(relaxUnitFun = true)
|
||||||
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory = mockk()
|
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory = mockk()
|
||||||
private val expressServiceLoader: ExpressServiceLoader = mockk(relaxUnitFun = true)
|
private val expressServiceFetcher: ExpressServiceFetcher = mockk(relaxUnitFun = true)
|
||||||
|
|
||||||
private val fetcher = DefaultMultiWalletCryptoCurrenciesFetcher(
|
private val fetcher = DefaultMultiWalletCryptoCurrenciesFetcher(
|
||||||
demoConfig = DemoConfig(),
|
demoConfig = DemoConfig(),
|
||||||
|
|
@ -55,7 +54,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
||||||
userTokensResponseStore = userTokensResponseStore,
|
userTokensResponseStore = userTokensResponseStore,
|
||||||
userTokensSaver = userTokensSaver,
|
userTokensSaver = userTokensSaver,
|
||||||
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
|
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
|
||||||
expressServiceLoader = expressServiceLoader,
|
expressServiceFetcher = expressServiceFetcher,
|
||||||
dispatchers = TestingCoroutineDispatcherProvider(),
|
dispatchers = TestingCoroutineDispatcherProvider(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -67,7 +66,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
||||||
userTokensResponseStore,
|
userTokensResponseStore,
|
||||||
userTokensSaver,
|
userTokensSaver,
|
||||||
cardCryptoCurrencyFactory,
|
cardCryptoCurrencyFactory,
|
||||||
expressServiceLoader,
|
expressServiceFetcher,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -131,6 +130,13 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
||||||
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = userTokensResponse)
|
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = userTokensResponse)
|
||||||
} returns userTokensResponse
|
} returns userTokensResponse
|
||||||
|
|
||||||
|
coEvery {
|
||||||
|
expressServiceFetcher.fetch(
|
||||||
|
userWallet = mockUserWallet,
|
||||||
|
assetIds = userTokensResponse.toAssetId(),
|
||||||
|
)
|
||||||
|
} returns Unit.right()
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
val actual = fetcher(params)
|
val actual = fetcher(params)
|
||||||
|
|
||||||
|
|
@ -144,7 +150,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
||||||
cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyWallet(mockUserWallet)
|
cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyWallet(mockUserWallet)
|
||||||
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = userTokensResponse)
|
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = userTokensResponse)
|
||||||
userTokensSaver.store(userWalletId = params.userWalletId, response = userTokensResponse)
|
userTokensSaver.store(userWalletId = params.userWalletId, response = userTokensResponse)
|
||||||
expressServiceLoader.update(userWallet = mockUserWallet, userTokens = userTokensResponse.toLeastTokens())
|
expressServiceFetcher.fetch(userWallet = mockUserWallet, assetIds = userTokensResponse.toAssetId())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -170,6 +176,13 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
||||||
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = apiResponse.data)
|
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = apiResponse.data)
|
||||||
} returns apiResponse.data
|
} returns apiResponse.data
|
||||||
|
|
||||||
|
coEvery {
|
||||||
|
expressServiceFetcher.fetch(
|
||||||
|
userWallet = mockUserWallet,
|
||||||
|
assetIds = defaultResponse.toAssetId(),
|
||||||
|
)
|
||||||
|
} returns Unit.right()
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
val actual = fetcher(params)
|
val actual = fetcher(params)
|
||||||
|
|
||||||
|
|
@ -183,7 +196,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
||||||
tangemTechApi.getUserTokens(userId = params.userWalletId.stringValue)
|
tangemTechApi.getUserTokens(userId = params.userWalletId.stringValue)
|
||||||
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = apiResponse.data)
|
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = apiResponse.data)
|
||||||
userTokensSaver.store(userWalletId = params.userWalletId, response = apiResponse.data)
|
userTokensSaver.store(userWalletId = params.userWalletId, response = apiResponse.data)
|
||||||
expressServiceLoader.update(userWallet = mockUserWallet, userTokens = defaultResponse.toLeastTokens())
|
expressServiceFetcher.fetch(userWallet = mockUserWallet, assetIds = defaultResponse.toAssetId())
|
||||||
}
|
}
|
||||||
|
|
||||||
coVerify(inverse = true) {
|
coVerify(inverse = true) {
|
||||||
|
|
@ -212,6 +225,13 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
||||||
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = apiResponse.data)
|
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = apiResponse.data)
|
||||||
} returns apiResponse.data
|
} returns apiResponse.data
|
||||||
|
|
||||||
|
coEvery {
|
||||||
|
expressServiceFetcher.fetch(
|
||||||
|
userWallet = mockUserWallet,
|
||||||
|
assetIds = apiResponse.data.toAssetId(),
|
||||||
|
)
|
||||||
|
} returns Unit.right()
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
val actual = fetcher(params)
|
val actual = fetcher(params)
|
||||||
|
|
||||||
|
|
@ -224,7 +244,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
||||||
tangemTechApi.getUserTokens(userId = params.userWalletId.stringValue)
|
tangemTechApi.getUserTokens(userId = params.userWalletId.stringValue)
|
||||||
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = apiResponse.data)
|
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = apiResponse.data)
|
||||||
userTokensSaver.store(userWalletId = params.userWalletId, response = apiResponse.data)
|
userTokensSaver.store(userWalletId = params.userWalletId, response = apiResponse.data)
|
||||||
expressServiceLoader.update(userWallet = mockUserWallet, userTokens = defaultResponse.toLeastTokens())
|
expressServiceFetcher.fetch(userWallet = mockUserWallet, assetIds = defaultResponse.toAssetId())
|
||||||
}
|
}
|
||||||
|
|
||||||
coVerify(inverse = true) {
|
coVerify(inverse = true) {
|
||||||
|
|
@ -273,6 +293,13 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
||||||
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = userTokensResponse)
|
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = userTokensResponse)
|
||||||
} returns userTokensResponse
|
} returns userTokensResponse
|
||||||
|
|
||||||
|
coEvery {
|
||||||
|
expressServiceFetcher.fetch(
|
||||||
|
userWallet = mockUserWallet,
|
||||||
|
assetIds = userTokensResponse.toAssetId(),
|
||||||
|
)
|
||||||
|
} returns Unit.right()
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
val actual = fetcher(params)
|
val actual = fetcher(params)
|
||||||
|
|
||||||
|
|
@ -286,7 +313,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
||||||
userTokensResponseStore.getSyncOrNull(userWalletId = userWalletId)
|
userTokensResponseStore.getSyncOrNull(userWalletId = userWalletId)
|
||||||
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = userTokensResponse)
|
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = userTokensResponse)
|
||||||
userTokensSaver.store(userWalletId = params.userWalletId, response = userTokensResponse)
|
userTokensSaver.store(userWalletId = params.userWalletId, response = userTokensResponse)
|
||||||
expressServiceLoader.update(userWallet = mockUserWallet, userTokens = userTokensResponse.toLeastTokens())
|
expressServiceFetcher.fetch(userWallet = mockUserWallet, assetIds = userTokensResponse.toAssetId())
|
||||||
}
|
}
|
||||||
|
|
||||||
coVerify(inverse = true) {
|
coVerify(inverse = true) {
|
||||||
|
|
@ -317,6 +344,13 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
||||||
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = defaultResponse)
|
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = defaultResponse)
|
||||||
} returns defaultResponse
|
} returns defaultResponse
|
||||||
|
|
||||||
|
coEvery {
|
||||||
|
expressServiceFetcher.fetch(
|
||||||
|
userWallet = mockUserWallet,
|
||||||
|
assetIds = defaultResponse.toAssetId(),
|
||||||
|
)
|
||||||
|
} returns Unit.right()
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
val actual = fetcher(params)
|
val actual = fetcher(params)
|
||||||
|
|
||||||
|
|
@ -330,7 +364,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
||||||
userTokensResponseStore.getSyncOrNull(userWalletId = userWalletId)
|
userTokensResponseStore.getSyncOrNull(userWalletId = userWalletId)
|
||||||
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = defaultResponse)
|
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = defaultResponse)
|
||||||
userTokensSaver.store(userWalletId = params.userWalletId, response = defaultResponse)
|
userTokensSaver.store(userWalletId = params.userWalletId, response = defaultResponse)
|
||||||
expressServiceLoader.update(userWallet = mockUserWallet, userTokens = defaultResponse.toLeastTokens())
|
expressServiceFetcher.fetch(userWallet = mockUserWallet, assetIds = defaultResponse.toAssetId())
|
||||||
}
|
}
|
||||||
|
|
||||||
coVerify(inverse = true) {
|
coVerify(inverse = true) {
|
||||||
|
|
@ -383,6 +417,13 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
||||||
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = userTokensResponse)
|
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = userTokensResponse)
|
||||||
} returns userTokensResponse
|
} returns userTokensResponse
|
||||||
|
|
||||||
|
coEvery {
|
||||||
|
expressServiceFetcher.fetch(
|
||||||
|
userWallet = mockUserWallet,
|
||||||
|
assetIds = userTokensResponse.toAssetId(),
|
||||||
|
)
|
||||||
|
} returns Unit.right()
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
val actual = fetcher(params)
|
val actual = fetcher(params)
|
||||||
|
|
||||||
|
|
@ -398,7 +439,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
||||||
userTokensSaver.push(userWalletId = params.userWalletId, response = userTokensResponse)
|
userTokensSaver.push(userWalletId = params.userWalletId, response = userTokensResponse)
|
||||||
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = userTokensResponse)
|
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = userTokensResponse)
|
||||||
userTokensSaver.store(userWalletId = params.userWalletId, response = userTokensResponse)
|
userTokensSaver.store(userWalletId = params.userWalletId, response = userTokensResponse)
|
||||||
expressServiceLoader.update(userWallet = mockUserWallet, userTokens = userTokensResponse.toLeastTokens())
|
expressServiceFetcher.fetch(userWallet = mockUserWallet, assetIds = userTokensResponse.toAssetId())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -429,6 +470,13 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
||||||
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = defaultResponse)
|
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = defaultResponse)
|
||||||
} returns defaultResponse
|
} returns defaultResponse
|
||||||
|
|
||||||
|
coEvery {
|
||||||
|
expressServiceFetcher.fetch(
|
||||||
|
userWallet = mockUserWallet,
|
||||||
|
assetIds = defaultResponse.toAssetId(),
|
||||||
|
)
|
||||||
|
} returns Unit.right()
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
val actual = fetcher(params)
|
val actual = fetcher(params)
|
||||||
|
|
||||||
|
|
@ -443,7 +491,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
||||||
userTokensSaver.push(userWalletId = params.userWalletId, response = defaultResponse)
|
userTokensSaver.push(userWalletId = params.userWalletId, response = defaultResponse)
|
||||||
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = defaultResponse)
|
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = defaultResponse)
|
||||||
userTokensSaver.store(userWalletId = params.userWalletId, response = defaultResponse)
|
userTokensSaver.store(userWalletId = params.userWalletId, response = defaultResponse)
|
||||||
expressServiceLoader.update(userWallet = mockUserWallet, userTokens = defaultResponse.toLeastTokens())
|
expressServiceFetcher.fetch(userWallet = mockUserWallet, assetIds = defaultResponse.toAssetId())
|
||||||
}
|
}
|
||||||
|
|
||||||
coVerify(inverse = true) {
|
coVerify(inverse = true) {
|
||||||
|
|
@ -471,11 +519,11 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
fun UserTokensResponse.toLeastTokens(): List<LeastTokenInfo> {
|
fun UserTokensResponse.toAssetId(): Set<ExpressAsset.ID> {
|
||||||
return tokens.map { token ->
|
return tokens.mapTo(hashSetOf()) { token ->
|
||||||
LeastTokenInfo(
|
ExpressAsset.ID(
|
||||||
contractAddress = token.contractAddress ?: EMPTY_CONTRACT_ADDRESS_VALUE,
|
networkId = token.networkId,
|
||||||
network = token.networkId,
|
contractAddress = token.contractAddress,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
package com.tangem.data.pay.repository
|
package com.tangem.data.pay.repository
|
||||||
|
|
||||||
|
import com.squareup.moshi.Moshi
|
||||||
import com.tangem.data.common.cache.CacheRegistry
|
import com.tangem.data.common.cache.CacheRegistry
|
||||||
import com.tangem.data.visa.utils.TangemPayTxHistoryItemConverter
|
import com.tangem.data.visa.utils.TangemPayTxHistoryItemConverter
|
||||||
import com.tangem.datasource.api.pay.TangemPayApi
|
import com.tangem.datasource.api.pay.TangemPayApi
|
||||||
|
import com.tangem.datasource.di.NetworkMoshi
|
||||||
import com.tangem.datasource.local.visa.TangemPayTxHistoryItemsStore
|
import com.tangem.datasource.local.visa.TangemPayTxHistoryItemsStore
|
||||||
import com.tangem.domain.tangempay.model.TangemPayTxHistoryListBatchFlow
|
import com.tangem.domain.tangempay.model.TangemPayTxHistoryListBatchFlow
|
||||||
import com.tangem.domain.tangempay.model.TangemPayTxHistoryListBatchingContext
|
import com.tangem.domain.tangempay.model.TangemPayTxHistoryListBatchingContext
|
||||||
|
|
@ -26,8 +28,11 @@ internal class DefaultTangemPayTxHistoryRepository @Inject constructor(
|
||||||
private val cacheRegistry: CacheRegistry,
|
private val cacheRegistry: CacheRegistry,
|
||||||
private val txHistoryItemsStore: TangemPayTxHistoryItemsStore,
|
private val txHistoryItemsStore: TangemPayTxHistoryItemsStore,
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
|
@NetworkMoshi private val moshi: Moshi,
|
||||||
) : TangemPayTxHistoryRepository {
|
) : TangemPayTxHistoryRepository {
|
||||||
|
|
||||||
|
private val txHistoryItemConverter by lazy { TangemPayTxHistoryItemConverter(moshi) }
|
||||||
|
|
||||||
override fun getTxHistoryBatchFlow(
|
override fun getTxHistoryBatchFlow(
|
||||||
batchSize: Int,
|
batchSize: Int,
|
||||||
context: TangemPayTxHistoryListBatchingContext,
|
context: TangemPayTxHistoryListBatchingContext,
|
||||||
|
|
@ -84,7 +89,7 @@ internal class DefaultTangemPayTxHistoryRepository @Inject constructor(
|
||||||
val result = requestPerformer.request { authHeader ->
|
val result = requestPerformer.request { authHeader ->
|
||||||
visaApi.getTangemPayTxHistory(authHeader = authHeader, limit = pageSize, cursor = cursor)
|
visaApi.getTangemPayTxHistory(authHeader = authHeader, limit = pageSize, cursor = cursor)
|
||||||
}.result
|
}.result
|
||||||
val items = TangemPayTxHistoryItemConverter.convertList(result.transactions).filterNotNull()
|
val items = txHistoryItemConverter.convertList(result.transactions).filterNotNull()
|
||||||
txHistoryItemsStore.store(key = customerWalletAddress, cursor = cursor ?: INITIAL_CURSOR, value = items)
|
txHistoryItemsStore.store(key = customerWalletAddress, cursor = cursor ?: INITIAL_CURSOR, value = items)
|
||||||
}.onLeft { error(it.toString()) }
|
}.onLeft { error(it.toString()) }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,19 @@
|
||||||
package com.tangem.data.visa.utils
|
package com.tangem.data.visa.utils
|
||||||
|
|
||||||
|
import com.squareup.moshi.Moshi
|
||||||
import com.tangem.datasource.api.pay.models.response.TangemPayTxHistoryResponse
|
import com.tangem.datasource.api.pay.models.response.TangemPayTxHistoryResponse
|
||||||
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
|
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
|
||||||
import com.tangem.utils.converter.Converter
|
import com.tangem.utils.converter.Converter
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.Currency
|
import java.util.Currency
|
||||||
|
|
||||||
internal object TangemPayTxHistoryItemConverter :
|
internal class TangemPayTxHistoryItemConverter(moshi: Moshi) :
|
||||||
Converter<TangemPayTxHistoryResponse.Transaction, TangemPayTxHistoryItem?> {
|
Converter<TangemPayTxHistoryResponse.Transaction, TangemPayTxHistoryItem?> {
|
||||||
|
|
||||||
|
private val spendAdapter = moshi.adapter(TangemPayTxHistoryResponse.Spend::class.java)
|
||||||
|
private val paymentAdapter = moshi.adapter(TangemPayTxHistoryResponse.Payment::class.java)
|
||||||
|
private val feeAdapter = moshi.adapter(TangemPayTxHistoryResponse.Fee::class.java)
|
||||||
|
|
||||||
override fun convert(value: TangemPayTxHistoryResponse.Transaction): TangemPayTxHistoryItem? {
|
override fun convert(value: TangemPayTxHistoryResponse.Transaction): TangemPayTxHistoryItem? {
|
||||||
return value.spend?.let { convertSpend(id = value.id, spend = it) }
|
return value.spend?.let { convertSpend(id = value.id, spend = it) }
|
||||||
?: value.payment?.let { convertPayment(id = value.id, payment = it) }
|
?: value.payment?.let { convertPayment(id = value.id, payment = it) }
|
||||||
|
|
@ -22,6 +27,7 @@ internal object TangemPayTxHistoryItemConverter :
|
||||||
private fun convertSpend(id: String, spend: TangemPayTxHistoryResponse.Spend): TangemPayTxHistoryItem.Spend {
|
private fun convertSpend(id: String, spend: TangemPayTxHistoryResponse.Spend): TangemPayTxHistoryItem.Spend {
|
||||||
return TangemPayTxHistoryItem.Spend(
|
return TangemPayTxHistoryItem.Spend(
|
||||||
id = id,
|
id = id,
|
||||||
|
jsonRepresentation = spendAdapter.toJson(spend),
|
||||||
// If postedAt is null, it means transaction wasn't posted and was likely declined. Use authorizedAt
|
// If postedAt is null, it means transaction wasn't posted and was likely declined. Use authorizedAt
|
||||||
date = spend.postedAt ?: spend.authorizedAt,
|
date = spend.postedAt ?: spend.authorizedAt,
|
||||||
amount = spend.amount,
|
amount = spend.amount,
|
||||||
|
|
@ -41,15 +47,18 @@ internal object TangemPayTxHistoryItemConverter :
|
||||||
): TangemPayTxHistoryItem.Payment {
|
): TangemPayTxHistoryItem.Payment {
|
||||||
return TangemPayTxHistoryItem.Payment(
|
return TangemPayTxHistoryItem.Payment(
|
||||||
id = id,
|
id = id,
|
||||||
|
jsonRepresentation = paymentAdapter.toJson(payment),
|
||||||
date = payment.postedAt,
|
date = payment.postedAt,
|
||||||
currency = Currency.getInstance(payment.currency),
|
currency = Currency.getInstance(payment.currency),
|
||||||
amount = payment.amount,
|
amount = payment.amount,
|
||||||
|
transactionHash = payment.transactionHash,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun convertFee(id: String, fee: TangemPayTxHistoryResponse.Fee): TangemPayTxHistoryItem.Fee {
|
private fun convertFee(id: String, fee: TangemPayTxHistoryResponse.Fee): TangemPayTxHistoryItem.Fee {
|
||||||
return TangemPayTxHistoryItem.Fee(
|
return TangemPayTxHistoryItem.Fee(
|
||||||
id = id,
|
id = id,
|
||||||
|
jsonRepresentation = feeAdapter.toJson(fee),
|
||||||
date = fee.postedAt,
|
date = fee.postedAt,
|
||||||
currency = Currency.getInstance(fee.currency),
|
currency = Currency.getInstance(fee.currency),
|
||||||
amount = fee.amount,
|
amount = fee.amount,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.tangem.domain.account.producer
|
||||||
|
|
||||||
|
import com.tangem.domain.core.flow.FlowProducer
|
||||||
|
import com.tangem.domain.models.account.Account
|
||||||
|
import com.tangem.domain.models.account.AccountId
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produces a flow of [Account.CryptoPortfolio] for a single account identified by [Params.accountId].
|
||||||
|
* The flow emits updates whenever the account's portfolio changes.
|
||||||
|
*/
|
||||||
|
interface SingleAccountProducer : FlowProducer<Account.CryptoPortfolio> {
|
||||||
|
|
||||||
|
data class Params(val accountId: AccountId)
|
||||||
|
|
||||||
|
interface Factory : FlowProducer.Factory<Params, SingleAccountProducer>
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.tangem.domain.account.supplier
|
||||||
|
|
||||||
|
import com.tangem.domain.account.producer.SingleAccountProducer
|
||||||
|
import com.tangem.domain.core.flow.FlowCachingSupplier
|
||||||
|
import com.tangem.domain.models.account.Account
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supplies instances of [SingleAccountProducer] that produce flows of [Account.CryptoPortfolio]
|
||||||
|
* for individual accounts. Each producer is uniquely identified by its [SingleAccountProducer.Params].
|
||||||
|
*
|
||||||
|
* @property factory A factory to create instances of [SingleAccountProducer].
|
||||||
|
* @property keyCreator A function that generates a unique key for caching based on [SingleAccountProducer.Params].
|
||||||
|
*/
|
||||||
|
abstract class SingleAccountSupplier(
|
||||||
|
override val factory: SingleAccountProducer.Factory,
|
||||||
|
override val keyCreator: (SingleAccountProducer.Params) -> String,
|
||||||
|
) : FlowCachingSupplier<SingleAccountProducer, SingleAccountProducer.Params, Account.CryptoPortfolio>()
|
||||||
|
|
@ -19,6 +19,7 @@ dependencies {
|
||||||
api(projects.domain.account)
|
api(projects.domain.account)
|
||||||
api(projects.domain.core)
|
api(projects.domain.core)
|
||||||
api(projects.domain.common)
|
api(projects.domain.common)
|
||||||
|
api(projects.domain.express)
|
||||||
api(projects.domain.quotes)
|
api(projects.domain.quotes)
|
||||||
api(projects.domain.models)
|
api(projects.domain.models)
|
||||||
api(projects.domain.networks)
|
api(projects.domain.networks)
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,11 @@ import com.tangem.domain.account.repository.AccountsCRUDRepository
|
||||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||||
import com.tangem.domain.account.status.usecase.GetAccountCurrencyByAddressUseCase
|
import com.tangem.domain.account.status.usecase.GetAccountCurrencyByAddressUseCase
|
||||||
import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase
|
import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase
|
||||||
import com.tangem.domain.account.status.usecase.SaveCryptoCurrenciesUseCase
|
import com.tangem.domain.account.status.usecase.GetCryptoCurrencyActionsUseCaseV2
|
||||||
|
import com.tangem.domain.account.status.usecase.ManageCryptoCurrenciesUseCase
|
||||||
import com.tangem.domain.account.supplier.SingleAccountListSupplier
|
import com.tangem.domain.account.supplier.SingleAccountListSupplier
|
||||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||||
|
import com.tangem.domain.express.ExpressServiceFetcher
|
||||||
import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher
|
import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher
|
||||||
import com.tangem.domain.networks.multi.MultiNetworkStatusSupplier
|
import com.tangem.domain.networks.multi.MultiNetworkStatusSupplier
|
||||||
import com.tangem.domain.networks.utils.NetworksCleaner
|
import com.tangem.domain.networks.utils.NetworksCleaner
|
||||||
|
|
@ -14,6 +16,7 @@ import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher
|
||||||
import com.tangem.domain.staking.StakingIdFactory
|
import com.tangem.domain.staking.StakingIdFactory
|
||||||
import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
|
import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
|
||||||
import com.tangem.domain.staking.utils.StakingCleaner
|
import com.tangem.domain.staking.utils.StakingCleaner
|
||||||
|
import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
|
||||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||||
import com.tangem.domain.wallets.derivations.DerivationsRepository
|
import com.tangem.domain.wallets.derivations.DerivationsRepository
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
|
|
@ -21,6 +24,8 @@ import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
import dagger.hilt.components.SingletonComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
|
|
@ -41,6 +46,20 @@ internal object AccountStatusUseCaseModule {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideGetCryptoCurrencyActionsUseCaseV2(
|
||||||
|
accountsCRUDRepository: AccountsCRUDRepository,
|
||||||
|
singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
|
||||||
|
getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCase,
|
||||||
|
): GetCryptoCurrencyActionsUseCaseV2 {
|
||||||
|
return GetCryptoCurrencyActionsUseCaseV2(
|
||||||
|
accountsCRUDRepository = accountsCRUDRepository,
|
||||||
|
singleAccountStatusListSupplier = singleAccountStatusListSupplier,
|
||||||
|
getCryptoCurrencyActionsUseCase = getCryptoCurrencyActionsUseCase,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideGetAccountCurrencyStatusUseCase(
|
fun provideGetAccountCurrencyStatusUseCase(
|
||||||
|
|
@ -62,9 +81,10 @@ internal object AccountStatusUseCaseModule {
|
||||||
stakingIdFactory: StakingIdFactory,
|
stakingIdFactory: StakingIdFactory,
|
||||||
networksCleaner: NetworksCleaner,
|
networksCleaner: NetworksCleaner,
|
||||||
stakingCleaner: StakingCleaner,
|
stakingCleaner: StakingCleaner,
|
||||||
|
expressServiceFetcher: ExpressServiceFetcher,
|
||||||
dispatchers: CoroutineDispatcherProvider,
|
dispatchers: CoroutineDispatcherProvider,
|
||||||
): SaveCryptoCurrenciesUseCase {
|
): ManageCryptoCurrenciesUseCase {
|
||||||
return SaveCryptoCurrenciesUseCase(
|
return ManageCryptoCurrenciesUseCase(
|
||||||
singleAccountListSupplier = singleAccountListSupplier,
|
singleAccountListSupplier = singleAccountListSupplier,
|
||||||
accountsCRUDRepository = accountsCRUDRepository,
|
accountsCRUDRepository = accountsCRUDRepository,
|
||||||
currenciesRepository = currenciesRepository,
|
currenciesRepository = currenciesRepository,
|
||||||
|
|
@ -75,6 +95,8 @@ internal object AccountStatusUseCaseModule {
|
||||||
stakingIdFactory = stakingIdFactory,
|
stakingIdFactory = stakingIdFactory,
|
||||||
networksCleaner = networksCleaner,
|
networksCleaner = networksCleaner,
|
||||||
stakingCleaner = stakingCleaner,
|
stakingCleaner = stakingCleaner,
|
||||||
|
expressServiceFetcher = expressServiceFetcher,
|
||||||
|
parallelUpdatingScope = CoroutineScope(SupervisorJob() + dispatchers.default),
|
||||||
dispatchers = dispatchers,
|
dispatchers = dispatchers,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue