Updated on 2026-08-14
This commit is contained in:
commit
6b9fc4a3ce
1058 changed files with 48197 additions and 12718 deletions
|
|
@ -24,8 +24,6 @@ import com.tangem.datasource.local.preferences.AppPreferencesStore
|
|||
import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||
import com.tangem.datasource.local.walletmanager.WalletManagersStore
|
||||
import com.tangem.datasource.utils.WireMockRedirectInterceptor
|
||||
import com.tangem.domain.promo.PromoRepository
|
||||
import com.tangem.domain.promo.models.PromoId
|
||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
|
||||
import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION
|
||||
import com.tangem.tap.MainActivity
|
||||
|
|
@ -59,9 +57,6 @@ abstract class BaseTestCase : TestCase(
|
|||
@Inject
|
||||
lateinit var appPreferencesStore: AppPreferencesStore
|
||||
|
||||
@Inject
|
||||
lateinit var promoRepository: PromoRepository
|
||||
|
||||
@Inject
|
||||
lateinit var walletManagersStore: WalletManagersStore
|
||||
|
||||
|
|
@ -136,7 +131,6 @@ abstract class BaseTestCase : TestCase(
|
|||
value = false
|
||||
)
|
||||
}
|
||||
promoRepository.setNeverToShowWalletPromo(PromoId.Sepa)
|
||||
}
|
||||
apiEnvironmentRule.setup(apiConfigsManager)
|
||||
ActivityScenario.launch(MainActivity::class.java)
|
||||
|
|
@ -189,6 +183,9 @@ abstract class BaseTestCase : TestCase(
|
|||
"GASLESS_APPROVAL_ENABLED" to true,
|
||||
"MAIN_SCREEN_QR_SCANNING_ENABLED" to true,
|
||||
"ADD_AND_MANAGE_TOKENS_ENABLED" to true,
|
||||
"VISA_ONBOARDING_ENABLED" to true,
|
||||
"AND_15101_TANGEM_PAY_HOT_WALLET_ONBOARDING" to true,
|
||||
"AND_15310_ADD_FUNDS_STAGE1" to true,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ object TestConstants {
|
|||
const val TERRA_RECIPIENT_ADDRESS = "terra148dmp5ccazcwdmrcpvqz5rprnn886kemqen3tj"
|
||||
const val POLYGON_RECIPIENT_ADDRESS = "0x742d35cc6634c0532925a3b844bc9e7595f2bd18"
|
||||
|
||||
const val WAIT_UNTIL_TIMEOUT_SHORT = 5_000L
|
||||
const val WAIT_UNTIL_TIMEOUT = 20_000L
|
||||
const val WAIT_UNTIL_TIMEOUT_LONG = 30_000L
|
||||
const val WAIT_UNTIL_TIMEOUT_VERY_LONG = 60_000L
|
||||
|
|
@ -58,4 +59,7 @@ object TestConstants {
|
|||
const val SEED_PHRASE_24 = "force visit fresh brown razor target ill scissors figure cave feel genre cargo category " +
|
||||
"bread much nature basic fun iron benefit egg error prosper"
|
||||
const val SVS_SEED_PHRASE_12 = "diagram thunder merit soup muscle amused refuse usual ring couch popular wash"
|
||||
|
||||
const val TANGEM_PAY_ELIGIBILITY_SCENARIO = "tangem_pay_eligibility"
|
||||
const val TANGEM_PAY_ACCESS_CODE = "517384"
|
||||
}
|
||||
|
|
@ -3,6 +3,9 @@ package com.tangem.common.extensions
|
|||
import androidx.compose.ui.semantics.SemanticsNode
|
||||
import androidx.compose.ui.semantics.SemanticsProperties
|
||||
import androidx.compose.ui.test.SemanticsMatcher
|
||||
import androidx.compose.ui.test.onAllNodesWithText
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT
|
||||
import com.tangem.common.utils.LazyListItemNode
|
||||
import com.tangem.core.ui.components.buttons.actions.HasBadgeKey
|
||||
import com.tangem.core.ui.components.buttons.actions.IsDimmedKey
|
||||
|
|
@ -10,6 +13,15 @@ import io.github.kakaocup.compose.node.element.KNode
|
|||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
|
||||
fun BaseTestCase.assertSnackbarWithText(text: String, timeoutMs: Long = WAIT_UNTIL_TIMEOUT) {
|
||||
composeTestRule.waitUntil(timeoutMillis = timeoutMs) {
|
||||
composeTestRule
|
||||
.onAllNodesWithText(text, substring = true)
|
||||
.fetchSemanticsNodes()
|
||||
.isNotEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
fun assertElementDoesNotExist(
|
||||
elementProvider: () -> KNode,
|
||||
elementDescription: String,
|
||||
|
|
|
|||
|
|
@ -1,19 +1,40 @@
|
|||
package com.tangem.common.utils
|
||||
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import org.json.JSONObject
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
// WC URIs embed a session symKey that lets anyone join/hijack the session — strip it before logging.
|
||||
private val WC_SECRET_REGEX = Regex("(symKey(?:=|%3D))[^&\\s\"']+", RegexOption.IGNORE_CASE)
|
||||
|
||||
private fun redactWcSecrets(text: String): String =
|
||||
WC_SECRET_REGEX.replace(text) { "${it.groupValues[1]}<redacted>" }
|
||||
|
||||
/**
|
||||
* Requests a WalletConnect URI from the qa-tools service.
|
||||
*
|
||||
* Response shape (see qa-tools `/wc_uri` swagger):
|
||||
* - 200: { success: true, wcUri: "wc:...", network, wallet, tangemDeepLink, timestamp, processingTime }
|
||||
* - 5xx: { error, network, timestamp, errorType }
|
||||
*/
|
||||
fun getWcUri(
|
||||
network: String = "ethereum",
|
||||
dAppUrl: String? = null,
|
||||
dAppName: String? = null,
|
||||
baseUrl: String = "[REDACTED_ENV_URL]"
|
||||
): String? {
|
||||
TangemLogger.i("Getting WC URI for network: $network")
|
||||
val url = "$baseUrl/wc_uri".toHttpUrl().newBuilder()
|
||||
.addQueryParameter("network", network)
|
||||
.apply {
|
||||
if (dAppUrl != null) addQueryParameter("dappUrl", dAppUrl)
|
||||
if (dAppName != null) addQueryParameter("dappName", dAppName)
|
||||
}
|
||||
.build()
|
||||
.toString()
|
||||
TangemLogger.i("getWcUri: requesting $url")
|
||||
|
||||
val client = OkHttpClient.Builder()
|
||||
.connectTimeout(30, TimeUnit.SECONDS)
|
||||
|
|
@ -23,37 +44,94 @@ fun getWcUri(
|
|||
.build()
|
||||
|
||||
val request = Request.Builder()
|
||||
.url("$baseUrl/wc_uri?network=$network")
|
||||
.url(url)
|
||||
.header("Accept", "application/json")
|
||||
.get()
|
||||
.build()
|
||||
|
||||
return try {
|
||||
client.newCall(request).execute().use { response ->
|
||||
TangemLogger.i("Response code: ${response.code}")
|
||||
val body = response.body?.string().orEmpty()
|
||||
val contentType = response.header("Content-Type") ?: "<missing>"
|
||||
TangemLogger.i(
|
||||
"getWcUri: HTTP ${response.code} ${response.message}, " +
|
||||
"Content-Type=$contentType, body.length=${body.length}"
|
||||
)
|
||||
TangemLogger.i("getWcUri: raw body=${redactWcSecrets(body)}")
|
||||
|
||||
if (response.isSuccessful) {
|
||||
val body = response.body?.string() ?: ""
|
||||
TangemLogger.i("Response body: $body")
|
||||
|
||||
val jsonObject = JSONObject(body)
|
||||
|
||||
if (jsonObject.getBoolean("success")) {
|
||||
val wcUri = jsonObject.getString("wcUri")
|
||||
TangemLogger.i("Got WC URI successfully: $wcUri")
|
||||
|
||||
wcUri
|
||||
} else {
|
||||
TangemLogger.e("API returned error: ${jsonObject.optString("error", "Unknown")}")
|
||||
null
|
||||
}
|
||||
} else {
|
||||
val errorBody = response.body?.string() ?: "No error body"
|
||||
TangemLogger.e("Request failed: ${response.code}, body: $errorBody")
|
||||
null
|
||||
if (!response.isSuccessful) {
|
||||
TangemLogger.e("getWcUri: non-2xx response (${response.code}), body=${redactWcSecrets(body)}")
|
||||
return@use null
|
||||
}
|
||||
|
||||
if (body.isBlank()) {
|
||||
TangemLogger.e("getWcUri: response body is empty")
|
||||
return@use null
|
||||
}
|
||||
|
||||
if (contentType.contains("text/html", ignoreCase = true) ||
|
||||
body.trimStart().startsWith("<")
|
||||
) {
|
||||
val server = response.header("Server").orEmpty()
|
||||
val wwwAuth = response.header("WWW-Authenticate").orEmpty()
|
||||
val isCloudflareAccess = server.contains("cloudflare", ignoreCase = true) ||
|
||||
wwwAuth.contains("Cloudflare-Access", ignoreCase = true) ||
|
||||
body.contains("cloudflareaccess.com", ignoreCase = true)
|
||||
if (isCloudflareAccess) {
|
||||
TangemLogger.e(
|
||||
"getWcUri: blocked by Cloudflare Access. The test runner is not " +
|
||||
"authorized to reach $url — connect to the corporate VPN or " +
|
||||
"configure a Cloudflare Access service token (CF-Access-Client-Id / " +
|
||||
"CF-Access-Client-Secret headers) on the device."
|
||||
)
|
||||
} else {
|
||||
TangemLogger.e(
|
||||
"getWcUri: server returned HTML instead of JSON for $url. " +
|
||||
"Verify [REDACTED_ENV_URL] and the current API in /docs."
|
||||
)
|
||||
}
|
||||
return@use null
|
||||
}
|
||||
|
||||
val jsonObject = try {
|
||||
JSONObject(body)
|
||||
} catch (e: Exception) {
|
||||
TangemLogger.e("getWcUri: failed to parse body as JSON: ${redactWcSecrets(body)}", e)
|
||||
return@use null
|
||||
}
|
||||
|
||||
TangemLogger.i("getWcUri: response keys=${jsonObject.keys().asSequence().toList()}")
|
||||
|
||||
val success = jsonObject.optBoolean("success", false)
|
||||
if (!success) {
|
||||
val error = jsonObject.optString("error", "<no error field>")
|
||||
val errorType = jsonObject.optString("errorType", "<no errorType field>")
|
||||
TangemLogger.e(
|
||||
"getWcUri: API success=false, error=$error, errorType=$errorType, body=${redactWcSecrets(body)}"
|
||||
)
|
||||
return@use null
|
||||
}
|
||||
|
||||
val wcUri = jsonObject.optString("wcUri", "")
|
||||
val tangemDeepLink = jsonObject.optString("tangemDeepLink", "")
|
||||
val processingTime = jsonObject.optString("processingTime", "")
|
||||
TangemLogger.i(
|
||||
"getWcUri: parsed wcUri=${redactWcSecrets(wcUri)}, " +
|
||||
"tangemDeepLink=${redactWcSecrets(tangemDeepLink)}, processingTime=$processingTime"
|
||||
)
|
||||
|
||||
if (wcUri.isBlank() || !wcUri.startsWith("wc:")) {
|
||||
TangemLogger.e(
|
||||
"getWcUri: wcUri is missing or has unexpected format. " +
|
||||
"wcUri='${redactWcSecrets(wcUri)}', body=${redactWcSecrets(body)}"
|
||||
)
|
||||
return@use null
|
||||
}
|
||||
|
||||
wcUri
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
TangemLogger.e("Error getting WC URI", e)
|
||||
TangemLogger.e("getWcUri: exception while requesting $url", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,20 @@
|
|||
package com.tangem.scenarios
|
||||
|
||||
import androidx.compose.ui.test.ExperimentalTestApi
|
||||
import androidx.compose.ui.test.hasText
|
||||
import androidx.compose.ui.test.performTextInput
|
||||
import androidx.compose.ui.test.waitUntilAtLeastOneExists
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT_LONG
|
||||
import com.tangem.common.extensions.clickWithAssertion
|
||||
import com.tangem.common.extensions.isDisplayedSafely
|
||||
import com.tangem.core.ui.R as CoreUiR
|
||||
import com.tangem.domain.models.scan.ProductType
|
||||
import com.tangem.screens.*
|
||||
import com.tangem.tap.domain.sdk.mocks.MockContent
|
||||
import com.tangem.tap.domain.sdk.mocks.MockProvider
|
||||
import com.tangem.utils.StringsSigns.DASH_SIGN
|
||||
import io.github.kakaocup.kakao.common.utilities.getResourceString
|
||||
import io.qameta.allure.kotlin.Allure.step
|
||||
|
||||
fun BaseTestCase.scanCard(
|
||||
|
|
@ -56,7 +63,8 @@ fun BaseTestCase.openMainScreen(
|
|||
}
|
||||
}
|
||||
|
||||
fun BaseTestCase.openMainScreenWithExistingHotWallet(seedPhrase: String) {
|
||||
@OptIn(ExperimentalTestApi::class)
|
||||
fun BaseTestCase.openMainScreenWithExistingHotWallet(seedPhrase: String, accessCode: String = "") {
|
||||
step("Click on 'Get started' button") {
|
||||
onStoriesScreen { getStartedButton.clickWithAssertion() }
|
||||
}
|
||||
|
|
@ -84,11 +92,39 @@ fun BaseTestCase.openMainScreenWithExistingHotWallet(seedPhrase: String) {
|
|||
continueButton.performClick()
|
||||
}
|
||||
}
|
||||
step("Click on 'Skip' button") {
|
||||
onImportWalletScreen { skipButton.performClick() }
|
||||
}
|
||||
step("Click on 'Skip anyway' dialog button") {
|
||||
onDialog { skipAnywayButton.performClick() }
|
||||
if (accessCode.isNotEmpty()) {
|
||||
step("Enter access code '$accessCode' (create)") {
|
||||
onHotWalletAccessCodeScreen {
|
||||
accessCodeInput.performClick()
|
||||
accessCodeInput.performTextInput(accessCode)
|
||||
}
|
||||
}
|
||||
step("Re-enter access code '$accessCode' (confirm)") {
|
||||
// Create+confirm screens share ACCESS_CODE_INPUT — gate on confirm-screen title.
|
||||
composeTestRule.waitUntilAtLeastOneExists(
|
||||
hasText(getResourceString(CoreUiR.string.access_code_confirm_title)),
|
||||
timeoutMillis = WAIT_UNTIL_TIMEOUT_LONG,
|
||||
)
|
||||
onHotWalletAccessCodeScreen {
|
||||
accessCodeInput.performClick()
|
||||
accessCodeInput.performTextInput(accessCode)
|
||||
}
|
||||
}
|
||||
step("Dismiss biometry prompt if shown") {
|
||||
waitForIdle()
|
||||
onBiometryDialog {
|
||||
if (dontAllowButton.isDisplayedSafely()) {
|
||||
dontAllowButton.performClick()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
step("Click on 'Skip' button") {
|
||||
onImportWalletScreen { skipButton.performClick() }
|
||||
}
|
||||
step("Click on 'Skip anyway' dialog button") {
|
||||
onDialog { skipAnywayButton.performClick() }
|
||||
}
|
||||
}
|
||||
step("Click on 'Finish' button") {
|
||||
onImportWalletScreen {
|
||||
|
|
|
|||
|
|
@ -97,8 +97,8 @@ fun BaseTestCase.checkMultiCurrencyMainScreen(
|
|||
step("Assert devices count equal to '$devicesCount'") {
|
||||
onMainScreen { walletDevicesCount.assertTextContains(devicesCount) }
|
||||
}
|
||||
step("Assert 'Buy' button is displayed") {
|
||||
onMainScreen { buyButton.assertIsDisplayed() }
|
||||
step("Assert 'Add funds' button is displayed") {
|
||||
onMainScreen { addFundsButton.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert 'Swap' button is displayed") {
|
||||
onMainScreen { swapButton.assertIsDisplayed() }
|
||||
|
|
@ -119,8 +119,8 @@ fun BaseTestCase.checkMultiCurrencyMainScreen(
|
|||
|
||||
fun BaseTestCase.assertActionButtonsForMultiCurrencyWallet(isEnabled: Boolean = true) {
|
||||
if (isEnabled) {
|
||||
step("Assert 'Buy' button is enabled") {
|
||||
onMainScreen { buyButton.assertIsEnabled() }
|
||||
step("Assert 'Add funds' button is enabled") {
|
||||
onMainScreen { addFundsButton.assertIsEnabled() }
|
||||
}
|
||||
step("Assert 'Swap' button is enabled") {
|
||||
onMainScreen { swapButton.assertIsEnabled() }
|
||||
|
|
@ -129,8 +129,8 @@ fun BaseTestCase.assertActionButtonsForMultiCurrencyWallet(isEnabled: Boolean =
|
|||
onMainScreen { sellButton.assertIsEnabled() }
|
||||
}
|
||||
} else {
|
||||
step("Assert 'Buy' button is not enabled") {
|
||||
onMainScreen { buyButton.assertIsNotEnabled() }
|
||||
step("Assert 'Add funds' button is not enabled") {
|
||||
onMainScreen { addFundsButton.assertIsNotEnabled() }
|
||||
}
|
||||
step("Assert 'Swap' button is not enabled") {
|
||||
onMainScreen { swapButton.assertIsNotEnabled() }
|
||||
|
|
|
|||
|
|
@ -7,9 +7,10 @@ import androidx.test.core.app.ApplicationProvider
|
|||
import io.github.kakaocup.kakao.intent.KIntent
|
||||
|
||||
fun openAppByDeepLink(deepLinkUri: String?) {
|
||||
val deeplinkScheme = "tangem://wc?uri="
|
||||
requireNotNull(deepLinkUri) { "openAppByDeepLink: deepLinkUri is null" }
|
||||
val finalUri = Uri.parse(deepLinkUri)
|
||||
val context = ApplicationProvider.getApplicationContext<android.content.Context>()
|
||||
val intent = Intent(ACTION_VIEW, Uri.parse(deeplinkScheme + deepLinkUri)).apply {
|
||||
val intent = Intent(ACTION_VIEW, finalUri).apply {
|
||||
addFlags(FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,20 @@
|
|||
package com.tangem.scenarios
|
||||
|
||||
import androidx.compose.ui.test.click
|
||||
import androidx.compose.ui.test.hasTestTag
|
||||
import androidx.compose.ui.test.hasText
|
||||
import androidx.compose.ui.test.longClick
|
||||
import androidx.compose.ui.test.performTextInput
|
||||
import androidx.compose.ui.test.performTouchInput
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.common.constants.TestConstants.HOLD_DURATION_MS
|
||||
import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT_LONG
|
||||
import com.tangem.common.extensions.assertVisibility
|
||||
import com.tangem.common.extensions.clickWithAssertion
|
||||
import com.tangem.common.extensions.isDisplayedSafely
|
||||
import com.tangem.core.ui.R as CoreUiR
|
||||
import com.tangem.core.ui.test.BaseButtonTestTags
|
||||
import com.tangem.core.ui.test.HotWalletAccessCodeTestTags
|
||||
import com.tangem.screens.*
|
||||
import io.github.kakaocup.kakao.common.utilities.getResourceString
|
||||
import io.qameta.allure.kotlin.Allure.step
|
||||
|
|
@ -283,6 +293,29 @@ fun BaseTestCase.chooseReceiveToken(tokenName: String) {
|
|||
}
|
||||
}
|
||||
|
||||
/** Holds the last BASE_BUTTON; enters [accessCode] if a hot wallet prompts for it. */
|
||||
fun BaseTestCase.confirmSwapByHolding(accessCode: String? = null) {
|
||||
val buttonMatcher = hasTestTag(BaseButtonTestTags.BUTTON)
|
||||
val buttons = composeTestRule.onAllNodes(buttonMatcher)
|
||||
// HoldToConfirm is always last — withdraw renders an extra BASE_BUTTON for notifications.
|
||||
val swapButton = buttons[buttons.fetchSemanticsNodes().lastIndex]
|
||||
swapButton.performTouchInput { longClick(durationMillis = HOLD_DURATION_MS) }
|
||||
waitForIdle()
|
||||
val accessCodeInput = hasTestTag(HotWalletAccessCodeTestTags.ACCESS_CODE_INPUT)
|
||||
val swapInProgressText = hasText(getResourceString(CoreUiR.string.swap_in_progress))
|
||||
composeTestRule.waitUntil(timeoutMillis = WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
composeTestRule.onAllNodes(accessCodeInput).fetchSemanticsNodes().isNotEmpty() ||
|
||||
composeTestRule.onAllNodes(swapInProgressText, useUnmergedTree = true)
|
||||
.fetchSemanticsNodes().isNotEmpty()
|
||||
}
|
||||
val needsAccessCode =
|
||||
composeTestRule.onAllNodes(accessCodeInput).fetchSemanticsNodes().isNotEmpty()
|
||||
if (needsAccessCode && accessCode != null) {
|
||||
composeTestRule.onNode(accessCodeInput).performTextInput(accessCode)
|
||||
waitForIdle()
|
||||
}
|
||||
}
|
||||
|
||||
sealed class SwapEntryPoint {
|
||||
object MainScreen : SwapEntryPoint()
|
||||
object TokenDetails : SwapEntryPoint()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
package com.tangem.scenarios
|
||||
|
||||
import androidx.compose.ui.test.hasTestTag
|
||||
import androidx.compose.ui.test.performTouchInput
|
||||
import androidx.compose.ui.test.swipeDown
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.common.constants.TestConstants.SVS_SEED_PHRASE_12
|
||||
import com.tangem.common.constants.TestConstants.TANGEM_PAY_ACCESS_CODE
|
||||
import com.tangem.common.extensions.clickWithAssertion
|
||||
import com.tangem.core.ui.test.TangemPayTestTags
|
||||
import com.tangem.screens.tangempay.*
|
||||
import io.qameta.allure.kotlin.Allure.step
|
||||
|
||||
fun BaseTestCase.openTangemPay() {
|
||||
step("Import hot wallet from Tangem Pay seed phrase (with access code)") {
|
||||
openMainScreenWithExistingHotWallet(SVS_SEED_PHRASE_12, accessCode = TANGEM_PAY_ACCESS_CODE)
|
||||
}
|
||||
step("Click on Tangem Pay tile") {
|
||||
onTangemPayMainScreen { mainScreenTile.clickWithAssertion() }
|
||||
}
|
||||
step("Assert payment account balance is displayed") {
|
||||
onTangemPayMainScreen { balance.assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
|
||||
// Compose Test gesture — UiAutomator swipe doesn't reach Material3 PullToRefreshBox's NestedScrollConnection.
|
||||
fun BaseTestCase.pullToRefreshTangemPay() {
|
||||
val balance = composeTestRule.onNode(hasTestTag(TangemPayTestTags.PAYMENT_ACCOUNT_BALANCE))
|
||||
balance.performTouchInput {
|
||||
swipeDown(startY = 0f, endY = visibleSize.height.toFloat() * 6f, durationMillis = 800)
|
||||
}
|
||||
composeTestRule.mainClock.advanceTimeBy(2_000L)
|
||||
waitForIdle()
|
||||
}
|
||||
|
|
@ -1,9 +1,19 @@
|
|||
package com.tangem.scenarios
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.ui.test.onAllNodesWithText
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.common.constants.TestConstants
|
||||
import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT_SHORT
|
||||
import com.tangem.common.extensions.clickWithAssertion
|
||||
import com.tangem.common.utils.setClipboardText
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.screens.onScanQrScreen
|
||||
import com.tangem.screens.onWalletConnectBottomSheet
|
||||
import com.tangem.screens.onWalletConnectDetailsBottomSheet
|
||||
import com.tangem.screens.onWalletConnectScreen
|
||||
import com.tangem.screens.onWarningBottomSheet
|
||||
import io.github.kakaocup.kakao.common.utilities.getResourceString
|
||||
import io.qameta.allure.kotlin.Allure.step
|
||||
|
||||
fun BaseTestCase.checkWalletConnectBottomSheet() {
|
||||
|
|
@ -17,9 +27,6 @@ fun BaseTestCase.checkWalletConnectBottomSheet() {
|
|||
step("Assert 'Wallet Connect' bottom sheet app name is displayed") {
|
||||
onWalletConnectBottomSheet { appName.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert 'Wallet Connect' bottom sheet approve icon is displayed") {
|
||||
onWalletConnectBottomSheet { approveIcon.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert 'Wallet Connect' bottom sheet app URL is displayed") {
|
||||
onWalletConnectBottomSheet { appUrl.assertIsDisplayed() }
|
||||
}
|
||||
|
|
@ -82,9 +89,6 @@ fun BaseTestCase.checkWalletConnectScreen(withConnections: Boolean) {
|
|||
step("Assert app name is displayed") {
|
||||
onWalletConnectScreen { appName.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert approve icon is displayed") {
|
||||
onWalletConnectScreen { approveIcon.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert app URL is displayed") {
|
||||
onWalletConnectScreen { appUrl.assertIsDisplayed() }
|
||||
}
|
||||
|
|
@ -117,6 +121,73 @@ fun BaseTestCase.checkWalletConnectScreen(withConnections: Boolean) {
|
|||
|
||||
}
|
||||
|
||||
fun BaseTestCase.establishAndDisconnectWcSession(
|
||||
context: Context,
|
||||
deepLinkUri: String?,
|
||||
dAppName: String,
|
||||
) {
|
||||
step("Set URI to clipboard") {
|
||||
setClipboardText(context, deepLinkUri)
|
||||
}
|
||||
step("Create connection via 'Paste from clipboard' button") {
|
||||
createConnectionViaPasteFromClipboardButton()
|
||||
}
|
||||
step("Check 'Wallet Connect' bottom sheet") {
|
||||
composeTestRule.waitUntil(timeoutMillis = TestConstants.WAIT_UNTIL_TIMEOUT) {
|
||||
runCatching { checkWalletConnectBottomSheet() }.isSuccess
|
||||
}
|
||||
}
|
||||
step("Click on 'Connect' button and dismiss 'Unknown domain' alert if shown") {
|
||||
confirmWcConnection()
|
||||
}
|
||||
step("Check 'Wallet Connect' screen with connections") {
|
||||
checkWalletConnectScreen(withConnections = true)
|
||||
}
|
||||
step("Click on app icon") {
|
||||
onWalletConnectScreen { appIcon.performClick() }
|
||||
}
|
||||
step("Check 'Wallet Connect' details bottom sheet") {
|
||||
checkWalletConnectDetailsBottomSheet(dAppName)
|
||||
}
|
||||
step("Click on 'Disconnect' button") {
|
||||
onWalletConnectDetailsBottomSheet { disconnectButton.performClick() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clicks 'Connect' in the WalletConnect bottom sheet and dismisses the 'Unknown domain' security
|
||||
* alert if it appears.
|
||||
*
|
||||
* qa-tools URIs are not registered with Reown Verify API, so Reown returns validation=UNKNOWN —
|
||||
* after the production change in DefaultWcPairUseCase that maps UNKNOWN to FAILED_TO_VERIFY, the
|
||||
* app shows a Security Alert before establishing the session. Tests that drive qa-tools URIs go
|
||||
* through this helper to consistently accept the warning.
|
||||
*/
|
||||
fun BaseTestCase.confirmWcConnection() {
|
||||
step("Click on 'Connect' button") {
|
||||
onWalletConnectBottomSheet { connectButton.performClick() }
|
||||
}
|
||||
waitForIdle()
|
||||
|
||||
val alertText = getResourceString(R.string.wc_alert_connect_anyway)
|
||||
val alertAppeared = runCatching {
|
||||
composeTestRule.waitUntil(timeoutMillis = WAIT_UNTIL_TIMEOUT_SHORT) {
|
||||
composeTestRule.onAllNodesWithText(alertText).fetchSemanticsNodes().isNotEmpty()
|
||||
}
|
||||
}.isSuccess
|
||||
|
||||
if (alertAppeared) {
|
||||
step("Click on 'Connect anyway' button") {
|
||||
onWarningBottomSheet { connectAnywayButton.clickWithAssertion() }
|
||||
}
|
||||
}
|
||||
|
||||
step("Assert 'Connect' button is not displayed") {
|
||||
waitForIdle()
|
||||
onWalletConnectBottomSheet { connectButton.assertIsNotDisplayed() }
|
||||
}
|
||||
}
|
||||
|
||||
fun BaseTestCase.checkWalletConnectDetailsBottomSheet(dAppName: String) {
|
||||
waitForIdle()
|
||||
step("Assert connection details title is displayed") {
|
||||
|
|
@ -134,21 +205,9 @@ fun BaseTestCase.checkWalletConnectDetailsBottomSheet(dAppName: String) {
|
|||
step("Assert app name is displayed") {
|
||||
onWalletConnectDetailsBottomSheet { appName.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert approve icon is displayed") {
|
||||
onWalletConnectDetailsBottomSheet { approveIcon.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert app URL is displayed") {
|
||||
onWalletConnectDetailsBottomSheet { appUrl.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert wallet icon is displayed") {
|
||||
onWalletConnectDetailsBottomSheet { walletIcon.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert wallet title is displayed") {
|
||||
onWalletConnectDetailsBottomSheet { walletTitle.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert wallet name is displayed") {
|
||||
onWalletConnectDetailsBottomSheet { walletName.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert 'Connected networks' title is displayed") {
|
||||
onWalletConnectDetailsBottomSheet { connectedNetworksTitle.assertIsDisplayed() }
|
||||
}
|
||||
|
|
@ -167,4 +226,13 @@ fun BaseTestCase.checkWalletConnectDetailsBottomSheet(dAppName: String) {
|
|||
step("Assert 'Disconnect button' is displayed") {
|
||||
onWalletConnectDetailsBottomSheet { disconnectButton.assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
|
||||
fun BaseTestCase.createConnectionViaPasteFromClipboardButton() {
|
||||
step("Click on 'New connection' button") {
|
||||
onWalletConnectScreen { newConnectionButton.performClick() }
|
||||
}
|
||||
step("Click on 'Paste from clipboard' button") {
|
||||
onScanQrScreen { pasteFromClipboardButton.clickWithAssertion() }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.screens
|
||||
|
||||
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
|
||||
import androidx.compose.ui.test.hasText as withText
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.core.ui.R as CoreUiR
|
||||
import com.tangem.core.ui.test.BaseButtonTestTags
|
||||
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 BiometryDialogPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
||||
ComposeScreen<BiometryDialogPageObject>(semanticsProvider = semanticsProvider) {
|
||||
|
||||
val dontAllowButton: KNode = child {
|
||||
hasTestTag(BaseButtonTestTags.BUTTON)
|
||||
hasAnyDescendant(withText(getResourceString(CoreUiR.string.save_user_wallet_agreement_dont_allow)))
|
||||
useUnmergedTree = true
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BaseTestCase.onBiometryDialog(function: BiometryDialogPageObject.() -> Unit) =
|
||||
onComposeScreen(composeTestRule, function)
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package com.tangem.screens
|
||||
|
||||
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.core.ui.test.BaseSearchBarTestTags
|
||||
import com.tangem.core.ui.test.BuyTokenScreenTestTags
|
||||
import com.tangem.core.ui.test.TokenElementsTestTags
|
||||
import com.tangem.core.ui.test.TopAppBarTestTags
|
||||
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 androidx.compose.ui.test.hasTestTag as withTestTag
|
||||
import androidx.compose.ui.test.hasText as withText
|
||||
|
||||
/**
|
||||
* "You receive" token chooser opened from the main-screen "Add funds" button.
|
||||
*/
|
||||
class ChooseTokenPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
||||
ComposeScreen<ChooseTokenPageObject>(semanticsProvider = semanticsProvider) {
|
||||
|
||||
val topAppBarTitle: KNode = child {
|
||||
hasTestTag(TopAppBarTestTags.TITLE)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val searchBar: KNode = child {
|
||||
hasTestTag(BaseSearchBarTestTags.SEARCH_BAR)
|
||||
}
|
||||
|
||||
fun tokenWithTitle(tokenTitle: String): KNode = child {
|
||||
hasTestTag(BuyTokenScreenTestTags.LAZY_LIST_ITEM)
|
||||
hasAnyDescendant(withTestTag(TokenElementsTestTags.TOKEN_TITLE))
|
||||
hasAnyDescendant(withText(tokenTitle))
|
||||
useUnmergedTree = true
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BaseTestCase.onChooseTokenScreen(function: ChooseTokenPageObject.() -> Unit) =
|
||||
onComposeScreen(composeTestRule, function)
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
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.BaseBottomSheetTestTags
|
||||
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
|
||||
|
||||
/**
|
||||
* "Get token" bottom sheet shown after picking a token in the Add funds flow.
|
||||
* Contains quick actions (Buy / Receive / …) and the "Go to token" button.
|
||||
*/
|
||||
class GetTokenBottomSheetPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
||||
ComposeScreen<GetTokenBottomSheetPageObject>(semanticsProvider = semanticsProvider) {
|
||||
|
||||
val title: KNode = child {
|
||||
hasTestTag(BaseBottomSheetTestTags.TITLE)
|
||||
}
|
||||
|
||||
val closeButton: KNode = child {
|
||||
hasTestTag(BaseBottomSheetTestTags.CLOSE_BUTTON)
|
||||
}
|
||||
|
||||
// The "Get token" sheet action rows use combinedClickable; the row's testTag lands on a
|
||||
// separate zero-bounds semantics node that fails assertIsDisplayed. Matching the merged node
|
||||
// by its title text yields the displayed, clickable row (performClick injects a touch at its
|
||||
// center, which the row's clickable handles).
|
||||
val buyButton: KNode = child {
|
||||
hasText(getResourceString(R.string.common_buy))
|
||||
}
|
||||
|
||||
val receiveButton: KNode = child {
|
||||
hasText(getResourceString(R.string.common_receive))
|
||||
}
|
||||
|
||||
val goToTokenButton: KNode = child {
|
||||
hasText(getResourceString(R.string.common_go_to_token))
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BaseTestCase.onGetTokenBottomSheet(function: GetTokenBottomSheetPageObject.() -> Unit) =
|
||||
onComposeScreen(composeTestRule, function)
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.screens
|
||||
|
||||
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.core.ui.test.HotWalletAccessCodeTestTags
|
||||
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
|
||||
|
||||
class HotWalletAccessCodePageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
||||
ComposeScreen<HotWalletAccessCodePageObject>(semanticsProvider = semanticsProvider) {
|
||||
|
||||
val accessCodeInput: KNode = child {
|
||||
hasTestTag(HotWalletAccessCodeTestTags.ACCESS_CODE_INPUT)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BaseTestCase.onHotWalletAccessCodeScreen(function: HotWalletAccessCodePageObject.() -> Unit) =
|
||||
onComposeScreen(composeTestRule, function)
|
||||
|
|
@ -52,6 +52,11 @@ class MainScreenPageObject(semanticsProvider: SemanticsNodeInteractionsProvider)
|
|||
hasText(getResourceString(R.string.common_buy))
|
||||
}
|
||||
|
||||
val addFundsButton: KNode = child {
|
||||
hasTestTag(BaseActionButtonsBlockTestTags.ACTION_BUTTON)
|
||||
hasText(getResourceString(R.string.common_add_funds))
|
||||
}
|
||||
|
||||
val sendButton: KNode = child {
|
||||
hasTestTag(BaseActionButtonsBlockTestTags.ACTION_BUTTON)
|
||||
hasText(getResourceString(R.string.common_send))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
package com.tangem.screens
|
||||
|
||||
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
|
||||
import androidx.compose.ui.test.hasText as withText
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.test.BaseButtonTestTags
|
||||
import com.tangem.core.ui.test.TransactionSuccessScreenTestTags
|
||||
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
|
||||
|
||||
// Legacy swap feature's success screen — lacks CONTAINER testTag that SendSuccessPageObject relies on.
|
||||
class SwapSuccessPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
||||
ComposeScreen<SwapSuccessPageObject>(semanticsProvider = semanticsProvider) {
|
||||
|
||||
val title: KNode = child {
|
||||
hasTestTag(TransactionSuccessScreenTestTags.TITLE)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val closeButton: KNode = child {
|
||||
hasTestTag(BaseButtonTestTags.BUTTON)
|
||||
hasAnyDescendant(withText(getResourceString(R.string.common_close)))
|
||||
useUnmergedTree = true
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BaseTestCase.onSwapSuccessScreen(function: SwapSuccessPageObject.() -> Unit) =
|
||||
onComposeScreen(composeTestRule, function)
|
||||
|
|
@ -38,6 +38,12 @@ class SwapTokenPageObject(semanticsProvider: SemanticsNodeInteractionsProvider)
|
|||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
// Tangem Pay withdraw hides FEE_SELECTOR_BLOCK; gate on the "Network fee" label instead.
|
||||
val networkFeeTitle: KNode = child {
|
||||
hasText(getResourceString(R.string.common_network_fee_title))
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val selectFeeIcon: KNode = child {
|
||||
hasTestTag(FeeSelectorBlockTestTags.SELECT_FEE_ICON)
|
||||
useUnmergedTree = true
|
||||
|
|
|
|||
|
|
@ -30,11 +30,29 @@ class WarningBottomSheetPageObject(semanticsProvider: SemanticsNodeInteractionsP
|
|||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val gotItButton: KNode = child {
|
||||
val okGotItButton: KNode = child {
|
||||
hasTestTag(BaseButtonTestTags.TEXT)
|
||||
hasText(getResourceString(R.string.warning_button_ok))
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val gotItButton: KNode = child {
|
||||
hasTestTag(BaseButtonTestTags.TEXT)
|
||||
hasText(getResourceString(R.string.common_got_it))
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val cancelButton: KNode = child {
|
||||
hasTestTag(BaseButtonTestTags.TEXT)
|
||||
hasText(getResourceString(R.string.common_cancel))
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val connectAnywayButton: KNode = child {
|
||||
hasTestTag(BaseButtonTestTags.TEXT)
|
||||
hasText(getResourceString(R.string.wc_alert_connect_anyway))
|
||||
useUnmergedTree = true
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BaseTestCase.onWarningBottomSheet(function: WarningBottomSheetPageObject.() -> Unit) =
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
package com.tangem.screens.tangempay
|
||||
|
||||
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.core.res.R as CoreResR
|
||||
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 TangemPayAddFundsSheetPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
||||
ComposeScreen<TangemPayAddFundsSheetPageObject>(semanticsProvider = semanticsProvider) {
|
||||
|
||||
val swapOption: KNode = child {
|
||||
hasText(getResourceString(CoreResR.string.tangempay_topup_swap_title))
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val receiveOption: KNode = child {
|
||||
hasText(getResourceString(CoreResR.string.common_receive))
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val title: KNode = child {
|
||||
hasText(getResourceString(CoreResR.string.tangempay_card_details_add_funds))
|
||||
useUnmergedTree = true
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BaseTestCase.onTangemPayAddFundsSheet(function: TangemPayAddFundsSheetPageObject.() -> Unit) =
|
||||
onComposeScreen(composeTestRule, function)
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
package com.tangem.screens.tangempay
|
||||
|
||||
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.core.ui.test.TangemPayTestTags
|
||||
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
|
||||
|
||||
class TangemPayCardPagePageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
||||
ComposeScreen<TangemPayCardPagePageObject>(semanticsProvider = semanticsProvider) {
|
||||
|
||||
val changePinRow: KNode = child {
|
||||
hasTestTag(TangemPayTestTags.CHANGE_PIN_ROW)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val freezeCardRow: KNode = child {
|
||||
hasTestTag(TangemPayTestTags.FREEZE_CARD_ROW)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val cardFrozenBadge: KNode = child {
|
||||
hasTestTag(TangemPayTestTags.CARD_FROZEN_BADGE)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val showDetailsButton: KNode = child {
|
||||
hasTestTag(TangemPayTestTags.CARD_DETAILS_SHOW_BUTTON)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val hideDetailsButton: KNode = child {
|
||||
hasTestTag(TangemPayTestTags.CARD_DETAILS_HIDE_BUTTON)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val numberValue: KNode = child {
|
||||
hasTestTag(TangemPayTestTags.CARD_DETAILS_NUMBER_VALUE)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val expirationValue: KNode = child {
|
||||
hasTestTag(TangemPayTestTags.CARD_DETAILS_EXPIRATION_VALUE)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val cvcValue: KNode = child {
|
||||
hasTestTag(TangemPayTestTags.CARD_DETAILS_CVC_VALUE)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val copyNumberButton: KNode = child {
|
||||
hasTestTag(TangemPayTestTags.CARD_DETAILS_COPY_NUMBER)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val copyExpirationButton: KNode = child {
|
||||
hasTestTag(TangemPayTestTags.CARD_DETAILS_COPY_EXPIRATION)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val copyCvcButton: KNode = child {
|
||||
hasTestTag(TangemPayTestTags.CARD_DETAILS_COPY_CVC)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BaseTestCase.onTangemPayCardPageScreen(function: TangemPayCardPagePageObject.() -> Unit) =
|
||||
onComposeScreen(composeTestRule, function)
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
package com.tangem.screens.tangempay
|
||||
|
||||
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.core.ui.test.TangemPayTestTags
|
||||
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
|
||||
|
||||
class TangemPayChangePinPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
||||
ComposeScreen<TangemPayChangePinPageObject>(semanticsProvider = semanticsProvider) {
|
||||
|
||||
val title: KNode = child {
|
||||
hasTestTag(TangemPayTestTags.PIN_SCREEN_TITLE)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val description: KNode = child {
|
||||
hasTestTag(TangemPayTestTags.PIN_SCREEN_DESCRIPTION)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val inputField: KNode = child {
|
||||
hasTestTag(TangemPayTestTags.PIN_INPUT_FIELD)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val submitButton: KNode = child {
|
||||
hasTestTag(TangemPayTestTags.PIN_SUBMIT_BUTTON)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val errorMessage: KNode = child {
|
||||
hasTestTag(TangemPayTestTags.PIN_ERROR_MESSAGE)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val successTitle: KNode = child {
|
||||
hasTestTag(TangemPayTestTags.PIN_SUCCESS_TITLE)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val successDescription: KNode = child {
|
||||
hasTestTag(TangemPayTestTags.PIN_SUCCESS_DESCRIPTION)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val doneButton: KNode = child {
|
||||
hasTestTag(TangemPayTestTags.PIN_DONE_BUTTON)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BaseTestCase.onTangemPayChangePinScreen(function: TangemPayChangePinPageObject.() -> Unit) =
|
||||
onComposeScreen(composeTestRule, function)
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package com.tangem.screens.tangempay
|
||||
|
||||
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.core.ui.test.WarningBottomSheetTestTags
|
||||
import com.tangem.core.res.R as CoreResR
|
||||
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 TangemPayFreezeConfirmationPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
||||
ComposeScreen<TangemPayFreezeConfirmationPageObject>(semanticsProvider = semanticsProvider) {
|
||||
|
||||
val freezeTitle: KNode = child {
|
||||
hasTestTag(WarningBottomSheetTestTags.TITLE)
|
||||
hasText(getResourceString(CoreResR.string.tangem_pay_freeze_card_alert_title))
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val unfreezeTitle: KNode = child {
|
||||
hasTestTag(WarningBottomSheetTestTags.TITLE)
|
||||
hasText(getResourceString(CoreResR.string.tangem_pay_unfreeze_card_alert_title))
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val submitButton: KNode = child {
|
||||
hasTestTag(WarningBottomSheetTestTags.BUTTON_PRIMARY)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BaseTestCase.onTangemPayFreezeConfirmation(function: TangemPayFreezeConfirmationPageObject.() -> Unit) =
|
||||
onComposeScreen(composeTestRule, function)
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package com.tangem.screens.tangempay
|
||||
|
||||
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.core.ui.test.BaseActionButtonsBlockTestTags
|
||||
import com.tangem.core.ui.test.TangemPayTestTags
|
||||
import com.tangem.core.res.R as CoreResR
|
||||
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 androidx.compose.ui.test.hasText as withText
|
||||
|
||||
class TangemPayMainPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
||||
ComposeScreen<TangemPayMainPageObject>(semanticsProvider = semanticsProvider) {
|
||||
|
||||
val mainScreenTile: KNode = child {
|
||||
hasTestTag(TangemPayTestTags.MAIN_SCREEN_TILE)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val balance: KNode = child {
|
||||
hasTestTag(TangemPayTestTags.PAYMENT_ACCOUNT_BALANCE)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val cardButton: KNode = child {
|
||||
hasTestTag(TangemPayTestTags.PAYMENT_ACCOUNT_CARD_BUTTON)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val topUpButton: KNode = child {
|
||||
hasTestTag(BaseActionButtonsBlockTestTags.ACTION_BUTTON)
|
||||
hasAnyDescendant(withText(getResourceString(CoreResR.string.tangempay_card_details_add_funds)))
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val withdrawButton: KNode = child {
|
||||
hasTestTag(BaseActionButtonsBlockTestTags.ACTION_BUTTON)
|
||||
hasAnyDescendant(withText(getResourceString(CoreResR.string.tangempay_card_details_withdraw)))
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
fun transactionRowWithText(text: String): KNode = child {
|
||||
hasText(text)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BaseTestCase.onTangemPayMainScreen(function: TangemPayMainPageObject.() -> Unit) =
|
||||
onComposeScreen(composeTestRule, function)
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package com.tangem.screens.tangempay
|
||||
|
||||
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.core.ui.test.WarningBottomSheetTestTags
|
||||
import com.tangem.core.res.R as CoreResR
|
||||
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 TangemPayWithdrawNoteSheetPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
||||
ComposeScreen<TangemPayWithdrawNoteSheetPageObject>(semanticsProvider = semanticsProvider) {
|
||||
|
||||
val title: KNode = child {
|
||||
hasTestTag(WarningBottomSheetTestTags.TITLE)
|
||||
hasText(getResourceString(CoreResR.string.tangempay_withdrawal_note_title))
|
||||
useUnmergedTree = true
|
||||
}
|
||||
|
||||
val gotItButton: KNode = child {
|
||||
hasTestTag(WarningBottomSheetTestTags.BUTTON_PRIMARY)
|
||||
useUnmergedTree = true
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BaseTestCase.onTangemPayWithdrawNoteSheet(function: TangemPayWithdrawNoteSheetPageObject.() -> Unit) =
|
||||
onComposeScreen(composeTestRule, function)
|
||||
|
|
@ -40,15 +40,18 @@ class BuyTokenTest : BaseTestCase() {
|
|||
step("Synchronize addresses") {
|
||||
synchronizeAddresses()
|
||||
}
|
||||
step("Click on 'Buy' button") {
|
||||
onMainScreen { buyButton.clickWithAssertion() }
|
||||
step("Click on 'Add funds' button") {
|
||||
onMainScreen { addFundsButton.clickWithAssertion() }
|
||||
}
|
||||
step("Click on token with name: '$tokenTitle'") {
|
||||
onBuyTokenScreen {
|
||||
onChooseTokenScreen {
|
||||
topAppBarTitle.assertIsDisplayed()
|
||||
tokenWithTitleAndFiatAmount(tokenTitle).clickWithAssertion()
|
||||
tokenWithTitle(tokenTitle).clickWithAssertion()
|
||||
}
|
||||
}
|
||||
step("Click on 'Buy' in 'Get token' bottom sheet") {
|
||||
onGetTokenBottomSheet { buyButton.clickWithAssertion() }
|
||||
}
|
||||
step("Assert error notification title is displayed") {
|
||||
onBuyTokenDetailsScreen { errorNotificationTitle.assertIsDisplayed() }
|
||||
}
|
||||
|
|
@ -84,15 +87,18 @@ class BuyTokenTest : BaseTestCase() {
|
|||
step("Synchronize addresses") {
|
||||
synchronizeAddresses()
|
||||
}
|
||||
step("Click on 'Buy' button") {
|
||||
onMainScreen { buyButton.clickWithAssertion() }
|
||||
step("Click on 'Add funds' button") {
|
||||
onMainScreen { addFundsButton.clickWithAssertion() }
|
||||
}
|
||||
step("Click on token with name: '$tokenTitle'") {
|
||||
onBuyTokenScreen {
|
||||
onChooseTokenScreen {
|
||||
topAppBarTitle.assertIsDisplayed()
|
||||
tokenWithTitleAndFiatAmount(tokenTitle).clickWithAssertion()
|
||||
tokenWithTitle(tokenTitle).clickWithAssertion()
|
||||
}
|
||||
}
|
||||
step("Click on 'Buy' in 'Get token' bottom sheet") {
|
||||
onGetTokenBottomSheet { buyButton.clickWithAssertion() }
|
||||
}
|
||||
step("Click on 'Confirm' button in 'Dialog'") {
|
||||
onDialog { confirmButton.clickWithAssertion() }
|
||||
}
|
||||
|
|
@ -155,15 +161,18 @@ class BuyTokenTest : BaseTestCase() {
|
|||
step("Synchronize addresses") {
|
||||
synchronizeAddresses()
|
||||
}
|
||||
step("Click on 'Buy' button") {
|
||||
onMainScreen { buyButton.clickWithAssertion() }
|
||||
step("Click on 'Add funds' button") {
|
||||
onMainScreen { addFundsButton.clickWithAssertion() }
|
||||
}
|
||||
step("Click on token with name: '$tokenTitle'") {
|
||||
onBuyTokenScreen {
|
||||
onChooseTokenScreen {
|
||||
topAppBarTitle.assertIsDisplayed()
|
||||
tokenWithTitleAndFiatAmount(tokenTitle).clickWithAssertion()
|
||||
tokenWithTitle(tokenTitle).clickWithAssertion()
|
||||
}
|
||||
}
|
||||
step("Click on 'Buy' in 'Get token' bottom sheet") {
|
||||
onGetTokenBottomSheet { buyButton.clickWithAssertion() }
|
||||
}
|
||||
step("Click on 'Confirm' button in 'Dialog'") {
|
||||
onDialog { confirmButton.clickWithAssertion() }
|
||||
}
|
||||
|
|
@ -238,15 +247,18 @@ class BuyTokenTest : BaseTestCase() {
|
|||
step("Synchronize addresses") {
|
||||
synchronizeAddresses()
|
||||
}
|
||||
step("Click on 'Buy' button") {
|
||||
onMainScreen { buyButton.clickWithAssertion() }
|
||||
step("Click on 'Add funds' button") {
|
||||
onMainScreen { addFundsButton.clickWithAssertion() }
|
||||
}
|
||||
step("Click on token with name: '$tokenTitle'") {
|
||||
onBuyTokenScreen {
|
||||
onChooseTokenScreen {
|
||||
topAppBarTitle.assertIsDisplayed()
|
||||
tokenWithTitleAndFiatAmount(tokenTitle).clickWithAssertion()
|
||||
tokenWithTitle(tokenTitle).clickWithAssertion()
|
||||
}
|
||||
}
|
||||
step("Click on 'Buy' in 'Get token' bottom sheet") {
|
||||
onGetTokenBottomSheet { buyButton.clickWithAssertion() }
|
||||
}
|
||||
step("Click on 'Confirm' button in 'Dialog'") {
|
||||
onDialog { confirmButton.clickWithAssertion() }
|
||||
}
|
||||
|
|
@ -320,15 +332,18 @@ class BuyTokenTest : BaseTestCase() {
|
|||
step("Synchronize addresses") {
|
||||
synchronizeAddresses()
|
||||
}
|
||||
step("Click on 'Buy' button") {
|
||||
onMainScreen { buyButton.clickWithAssertion() }
|
||||
step("Click on 'Add funds' button") {
|
||||
onMainScreen { addFundsButton.clickWithAssertion() }
|
||||
}
|
||||
step("Click on token with name: '$tokenTitle'") {
|
||||
onBuyTokenScreen {
|
||||
onChooseTokenScreen {
|
||||
topAppBarTitle.assertIsDisplayed()
|
||||
tokenWithTitleAndFiatAmount(tokenTitle).clickWithAssertion()
|
||||
tokenWithTitle(tokenTitle).clickWithAssertion()
|
||||
}
|
||||
}
|
||||
step("Click on 'Buy' in 'Get token' bottom sheet") {
|
||||
onGetTokenBottomSheet { buyButton.clickWithAssertion() }
|
||||
}
|
||||
step("Click on 'Confirm' button in 'Dialog'") {
|
||||
onDialog { confirmButton.clickWithAssertion() }
|
||||
}
|
||||
|
|
@ -406,15 +421,18 @@ class BuyTokenTest : BaseTestCase() {
|
|||
step("Synchronize addresses") {
|
||||
synchronizeAddresses()
|
||||
}
|
||||
step("Click on 'Buy' button") {
|
||||
onMainScreen { buyButton.clickWithAssertion() }
|
||||
step("Click on 'Add funds' button") {
|
||||
onMainScreen { addFundsButton.clickWithAssertion() }
|
||||
}
|
||||
step("Click on token with name: '$tokenTitle'") {
|
||||
onBuyTokenScreen {
|
||||
onChooseTokenScreen {
|
||||
topAppBarTitle.assertIsDisplayed()
|
||||
tokenWithTitleAndFiatAmount(tokenTitle).clickWithAssertion()
|
||||
tokenWithTitle(tokenTitle).clickWithAssertion()
|
||||
}
|
||||
}
|
||||
step("Click on 'Buy' in 'Get token' bottom sheet") {
|
||||
onGetTokenBottomSheet { buyButton.clickWithAssertion() }
|
||||
}
|
||||
step("Click on 'Confirm' button in 'Dialog'") {
|
||||
onDialog { confirmButton.clickWithAssertion() }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -419,17 +419,17 @@ class MainScreenActionButtonsTest : BaseTestCase() {
|
|||
step("Synchronize addresses") {
|
||||
synchronizeAddresses()
|
||||
}
|
||||
step("Assert 'Buy' button is displayed") {
|
||||
onMainScreen { buyButton.assertIsDisplayed() }
|
||||
step("Assert 'Add funds' button is displayed") {
|
||||
onMainScreen { addFundsButton.assertIsDisplayed() }
|
||||
}
|
||||
step("Click on 'Buy' button") {
|
||||
onMainScreen { buyButton.performClick() }
|
||||
step("Click on 'Add funds' button") {
|
||||
onMainScreen { addFundsButton.performClick() }
|
||||
}
|
||||
step("Assert 'Buy' screen title is displayed") {
|
||||
onBuyTokenScreen { topAppBarTitle.assertIsDisplayed() }
|
||||
step("Assert 'Choose token' screen title is displayed") {
|
||||
onChooseTokenScreen { topAppBarTitle.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert token with title: '$tokenTitle' is displayed") {
|
||||
onBuyTokenScreen { tokenWithTitleAndFiatAmount(tokenTitle).assertIsDisplayed() }
|
||||
onChooseTokenScreen { tokenWithTitle(tokenTitle).assertIsDisplayed() }
|
||||
}
|
||||
step("Press 'Back' button") {
|
||||
device.uiDevice.pressBack()
|
||||
|
|
@ -478,17 +478,18 @@ class MainScreenActionButtonsTest : BaseTestCase() {
|
|||
step("Open 'Main Screen'") {
|
||||
openMainScreen()
|
||||
}
|
||||
step("Assert 'Buy' button is displayed") {
|
||||
onMainScreen { buyButton.assertIsDisplayed() }
|
||||
step("Assert 'Add funds' button is displayed") {
|
||||
onMainScreen { addFundsButton.assertIsDisplayed() }
|
||||
}
|
||||
step("Click on 'Buy' button") {
|
||||
onMainScreen { buyButton.performClick() }
|
||||
step("Click on 'Add funds' button") {
|
||||
onMainScreen { addFundsButton.performClick() }
|
||||
}
|
||||
step("Check 'Action is unavailable' dialog") {
|
||||
checkActionIsUnavailableDialog()
|
||||
step("Assert 'Choose token' screen opens (Add funds is always available)") {
|
||||
onChooseTokenScreen { topAppBarTitle.assertIsDisplayed() }
|
||||
}
|
||||
step("Click on 'Ok' button") {
|
||||
onDialog { okButton.performClick() }
|
||||
step("Press 'Back' to return to main screen") {
|
||||
device.uiDevice.pressBack()
|
||||
waitForIdle()
|
||||
}
|
||||
step("Assert 'Swap' button is displayed") {
|
||||
onMainScreen { swapButton.assertIsDisplayed() }
|
||||
|
|
@ -535,17 +536,18 @@ class MainScreenActionButtonsTest : BaseTestCase() {
|
|||
step("Open 'Main Screen'") {
|
||||
openMainScreen()
|
||||
}
|
||||
step("Assert 'Buy' button is displayed") {
|
||||
onMainScreen { buyButton.assertIsDisplayed() }
|
||||
step("Assert 'Add funds' button is displayed") {
|
||||
onMainScreen { addFundsButton.assertIsDisplayed() }
|
||||
}
|
||||
step("Click on 'Buy' button") {
|
||||
onMainScreen { buyButton.performClick() }
|
||||
step("Click on 'Add funds' button") {
|
||||
onMainScreen { addFundsButton.performClick() }
|
||||
}
|
||||
step("Check 'Action is unavailable' dialog") {
|
||||
checkActionIsUnavailableDialog()
|
||||
step("Assert 'Choose token' screen opens (Add funds is always available)") {
|
||||
onChooseTokenScreen { topAppBarTitle.assertIsDisplayed() }
|
||||
}
|
||||
step("Click on 'Ok' button") {
|
||||
onDialog { okButton.performClick() }
|
||||
step("Press 'Back' to return to main screen") {
|
||||
device.uiDevice.pressBack()
|
||||
waitForIdle()
|
||||
}
|
||||
step("Assert 'Swap' button is displayed") {
|
||||
onMainScreen { swapButton.assertIsDisplayed() }
|
||||
|
|
|
|||
|
|
@ -51,9 +51,12 @@ class HideTokenTest : BaseTestCase() {
|
|||
dialogContainer.assertIsDisplayed()
|
||||
okButton.clickWithAssertion()
|
||||
}
|
||||
waitForIdle()
|
||||
}
|
||||
step("Assert token: '$tokenTitle' is not displayed") {
|
||||
onMainScreen { assertTokenDoesNotExist(tokenTitle) }
|
||||
flakySafely {
|
||||
onMainScreen { assertTokenDoesNotExist(tokenTitle) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@ package com.tangem.tests.main
|
|||
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.common.constants.TestConstants.USER_TOKENS_API_SCENARIO
|
||||
import com.tangem.common.extensions.clickWithAssertion
|
||||
import com.tangem.common.utils.resetWireMockScenarioState
|
||||
import com.tangem.common.utils.setWireMockScenarioState
|
||||
import com.tangem.scenarios.openMainScreen
|
||||
import com.tangem.scenarios.synchronizeAddresses
|
||||
import com.tangem.screens.onAddAndManageBottomSheet
|
||||
import com.tangem.screens.onMainScreen
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import io.qameta.allure.kotlin.AllureId
|
||||
|
|
@ -81,8 +83,17 @@ class MainScreenTest : BaseTestCase() {
|
|||
step("Open 'Main Screen'") {
|
||||
openMainScreen()
|
||||
}
|
||||
step("Assert 'Add & Manage' button is not displayed") {
|
||||
onMainScreen { addAndManageButtonNode.assertIsNotDisplayed()}
|
||||
step("Assert 'Add & Manage' button is displayed") {
|
||||
onMainScreen { addAndManageButtonNode.assertIsDisplayed() }
|
||||
}
|
||||
step("Click 'Add & Manage' button") {
|
||||
onMainScreen { addAndManageButtonNode.clickWithAssertion() }
|
||||
}
|
||||
step("Assert 'Organize tokens' option is not displayed (nothing to organize)") {
|
||||
onAddAndManageBottomSheet { organizeTokensButton.assertIsNotDisplayed() }
|
||||
}
|
||||
step("Assert 'Add tokens' option is displayed") {
|
||||
onAddAndManageBottomSheet { addTokensButton.assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ class SendViaSwapTest : BaseTestCase() {
|
|||
onWarningBottomSheet { message(warningMessage).assertIsDisplayed() }
|
||||
}
|
||||
step("Click on 'Ok, Got it!' button") {
|
||||
onWarningBottomSheet { gotItButton.performClick() }
|
||||
onWarningBottomSheet { okGotItButton.performClick() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,236 @@
|
|||
package com.tangem.tests.tangempay
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.common.constants.TestConstants.TANGEM_PAY_ELIGIBILITY_SCENARIO
|
||||
import com.tangem.common.extensions.assertTextContainsSafe
|
||||
import com.tangem.common.extensions.clickWithAssertion
|
||||
import com.tangem.common.extensions.extractText
|
||||
import com.tangem.common.extensions.pullToRefresh
|
||||
import com.tangem.common.utils.assertClipboardTextEquals
|
||||
import com.tangem.common.utils.resetWireMockScenarioState
|
||||
import com.tangem.common.utils.setWireMockScenarioState
|
||||
import com.tangem.scenarios.*
|
||||
import com.tangem.screens.tangempay.*
|
||||
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 TangemPayTest : BaseTestCase() {
|
||||
|
||||
@AllureId("4549")
|
||||
@DisplayName("Tangem Pay: change PIN code from card details")
|
||||
@Test
|
||||
fun changePin_SetsNewPinCode_FromCardDetails() {
|
||||
val newPin = "5217"
|
||||
val pinSetupScenario = "tangem_pay_pin_setup"
|
||||
val pinNotSetState = "PinNotSet"
|
||||
val eligibilityState = "PaeraCustomer"
|
||||
|
||||
setupHooks(
|
||||
additionalBeforeSection = {
|
||||
setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO, eligibilityState)
|
||||
setWireMockScenarioState(pinSetupScenario, pinNotSetState)
|
||||
},
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO)
|
||||
resetWireMockScenarioState(pinSetupScenario)
|
||||
},
|
||||
).run {
|
||||
openTangemPay()
|
||||
step("Click on card button") {
|
||||
onTangemPayMainScreen { cardButton.clickWithAssertion() }
|
||||
}
|
||||
step("Click on 'Change PIN' row") {
|
||||
onTangemPayCardPageScreen { changePinRow.clickWithAssertion() }
|
||||
}
|
||||
step("Assert PIN screen is displayed") {
|
||||
onTangemPayChangePinScreen { title.assertIsDisplayed() }
|
||||
}
|
||||
step("Enter PIN '$newPin'") {
|
||||
onTangemPayChangePinScreen { inputField.performTextInput(newPin) }
|
||||
}
|
||||
step("Click on 'Submit' button") {
|
||||
onTangemPayChangePinScreen { submitButton.performClick() }
|
||||
}
|
||||
step("Assert success screen is displayed") {
|
||||
onTangemPayChangePinScreen { successTitle.assertIsDisplayed() }
|
||||
}
|
||||
step("Click on 'Done' button") {
|
||||
onTangemPayChangePinScreen { doneButton.clickWithAssertion() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("4969")
|
||||
@DisplayName("Tangem Pay: balance updates after transaction on payment account screen")
|
||||
@Test
|
||||
fun balanceUpdatesAfterTransaction_OnPaymentAccountScreen() {
|
||||
val balanceScenario = "tangem_pay_balance_update"
|
||||
val initialState = "InitialBalance"
|
||||
val afterTransactionState = "AfterTransaction"
|
||||
val eligibilityState = "PaeraCustomer"
|
||||
|
||||
setupHooks(
|
||||
additionalBeforeSection = {
|
||||
setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO, eligibilityState)
|
||||
setWireMockScenarioState(balanceScenario, initialState)
|
||||
},
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO)
|
||||
resetWireMockScenarioState(balanceScenario)
|
||||
},
|
||||
).run {
|
||||
openTangemPay()
|
||||
step("Assert initial balance contains '10'") {
|
||||
onTangemPayMainScreen { balance.assertTextContainsSafe("10", substring = true) }
|
||||
}
|
||||
step("Switch WireMock scenario '$balanceScenario' to '$afterTransactionState'") {
|
||||
setWireMockScenarioState(balanceScenario, afterTransactionState)
|
||||
}
|
||||
step("Pull to refresh") { pullToRefresh() }
|
||||
step("Assert updated balance contains '9'") {
|
||||
onTangemPayMainScreen { balance.assertTextContainsSafe("9", substring = true) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("4970")
|
||||
@DisplayName("Tangem Pay: new transaction appears after mocked charge")
|
||||
@Test
|
||||
fun transactionList_NewTransactionAppears_AfterMockedCharge() {
|
||||
val historyScenario = "tangem_pay_transaction_history"
|
||||
val initialState = "InitialEmpty"
|
||||
val afterTransactionState = "AfterTransaction"
|
||||
val eligibilityState = "PaeraCustomer"
|
||||
val merchantName = "Mock Merchant"
|
||||
|
||||
setupHooks(
|
||||
additionalBeforeSection = {
|
||||
setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO, eligibilityState)
|
||||
setWireMockScenarioState(historyScenario, initialState)
|
||||
},
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO)
|
||||
resetWireMockScenarioState(historyScenario)
|
||||
},
|
||||
).run {
|
||||
openTangemPay()
|
||||
step("Assert transaction from '$merchantName' is not displayed") {
|
||||
onTangemPayMainScreen {
|
||||
transactionRowWithText(merchantName).assertDoesNotExist()
|
||||
}
|
||||
}
|
||||
step("Switch WireMock scenario '$historyScenario' to '$afterTransactionState'") {
|
||||
setWireMockScenarioState(historyScenario, afterTransactionState)
|
||||
}
|
||||
step("Pull to refresh") { pullToRefresh() }
|
||||
step("Assert transaction from '$merchantName' is displayed") {
|
||||
onTangemPayMainScreen {
|
||||
transactionRowWithText(merchantName).assertIsDisplayed()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("4974")
|
||||
@DisplayName("Tangem Pay: reveal and copy card number, expiration and CVC")
|
||||
@Test
|
||||
fun revealAndCopyCardDetails_NumberExpirationCVC() {
|
||||
val context = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
val eligibilityState = "PaeraCustomer"
|
||||
|
||||
setupHooks(
|
||||
additionalBeforeSection = {
|
||||
setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO, eligibilityState)
|
||||
},
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO)
|
||||
},
|
||||
).run {
|
||||
openTangemPay()
|
||||
step("Click on card button") {
|
||||
onTangemPayMainScreen { cardButton.clickWithAssertion() }
|
||||
}
|
||||
step("Click on 'Show details' button") {
|
||||
onTangemPayCardPageScreen { showDetailsButton.clickWithAssertion() }
|
||||
}
|
||||
step("Assert number, expiration and CVC values are visible") {
|
||||
onTangemPayCardPageScreen {
|
||||
numberValue.assertIsDisplayed()
|
||||
expirationValue.assertIsDisplayed()
|
||||
cvcValue.assertIsDisplayed()
|
||||
}
|
||||
}
|
||||
var displayedNumber = ""
|
||||
var displayedExpiration = ""
|
||||
var displayedCvc = ""
|
||||
onTangemPayCardPageScreen {
|
||||
displayedNumber = numberValue.extractText()
|
||||
displayedExpiration = expirationValue.extractText()
|
||||
displayedCvc = cvcValue.extractText()
|
||||
}
|
||||
step("Click on 'Copy card number' button") {
|
||||
onTangemPayCardPageScreen { copyNumberButton.clickWithAssertion() }
|
||||
waitForIdle()
|
||||
}
|
||||
step("Assert clipboard contains card number") {
|
||||
// Displayed number has spaces for readability; clipboard copies digits only.
|
||||
assertClipboardTextEquals(displayedNumber.replace(" ", ""), context)
|
||||
}
|
||||
step("Click on 'Copy expiration' button") {
|
||||
onTangemPayCardPageScreen { copyExpirationButton.clickWithAssertion() }
|
||||
waitForIdle()
|
||||
}
|
||||
step("Assert clipboard contains expiration date") {
|
||||
assertClipboardTextEquals(displayedExpiration, context)
|
||||
}
|
||||
step("Click on 'Copy CVC' button") {
|
||||
onTangemPayCardPageScreen { copyCvcButton.clickWithAssertion() }
|
||||
waitForIdle()
|
||||
}
|
||||
step("Assert clipboard contains CVC") {
|
||||
assertClipboardTextEquals(displayedCvc, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("4971")
|
||||
@DisplayName("Tangem Pay: freeze card via confirmation sheet")
|
||||
@Test
|
||||
fun freezeUnfreezeCard_TogglesCardState() {
|
||||
val freezeScenario = "tangem_pay_card_freeze"
|
||||
val startedState = "Started"
|
||||
val eligibilityState = "PaeraCustomer"
|
||||
|
||||
setupHooks(
|
||||
additionalBeforeSection = {
|
||||
setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO, eligibilityState)
|
||||
setWireMockScenarioState(freezeScenario, startedState)
|
||||
},
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO)
|
||||
resetWireMockScenarioState(freezeScenario)
|
||||
},
|
||||
).run {
|
||||
openTangemPay()
|
||||
step("Click on card button") {
|
||||
onTangemPayMainScreen { cardButton.clickWithAssertion() }
|
||||
}
|
||||
step("Click on freeze card row (card is active)") {
|
||||
onTangemPayCardPageScreen { freezeCardRow.clickWithAssertion() }
|
||||
}
|
||||
step("Assert freeze confirmation sheet is displayed") {
|
||||
onTangemPayFreezeConfirmation { freezeTitle.assertIsDisplayed() }
|
||||
}
|
||||
step("Click on 'Submit' button (confirm freeze)") {
|
||||
onTangemPayFreezeConfirmation { submitButton.clickWithAssertion() }
|
||||
}
|
||||
step("Assert frozen badge is displayed") {
|
||||
onTangemPayCardPageScreen { cardFrozenBadge.assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
package com.tangem.tests.tangempay
|
||||
|
||||
import androidx.test.espresso.Espresso
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.common.constants.TestConstants.TANGEM_PAY_ACCESS_CODE
|
||||
import com.tangem.common.constants.TestConstants.TANGEM_PAY_ELIGIBILITY_SCENARIO
|
||||
import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT_LONG
|
||||
import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT_VERY_LONG
|
||||
import com.tangem.common.extensions.assertTextContainsSafe
|
||||
import com.tangem.common.extensions.clickWithAssertion
|
||||
import com.tangem.common.utils.resetWireMockScenarioState
|
||||
import com.tangem.common.utils.setWireMockScenarioState
|
||||
import com.tangem.core.res.R as CoreResR
|
||||
import com.tangem.scenarios.*
|
||||
import com.tangem.screens.*
|
||||
import com.tangem.screens.tangempay.*
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import io.github.kakaocup.kakao.common.utilities.getResourceString
|
||||
import io.qameta.allure.kotlin.AllureId
|
||||
import io.qameta.allure.kotlin.junit4.DisplayName
|
||||
import org.junit.Test
|
||||
|
||||
@HiltAndroidTest
|
||||
class TangemPayTopUpTest : BaseTestCase() {
|
||||
|
||||
@AllureId("4973")
|
||||
@DisplayName("Tangem Pay: top up swaps Bitcoin to USDC and appends deposit to history")
|
||||
@Test
|
||||
fun topUpFromTangemPay_SwapsBitcoinToUSDC_AppendsDepositToHistory() {
|
||||
val bitcoinScenario = "bitcoin_utxo"
|
||||
val expressAssetsScenario = "express_api_assets"
|
||||
val balanceScenario = "tangem_pay_balance_update"
|
||||
val historyScenario = "tangem_pay_transaction_history"
|
||||
val eligibilityState = "PaeraCustomer"
|
||||
val bitcoinBalanceState = "BalanceHotWalletSvS"
|
||||
val expressAssetsState = "BitcoinExchangeEnabled"
|
||||
val balanceInitialState = "InitialBalance"
|
||||
val balanceAfterState = "AfterDeposit"
|
||||
val historyInitialState = "InitialEmpty"
|
||||
val historyAfterState = "AfterDeposit"
|
||||
val swapFromAmount = "0.001"
|
||||
val depositLabel = getResourceString(CoreResR.string.tangem_pay_deposit)
|
||||
|
||||
setupHooks(
|
||||
additionalBeforeSection = {
|
||||
setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO, eligibilityState)
|
||||
setWireMockScenarioState(bitcoinScenario, bitcoinBalanceState)
|
||||
setWireMockScenarioState(expressAssetsScenario, expressAssetsState)
|
||||
setWireMockScenarioState(balanceScenario, balanceInitialState)
|
||||
setWireMockScenarioState(historyScenario, historyInitialState)
|
||||
},
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO)
|
||||
resetWireMockScenarioState(bitcoinScenario)
|
||||
resetWireMockScenarioState(expressAssetsScenario)
|
||||
resetWireMockScenarioState(balanceScenario)
|
||||
resetWireMockScenarioState(historyScenario)
|
||||
},
|
||||
).run {
|
||||
openTangemPay()
|
||||
step("Assert initial balance contains '10'") {
|
||||
onTangemPayMainScreen { balance.assertTextContainsSafe("10", substring = true) }
|
||||
}
|
||||
step("Click on 'Top Up' action chip") {
|
||||
onTangemPayMainScreen { topUpButton.clickWithAssertion() }
|
||||
}
|
||||
step("Assert 'Add Funds' sheet is displayed") {
|
||||
onTangemPayAddFundsSheet { title.assertIsDisplayed() }
|
||||
}
|
||||
step("Click on 'Swap' option") {
|
||||
onTangemPayAddFundsSheet { swapOption.clickWithAssertion() }
|
||||
}
|
||||
step("Click on 'Close' button on Swap stories") {
|
||||
onSwapStoriesScreen { closeButton.performClick() }
|
||||
}
|
||||
step("Assert 'Swap' screen is displayed (USDC pre-filled as destination)") {
|
||||
onSwapTokenScreen { title.assertIsDisplayed() }
|
||||
}
|
||||
step("Click on 'Choose token' button (from)") {
|
||||
onSwapTokenScreen { chooseTokenButton.clickWithAssertion() }
|
||||
}
|
||||
step("Click on 'Main account'") {
|
||||
onSwapSelectTokenScreen { tokenWithName("Main account").clickWithAssertion() }
|
||||
}
|
||||
step("Click on token 'Bitcoin'") {
|
||||
waitForIdle()
|
||||
onSwapSelectTokenScreen { tokenWithName("Bitcoin").clickWithAssertion() }
|
||||
}
|
||||
step("Enter swap amount '$swapFromAmount'") {
|
||||
onSwapTokenScreen {
|
||||
textInput.performClick()
|
||||
textInput.performTextReplacement(swapFromAmount)
|
||||
}
|
||||
}
|
||||
step("Dismiss keyboard") {
|
||||
Espresso.closeSoftKeyboard()
|
||||
waitForIdle()
|
||||
}
|
||||
step("Wait until provider quote + fee are loaded") {
|
||||
onSwapTokenScreen {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
networkFeeBlock.assertIsDisplayed()
|
||||
feeAmount.assertIsDisplayed()
|
||||
}
|
||||
}
|
||||
}
|
||||
step("Confirm swap by holding the button") {
|
||||
confirmSwapByHolding(accessCode = TANGEM_PAY_ACCESS_CODE)
|
||||
}
|
||||
step("Wait for 'Swap in progress' screen") {
|
||||
onSwapSuccessScreen {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_VERY_LONG) { title.assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
step("Click on 'Close' button") {
|
||||
onSwapSuccessScreen { closeButton.performClick() }
|
||||
}
|
||||
step("Switch WireMock scenario '$balanceScenario' to '$balanceAfterState'") {
|
||||
setWireMockScenarioState(balanceScenario, balanceAfterState)
|
||||
}
|
||||
step("Switch WireMock scenario '$historyScenario' to '$historyAfterState'") {
|
||||
setWireMockScenarioState(historyScenario, historyAfterState)
|
||||
}
|
||||
step("Pull to refresh Tangem Pay") { pullToRefreshTangemPay() }
|
||||
step("Assert balance updated to '\$110.00'") {
|
||||
onTangemPayMainScreen {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
balance.assertTextContainsSafe("110", substring = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
step("Assert '$depositLabel' transaction visible in history") {
|
||||
onTangemPayMainScreen {
|
||||
transactionRowWithText(depositLabel).assertIsDisplayed()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
package com.tangem.tests.tangempay
|
||||
|
||||
import androidx.test.espresso.Espresso
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.common.constants.TestConstants.TANGEM_PAY_ACCESS_CODE
|
||||
import com.tangem.common.constants.TestConstants.TANGEM_PAY_ELIGIBILITY_SCENARIO
|
||||
import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT_LONG
|
||||
import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT_VERY_LONG
|
||||
import com.tangem.common.extensions.assertTextContainsSafe
|
||||
import com.tangem.common.extensions.clickWithAssertion
|
||||
import com.tangem.common.utils.resetWireMockScenarioState
|
||||
import com.tangem.common.utils.setWireMockScenarioState
|
||||
import com.tangem.core.res.R as CoreResR
|
||||
import com.tangem.scenarios.*
|
||||
import com.tangem.screens.*
|
||||
import com.tangem.screens.tangempay.*
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import io.github.kakaocup.kakao.common.utilities.getResourceString
|
||||
import io.qameta.allure.kotlin.AllureId
|
||||
import io.qameta.allure.kotlin.junit4.DisplayName
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
|
||||
@HiltAndroidTest
|
||||
class TangemPayWithdrawTest : BaseTestCase() {
|
||||
|
||||
@AllureId("4972")
|
||||
@DisplayName("Tangem Pay: withdraw swaps USDC to Bitcoin and appends withdrawal to history")
|
||||
@Ignore("[REDACTED_JIRA]")
|
||||
@Test
|
||||
fun withdrawFromTangemPay_SwapsUSDCToBitcoin_AppendsWithdrawalToHistory() {
|
||||
val bitcoinScenario = "bitcoin_utxo"
|
||||
val expressAssetsScenario = "express_api_assets"
|
||||
val exchangeStatusScenario = "exchange_status_provider"
|
||||
val balanceScenario = "tangem_pay_balance_update"
|
||||
val historyScenario = "tangem_pay_transaction_history"
|
||||
val eligibilityState = "PaeraCustomer"
|
||||
val bitcoinStartedState = "Started"
|
||||
val expressAssetsState = "BitcoinExchangeEnabled"
|
||||
val exchangeStatusState = "Changelly"
|
||||
val balanceInitialState = "InitialBalance"
|
||||
val balanceAfterState = "AfterWithdraw"
|
||||
val historyInitialState = "InitialEmpty"
|
||||
val historyAfterState = "AfterWithdraw"
|
||||
val withdrawAmount = "5"
|
||||
val withdrawalLabel = getResourceString(CoreResR.string.tangem_pay_withdrawal)
|
||||
|
||||
setupHooks(
|
||||
additionalBeforeSection = {
|
||||
setWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO, eligibilityState)
|
||||
setWireMockScenarioState(bitcoinScenario, bitcoinStartedState)
|
||||
setWireMockScenarioState(expressAssetsScenario, expressAssetsState)
|
||||
setWireMockScenarioState(exchangeStatusScenario, exchangeStatusState)
|
||||
setWireMockScenarioState(balanceScenario, balanceInitialState)
|
||||
setWireMockScenarioState(historyScenario, historyInitialState)
|
||||
},
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(TANGEM_PAY_ELIGIBILITY_SCENARIO)
|
||||
resetWireMockScenarioState(bitcoinScenario)
|
||||
resetWireMockScenarioState(expressAssetsScenario)
|
||||
resetWireMockScenarioState(exchangeStatusScenario)
|
||||
resetWireMockScenarioState(balanceScenario)
|
||||
resetWireMockScenarioState(historyScenario)
|
||||
},
|
||||
).run {
|
||||
openTangemPay()
|
||||
step("Assert initial balance contains '10'") {
|
||||
onTangemPayMainScreen { balance.assertTextContainsSafe("10", substring = true) }
|
||||
}
|
||||
step("Click on 'Withdraw' action chip") {
|
||||
onTangemPayMainScreen { withdrawButton.clickWithAssertion() }
|
||||
}
|
||||
step("Acknowledge withdrawal note sheet") {
|
||||
onTangemPayWithdrawNoteSheet {
|
||||
title.assertIsDisplayed()
|
||||
gotItButton.clickWithAssertion()
|
||||
}
|
||||
}
|
||||
step("Click on 'Close' button on Swap stories") {
|
||||
onSwapStoriesScreen { closeButton.performClick() }
|
||||
}
|
||||
step("Assert 'Swap' screen is displayed (USDC pre-filled as source)") {
|
||||
onSwapTokenScreen { title.assertIsDisplayed() }
|
||||
}
|
||||
step("Click on 'Choose token' button (to)") {
|
||||
onSwapTokenScreen { chooseTokenButton.clickWithAssertion() }
|
||||
}
|
||||
step("Click on 'Main account'") {
|
||||
onSwapSelectTokenScreen { tokenWithName("Main account").clickWithAssertion() }
|
||||
}
|
||||
step("Click on token 'Bitcoin'") {
|
||||
waitForIdle()
|
||||
onSwapSelectTokenScreen { tokenWithName("Bitcoin").clickWithAssertion() }
|
||||
}
|
||||
step("Enter withdraw amount '$withdrawAmount'") {
|
||||
onSwapTokenScreen {
|
||||
textInput.performClick()
|
||||
textInput.performTextReplacement(withdrawAmount)
|
||||
}
|
||||
}
|
||||
step("Dismiss keyboard") {
|
||||
Espresso.closeSoftKeyboard()
|
||||
waitForIdle()
|
||||
}
|
||||
step("Wait until network fee row is rendered (HoldToConfirm enabled)") {
|
||||
onSwapTokenScreen {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) { networkFeeTitle.assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
step("Confirm swap by holding the button") {
|
||||
confirmSwapByHolding(accessCode = TANGEM_PAY_ACCESS_CODE)
|
||||
}
|
||||
step("Wait for 'Swap in progress' screen") {
|
||||
onSwapSuccessScreen {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_VERY_LONG) { title.assertIsDisplayed() }
|
||||
}
|
||||
}
|
||||
step("Click on 'Close' button") {
|
||||
onSwapSuccessScreen { closeButton.performClick() }
|
||||
}
|
||||
step("Switch WireMock scenario '$balanceScenario' to '$balanceAfterState'") {
|
||||
setWireMockScenarioState(balanceScenario, balanceAfterState)
|
||||
}
|
||||
step("Switch WireMock scenario '$historyScenario' to '$historyAfterState'") {
|
||||
setWireMockScenarioState(historyScenario, historyAfterState)
|
||||
}
|
||||
step("Pull to refresh Tangem Pay") { pullToRefreshTangemPay() }
|
||||
step("Assert balance updated to '\$5.00'") {
|
||||
onTangemPayMainScreen {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT_LONG) {
|
||||
balance.assertTextContainsSafe("5", substring = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
step("Assert '$withdrawalLabel' transaction visible in history") {
|
||||
onTangemPayMainScreen {
|
||||
transactionRowWithText(withdrawalLabel).assertIsDisplayed()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +1,28 @@
|
|||
package com.tangem.tests
|
||||
package com.tangem.tests.walletConnect
|
||||
|
||||
import android.Manifest
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT
|
||||
import com.tangem.common.extensions.clickWithAssertion
|
||||
import com.tangem.common.constants.TestConstants
|
||||
import com.tangem.common.utils.getWcUri
|
||||
import com.tangem.common.utils.setClipboardText
|
||||
import com.tangem.scenarios.*
|
||||
import com.tangem.screens.onWalletConnectBottomSheet
|
||||
import com.tangem.screens.onWalletConnectDetailsBottomSheet
|
||||
import com.tangem.screens.onScanQrScreen
|
||||
import com.tangem.screens.onWalletConnectScreen
|
||||
import com.tangem.wallet.BuildConfig
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import io.qameta.allure.kotlin.AllureId
|
||||
import io.qameta.allure.kotlin.junit4.DisplayName
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
|
||||
@HiltAndroidTest
|
||||
class WalletConnectTest : BaseTestCase() {
|
||||
class EthereumWalletConnectTest : BaseTestCase() {
|
||||
|
||||
@AllureId("3958")
|
||||
@DisplayName("WC (React App): open session from deeplink on main screen")
|
||||
@Ignore("TODO [REDACTED_JIRA] React app deeplink doesn't work")
|
||||
@Test
|
||||
fun openWalletConnectSessionOnMainScreenTest() {
|
||||
val dAppName = "React App"
|
||||
val dAppName = "Tangem QA Tools"
|
||||
val deepLinkUri = getWcUri()
|
||||
|
||||
setupHooks().run {
|
||||
|
|
@ -36,30 +32,25 @@ class WalletConnectTest : BaseTestCase() {
|
|||
step("Synchronize addresses") {
|
||||
synchronizeAddresses()
|
||||
}
|
||||
step("Create WC session buy deeplink") {
|
||||
step("Create WC session by deeplink") {
|
||||
openAppByDeepLink(deepLinkUri)
|
||||
}
|
||||
step("Check 'Wallet Connect' bottom sheet") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
flakySafely(TestConstants.WAIT_UNTIL_TIMEOUT) {
|
||||
checkWalletConnectBottomSheet()
|
||||
}
|
||||
}
|
||||
step("Assert 'Connect' button is enabled") {
|
||||
onWalletConnectBottomSheet { connectButton.assertIsEnabled() }
|
||||
}
|
||||
step("Click on 'Connect' button") {
|
||||
waitForIdle()
|
||||
onWalletConnectBottomSheet { connectButton.performClick() }
|
||||
}
|
||||
step("Assert 'Connect' button is not displayed") {
|
||||
waitForIdle()
|
||||
onWalletConnectBottomSheet { connectButton.assertIsNotDisplayed() }
|
||||
step("Click on 'Connect' button and dismiss 'Unknown domain' alert if shown") {
|
||||
confirmWcConnection()
|
||||
}
|
||||
step("Open 'Wallet Connect' screen") {
|
||||
openWalletConnectScreen()
|
||||
}
|
||||
step("Check 'Wallet Connect' screen with connections") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
flakySafely(TestConstants.WAIT_UNTIL_TIMEOUT) {
|
||||
checkWalletConnectScreen(withConnections = true)
|
||||
}
|
||||
}
|
||||
|
|
@ -80,10 +71,9 @@ class WalletConnectTest : BaseTestCase() {
|
|||
|
||||
@AllureId("3959")
|
||||
@DisplayName("WC (React App): open session from deeplink not on main screen")
|
||||
@Ignore("TODO [REDACTED_JIRA] React app deeplink doesn't work")
|
||||
@Test
|
||||
fun openWalletConnectSessionNotOnMainScreenTest() {
|
||||
val dAppName = "React App"
|
||||
val dAppName = "Tangem QA Tools"
|
||||
val deepLinkUri = getWcUri()
|
||||
|
||||
setupHooks().run {
|
||||
|
|
@ -97,24 +87,19 @@ class WalletConnectTest : BaseTestCase() {
|
|||
openWalletConnectScreen()
|
||||
checkWalletConnectScreen(false)
|
||||
}
|
||||
step("Create WC session buy deeplink") {
|
||||
step("Create WC session by deeplink") {
|
||||
openAppByDeepLink(deepLinkUri)
|
||||
}
|
||||
step("Check 'Wallet Connect' bottom sheet") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
flakySafely(TestConstants.WAIT_UNTIL_TIMEOUT) {
|
||||
checkWalletConnectBottomSheet()
|
||||
}
|
||||
}
|
||||
step("Click on 'Connect' button") {
|
||||
waitForIdle()
|
||||
onWalletConnectBottomSheet { connectButton.performClick() }
|
||||
}
|
||||
step("Assert 'Connect' button is not displayed") {
|
||||
waitForIdle()
|
||||
onWalletConnectBottomSheet { connectButton.assertIsNotDisplayed() }
|
||||
step("Click on 'Connect' button and dismiss 'Unknown domain' alert if shown") {
|
||||
confirmWcConnection()
|
||||
}
|
||||
step("Check 'Wallet Connect' screen with connections") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
flakySafely(TestConstants.WAIT_UNTIL_TIMEOUT) {
|
||||
checkWalletConnectScreen(withConnections = true)
|
||||
}
|
||||
}
|
||||
|
|
@ -122,7 +107,7 @@ class WalletConnectTest : BaseTestCase() {
|
|||
onWalletConnectScreen { appIcon.performClick() }
|
||||
}
|
||||
step("Check 'Wallet Connect' details bottom sheet") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
flakySafely(TestConstants.WAIT_UNTIL_TIMEOUT) {
|
||||
checkWalletConnectDetailsBottomSheet(dAppName)
|
||||
}
|
||||
}
|
||||
|
|
@ -136,11 +121,10 @@ class WalletConnectTest : BaseTestCase() {
|
|||
}
|
||||
|
||||
@AllureId("3957")
|
||||
@DisplayName("WC (React App): open session from deeplink ")
|
||||
@Ignore("TODO [REDACTED_JIRA] React app deeplink doesn't work")
|
||||
@DisplayName("WC (React App): open session from deeplink")
|
||||
@Test
|
||||
fun openWalletConnectSessionTest() {
|
||||
val dAppName = "React App"
|
||||
val dAppName = "Tangem QA Tools"
|
||||
val packageName = BuildConfig.APPLICATION_ID
|
||||
val deepLinkUri = getWcUri()
|
||||
|
||||
|
|
@ -154,19 +138,16 @@ class WalletConnectTest : BaseTestCase() {
|
|||
step("Kill app") {
|
||||
device.apps.kill(packageName)
|
||||
}
|
||||
step("Create WC session buy deeplink") {
|
||||
step("Create WC session by deeplink") {
|
||||
openAppByDeepLink(deepLinkUri)
|
||||
}
|
||||
step("Check 'Wallet Connect' bottom sheet") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
flakySafely(TestConstants.WAIT_UNTIL_TIMEOUT) {
|
||||
checkWalletConnectBottomSheet()
|
||||
}
|
||||
}
|
||||
step("Click on 'Connect' button") {
|
||||
onWalletConnectBottomSheet { connectButton.performClick() }
|
||||
}
|
||||
step("Assert 'Connect' button is not displayed") {
|
||||
onWalletConnectBottomSheet { connectButton.assertIsNotDisplayed() }
|
||||
step("Click on 'Connect' button and dismiss 'Unknown domain' alert if shown") {
|
||||
confirmWcConnection()
|
||||
}
|
||||
step("Open 'Wallet Connect' screen") {
|
||||
openWalletConnectScreen()
|
||||
|
|
@ -191,10 +172,9 @@ class WalletConnectTest : BaseTestCase() {
|
|||
|
||||
@AllureId("887")
|
||||
@DisplayName("WC: open session by 'Paste from clipboard' button")
|
||||
@Ignore("TODO [REDACTED_JIRA] React app deeplink doesn't work")
|
||||
@Test
|
||||
fun openWalletConnectSessionByClipboardLinkTest() {
|
||||
val dAppName = "React App"
|
||||
val dAppName = "Tangem QA Tools"
|
||||
val context = device.context
|
||||
val deepLinkUri = getWcUri()
|
||||
val packageName = BuildConfig.APPLICATION_ID
|
||||
|
|
@ -217,25 +197,17 @@ class WalletConnectTest : BaseTestCase() {
|
|||
step("Open 'Wallet Connect' screen") {
|
||||
openWalletConnectScreen()
|
||||
}
|
||||
step("Click 'New connection' button") {
|
||||
onWalletConnectScreen { newConnectionButton.performClick() }
|
||||
}
|
||||
step("CLick 'Paste from clipboard' button") {
|
||||
onScanQrScreen { pasteFromClipboardButton.clickWithAssertion() }
|
||||
step("Create connection via 'Paste from clipboard' button") {
|
||||
createConnectionViaPasteFromClipboardButton()
|
||||
}
|
||||
step("Check 'Wallet Connect' bottom sheet") {
|
||||
waitForIdle()
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
flakySafely(TestConstants.WAIT_UNTIL_TIMEOUT) {
|
||||
checkWalletConnectBottomSheet()
|
||||
}
|
||||
}
|
||||
step("Click on 'Connect' button") {
|
||||
waitForIdle()
|
||||
onWalletConnectBottomSheet { connectButton.performClick() }
|
||||
}
|
||||
step("Assert 'Connect' button is not displayed") {
|
||||
waitForIdle()
|
||||
onWalletConnectBottomSheet { connectButton.assertIsNotDisplayed() }
|
||||
step("Click on 'Connect' button and dismiss 'Unknown domain' alert if shown") {
|
||||
confirmWcConnection()
|
||||
}
|
||||
step("Check 'Wallet Connect' screen with connections") {
|
||||
checkWalletConnectScreen(withConnections = true)
|
||||
|
|
@ -0,0 +1,272 @@
|
|||
package com.tangem.tests.walletConnect
|
||||
|
||||
import android.Manifest
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.common.constants.TestConstants
|
||||
import com.tangem.common.constants.TestConstants.USER_TOKENS_API_SCENARIO
|
||||
import com.tangem.common.utils.getWcUri
|
||||
import com.tangem.common.utils.resetWireMockScenarioState
|
||||
import com.tangem.common.utils.setClipboardText
|
||||
import com.tangem.common.utils.setWireMockScenarioState
|
||||
import com.tangem.scenarios.*
|
||||
import com.tangem.screens.onWalletConnectBottomSheet
|
||||
import com.tangem.screens.onWalletConnectDetailsBottomSheet
|
||||
import com.tangem.screens.onWalletConnectScreen
|
||||
import com.tangem.wallet.BuildConfig
|
||||
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 SolanaWalletConnectTest : BaseTestCase() {
|
||||
|
||||
@AllureId("4023")
|
||||
@DisplayName("WC (Raydium): open session from deeplink on main screen")
|
||||
@Test
|
||||
fun openWalletConnectSessionOnMainScreenTest() {
|
||||
val dAppName = "Tangem QA Tools"
|
||||
val deepLinkUri = getWcUri("solana")
|
||||
val scenarioState = "Solana"
|
||||
|
||||
setupHooks(
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(USER_TOKENS_API_SCENARIO)
|
||||
}
|
||||
).run {
|
||||
|
||||
step("Set WireMock scenario: '$USER_TOKENS_API_SCENARIO' to state: '$scenarioState'") {
|
||||
setWireMockScenarioState(USER_TOKENS_API_SCENARIO, scenarioState)
|
||||
}
|
||||
|
||||
step("Open 'Main Screen'") {
|
||||
openMainScreen()
|
||||
}
|
||||
step("Synchronize addresses") {
|
||||
synchronizeAddresses()
|
||||
}
|
||||
step("Create WC session by deeplink") {
|
||||
openAppByDeepLink(deepLinkUri)
|
||||
}
|
||||
step("Check 'Wallet Connect' bottom sheet") {
|
||||
flakySafely(TestConstants.WAIT_UNTIL_TIMEOUT) {
|
||||
checkWalletConnectBottomSheet()
|
||||
}
|
||||
}
|
||||
step("Assert 'Connect' button is enabled") {
|
||||
onWalletConnectBottomSheet { connectButton.assertIsEnabled() }
|
||||
}
|
||||
step("Click on 'Connect' button and dismiss 'Unknown domain' alert if shown") {
|
||||
confirmWcConnection()
|
||||
}
|
||||
step("Open 'Wallet Connect' screen") {
|
||||
openWalletConnectScreen()
|
||||
}
|
||||
step("Check 'Wallet Connect' screen with connections") {
|
||||
flakySafely(TestConstants.WAIT_UNTIL_TIMEOUT) {
|
||||
checkWalletConnectScreen(withConnections = true)
|
||||
}
|
||||
}
|
||||
step("Click on app icon") {
|
||||
onWalletConnectScreen { appIcon.performClick() }
|
||||
}
|
||||
step("Check 'Wallet Connect' details bottom sheet") {
|
||||
checkWalletConnectDetailsBottomSheet(dAppName)
|
||||
}
|
||||
step("Click on 'Disconnect' button") {
|
||||
onWalletConnectDetailsBottomSheet { disconnectButton.performClick() }
|
||||
}
|
||||
step("Check 'Wallet Connect' screen without connections") {
|
||||
checkWalletConnectScreen(withConnections = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("4024")
|
||||
@DisplayName("WC (Raydium): open session from deeplink not on main screen")
|
||||
@Test
|
||||
fun openWalletConnectSessionNotOnMainScreenTest() {
|
||||
val dAppName = "Tangem QA Tools"
|
||||
val deepLinkUri = getWcUri("solana")
|
||||
val scenarioState = "Solana"
|
||||
|
||||
setupHooks(
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(USER_TOKENS_API_SCENARIO)
|
||||
}
|
||||
).run {
|
||||
|
||||
step("Set WireMock scenario: '$USER_TOKENS_API_SCENARIO' to state: '$scenarioState'") {
|
||||
setWireMockScenarioState(USER_TOKENS_API_SCENARIO, scenarioState)
|
||||
}
|
||||
|
||||
step("Open 'Main Screen'") {
|
||||
openMainScreen()
|
||||
}
|
||||
step("Synchronize addresses") {
|
||||
synchronizeAddresses()
|
||||
}
|
||||
step("Open 'Wallet Connect' screen") {
|
||||
openWalletConnectScreen()
|
||||
checkWalletConnectScreen(false)
|
||||
}
|
||||
step("Create WC session by deeplink") {
|
||||
openAppByDeepLink(deepLinkUri)
|
||||
}
|
||||
step("Check 'Wallet Connect' bottom sheet") {
|
||||
flakySafely(TestConstants.WAIT_UNTIL_TIMEOUT) {
|
||||
checkWalletConnectBottomSheet()
|
||||
}
|
||||
}
|
||||
step("Click on 'Connect' button and dismiss 'Unknown domain' alert if shown") {
|
||||
confirmWcConnection()
|
||||
}
|
||||
step("Check 'Wallet Connect' screen with connections") {
|
||||
flakySafely(TestConstants.WAIT_UNTIL_TIMEOUT) {
|
||||
checkWalletConnectScreen(withConnections = true)
|
||||
}
|
||||
}
|
||||
step("Click on app icon") {
|
||||
onWalletConnectScreen { appIcon.performClick() }
|
||||
}
|
||||
step("Check 'Wallet Connect' details bottom sheet") {
|
||||
flakySafely(TestConstants.WAIT_UNTIL_TIMEOUT) {
|
||||
checkWalletConnectDetailsBottomSheet(dAppName)
|
||||
}
|
||||
}
|
||||
step("Click on 'Disconnect' button") {
|
||||
onWalletConnectDetailsBottomSheet { disconnectButton.performClick() }
|
||||
}
|
||||
step("Check 'Wallet Connect' screen without connections") {
|
||||
checkWalletConnectScreen(withConnections = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("4025")
|
||||
@DisplayName("WC (Raydium): open session from deeplink")
|
||||
@Test
|
||||
fun openWalletConnectSessionTest() {
|
||||
val dAppName = "Tangem QA Tools"
|
||||
val packageName = BuildConfig.APPLICATION_ID
|
||||
val deepLinkUri = getWcUri("solana")
|
||||
val scenarioState = "Solana"
|
||||
|
||||
setupHooks(
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(USER_TOKENS_API_SCENARIO)
|
||||
}
|
||||
).run {
|
||||
|
||||
step("Set WireMock scenario: '$USER_TOKENS_API_SCENARIO' to state: '$scenarioState'") {
|
||||
setWireMockScenarioState(USER_TOKENS_API_SCENARIO, scenarioState)
|
||||
}
|
||||
|
||||
step("Open 'Main Screen'") {
|
||||
openMainScreen()
|
||||
}
|
||||
step("Synchronize addresses") {
|
||||
synchronizeAddresses()
|
||||
}
|
||||
step("Kill app") {
|
||||
device.apps.kill(packageName)
|
||||
}
|
||||
step("Create WC session by deeplink") {
|
||||
openAppByDeepLink(deepLinkUri)
|
||||
}
|
||||
step("Check 'Wallet Connect' bottom sheet") {
|
||||
flakySafely(TestConstants.WAIT_UNTIL_TIMEOUT) {
|
||||
checkWalletConnectBottomSheet()
|
||||
}
|
||||
}
|
||||
step("Click on 'Connect' button and dismiss 'Unknown domain' alert if shown") {
|
||||
confirmWcConnection()
|
||||
}
|
||||
step("Open 'Wallet Connect' screen") {
|
||||
openWalletConnectScreen()
|
||||
}
|
||||
step("Check 'Wallet Connect' screen with connections") {
|
||||
checkWalletConnectScreen(withConnections = true)
|
||||
}
|
||||
step("Click on app icon") {
|
||||
onWalletConnectScreen { appIcon.performClick() }
|
||||
}
|
||||
step("Check 'Wallet Connect' details bottom sheet") {
|
||||
checkWalletConnectDetailsBottomSheet(dAppName)
|
||||
}
|
||||
step("Click on 'Disconnect' button") {
|
||||
onWalletConnectDetailsBottomSheet { disconnectButton.performClick() }
|
||||
}
|
||||
step("Check 'Wallet Connect' screen without connections") {
|
||||
checkWalletConnectScreen(withConnections = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("4026")
|
||||
@DisplayName("WC: open session by 'Paste from clipboard' button")
|
||||
@Test
|
||||
fun openWalletConnectSessionByClipboardLinkTest() {
|
||||
val dAppName = "Tangem QA Tools"
|
||||
val context = device.context
|
||||
val deepLinkUri = getWcUri("solana")
|
||||
val scenarioState = "Solana"
|
||||
val packageName = BuildConfig.APPLICATION_ID
|
||||
val permissionName = Manifest.permission.CAMERA
|
||||
|
||||
setupHooks(
|
||||
additionalBeforeSection = {
|
||||
device.uiDevice.executeShellCommand("pm grant $packageName $permissionName")
|
||||
|
||||
},
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(USER_TOKENS_API_SCENARIO)
|
||||
}
|
||||
).run {
|
||||
|
||||
step("Set WireMock scenario: '$USER_TOKENS_API_SCENARIO' to state: '$scenarioState'") {
|
||||
setWireMockScenarioState(USER_TOKENS_API_SCENARIO, scenarioState)
|
||||
}
|
||||
|
||||
step("Set URI to clipboard") {
|
||||
setClipboardText(context, deepLinkUri)
|
||||
}
|
||||
step("Open 'Main Screen'") {
|
||||
openMainScreen()
|
||||
}
|
||||
step("Synchronize addresses") {
|
||||
synchronizeAddresses()
|
||||
}
|
||||
step("Open 'Wallet Connect' screen") {
|
||||
openWalletConnectScreen()
|
||||
}
|
||||
step("Create connection via 'Paste from clipboard' button") {
|
||||
createConnectionViaPasteFromClipboardButton()
|
||||
}
|
||||
step("Check 'Wallet Connect' bottom sheet") {
|
||||
waitForIdle()
|
||||
flakySafely(TestConstants.WAIT_UNTIL_TIMEOUT) {
|
||||
checkWalletConnectBottomSheet()
|
||||
}
|
||||
}
|
||||
step("Click on 'Connect' button and dismiss 'Unknown domain' alert if shown") {
|
||||
confirmWcConnection()
|
||||
}
|
||||
step("Check 'Wallet Connect' screen with connections") {
|
||||
checkWalletConnectScreen(withConnections = true)
|
||||
}
|
||||
step("Click on app icon") {
|
||||
onWalletConnectScreen { appIcon.performClick() }
|
||||
}
|
||||
step("Check 'Wallet Connect' details bottom sheet") {
|
||||
checkWalletConnectDetailsBottomSheet(dAppName)
|
||||
}
|
||||
step("Click on 'Disconnect' button") {
|
||||
onWalletConnectDetailsBottomSheet { disconnectButton.performClick() }
|
||||
}
|
||||
step("Check 'Wallet Connect' screen without connections") {
|
||||
checkWalletConnectScreen(withConnections = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
package com.tangem.tests.walletConnect
|
||||
|
||||
import android.Manifest
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.common.constants.TestConstants
|
||||
import com.tangem.common.extensions.assertSnackbarWithText
|
||||
import com.tangem.common.extensions.clickWithAssertion
|
||||
import com.tangem.common.utils.getWcUri
|
||||
import com.tangem.common.utils.setClipboardText
|
||||
import com.tangem.scenarios.*
|
||||
import com.tangem.screens.onWarningBottomSheet
|
||||
import com.tangem.wallet.BuildConfig
|
||||
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 WalletConnectTest : BaseTestCase() {
|
||||
|
||||
@AllureId("9037")
|
||||
@DisplayName("WC: invalid wallet connect link")
|
||||
@Test
|
||||
fun invalidWalletConnectLinkTest() {
|
||||
val context = device.context
|
||||
val deepLinkUri = "wc:384617d590a47f11c26311b5cf2418859682920aa0ad52"
|
||||
val packageName = BuildConfig.APPLICATION_ID
|
||||
val permissionName = Manifest.permission.CAMERA
|
||||
|
||||
setupHooks(
|
||||
additionalBeforeSection = {
|
||||
device.uiDevice.executeShellCommand("pm grant $packageName $permissionName")
|
||||
},
|
||||
).run {
|
||||
step("Set URI to clipboard") {
|
||||
setClipboardText(context, deepLinkUri)
|
||||
}
|
||||
step("Open 'Main Screen'") {
|
||||
openMainScreen()
|
||||
}
|
||||
step("Synchronize addresses") {
|
||||
synchronizeAddresses()
|
||||
}
|
||||
step("Open 'Wallet Connect' screen") {
|
||||
openWalletConnectScreen()
|
||||
}
|
||||
step("Create connection via 'Paste from clipboard' button") {
|
||||
createConnectionViaPasteFromClipboardButton()
|
||||
}
|
||||
step("Assert error snackbar about invalid WC URI is displayed") {
|
||||
assertSnackbarWithText("getUserInfo")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("9040")
|
||||
@DisplayName("WC (React App): repeat open/close session")
|
||||
@Test
|
||||
fun repeatedConnectByWalletConnectDeeplinkScreenTest() {
|
||||
val dAppName = "Tangem QA Tools"
|
||||
val context = device.context
|
||||
val packageName = BuildConfig.APPLICATION_ID
|
||||
val permissionName = Manifest.permission.CAMERA
|
||||
val sessionsCount = 3
|
||||
|
||||
setupHooks(
|
||||
additionalBeforeSection = {
|
||||
device.uiDevice.executeShellCommand("pm grant $packageName $permissionName")
|
||||
},
|
||||
).run {
|
||||
step("Open 'Main Screen'") {
|
||||
openMainScreen()
|
||||
}
|
||||
step("Synchronize addresses") {
|
||||
synchronizeAddresses()
|
||||
}
|
||||
step("Open 'Wallet Connect' screen") {
|
||||
openWalletConnectScreen()
|
||||
}
|
||||
repeat(sessionsCount) { iteration ->
|
||||
step("Session #${iteration + 1}: connect and disconnect") {
|
||||
establishAndDisconnectWcSession(
|
||||
context = context,
|
||||
deepLinkUri = getWcUri(),
|
||||
dAppName = dAppName,
|
||||
)
|
||||
}
|
||||
step("Check 'Wallet Connect' screen without connections") {
|
||||
checkWalletConnectScreen(withConnections = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("9066")
|
||||
@DisplayName("WC: connect to unsupported dApp shows error")
|
||||
@Test
|
||||
fun connectToUnsupportedDAppShowsUnsupportedErrorTest() {
|
||||
val unsupportedDAppUrl = "https://dydx.trade/test"
|
||||
val dAppName = "dYdX"
|
||||
val context = device.context
|
||||
val packageName = BuildConfig.APPLICATION_ID
|
||||
val permissionName = Manifest.permission.CAMERA
|
||||
|
||||
setupHooks(
|
||||
additionalBeforeSection = {
|
||||
device.uiDevice.executeShellCommand("pm grant $packageName $permissionName")
|
||||
},
|
||||
).run {
|
||||
step("Open 'Main Screen'") {
|
||||
openMainScreen()
|
||||
}
|
||||
step("Synchronize addresses") {
|
||||
synchronizeAddresses()
|
||||
}
|
||||
step("Open 'Wallet Connect' screen") {
|
||||
openWalletConnectScreen()
|
||||
}
|
||||
step("Set unsupported dApp URI to clipboard") {
|
||||
setClipboardText(
|
||||
context = context,
|
||||
text = getWcUri(dAppUrl = unsupportedDAppUrl, dAppName = dAppName),
|
||||
)
|
||||
}
|
||||
step("Create connection via 'Paste from clipboard' button") {
|
||||
createConnectionViaPasteFromClipboardButton()
|
||||
}
|
||||
step("Wait for unsupported dApp error bottom sheet") {
|
||||
composeTestRule.waitUntil(timeoutMillis = TestConstants.WAIT_UNTIL_TIMEOUT) {
|
||||
runCatching {
|
||||
onWarningBottomSheet { gotItButton.assertIsDisplayed() }
|
||||
}.isSuccess
|
||||
}
|
||||
}
|
||||
step("Click on 'Got it' button") {
|
||||
onWarningBottomSheet { gotItButton.clickWithAssertion() }
|
||||
}
|
||||
step("Check 'Wallet Connect' screen without connections") {
|
||||
checkWalletConnectScreen(withConnections = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,11 +4,18 @@ import android.os.Bundle
|
|||
import com.huawei.hms.push.HmsMessageService
|
||||
import com.huawei.hms.push.RemoteMessage
|
||||
import com.tangem.google.GoogleServicesHelper
|
||||
import com.tangem.tap.common.pushes.PushMessageHandler
|
||||
import com.tangem.tap.common.pushes.PushNotificationDelegate
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class HuaweiPushService : HmsMessageService() {
|
||||
|
||||
@Inject
|
||||
internal lateinit var pushMessageHandler: PushMessageHandler
|
||||
|
||||
private val pushNotificationDelegate: PushNotificationDelegate by lazy {
|
||||
PushNotificationDelegate(applicationContext)
|
||||
}
|
||||
|
|
@ -27,6 +34,9 @@ class HuaweiPushService : HmsMessageService() {
|
|||
super.onMessageReceived(message)
|
||||
val isGoogleServicesAvailable = GoogleServicesHelper.checkGoogleServicesAvailability(this)
|
||||
if (isGoogleServicesAvailable) return
|
||||
|
||||
message?.dataOfMap?.let(pushMessageHandler::onMessageReceived)
|
||||
|
||||
val notification = message?.notification ?: return
|
||||
val channelId = notification.channelId ?: TANGEM_CHANNEL_ID
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 158fbd8808d2db92ef82d3f9ed92c81340c707c5
|
||||
Subproject commit de8d3eff2b3a4d6b1d2794ce3c17945c86c449bd
|
||||
|
|
@ -19,10 +19,10 @@ import androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode
|
|||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.semantics.testTagsAsResourceId
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.semantics.testTagsAsResourceId
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.lifecycle.flowWithLifecycle
|
||||
|
|
@ -170,7 +170,7 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
|
|||
private val onActivityResultCallbacks = mutableListOf<OnActivityResultCallback>()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
TangemLogger.i("onCreate")
|
||||
TangemLogger.i("onCreate: data=${intent?.data}, extras=${intent?.extras?.keySet()}")
|
||||
// We need to call it before onCreate to prevent unnecessary activity recreation
|
||||
installAppTheme()
|
||||
|
||||
|
|
|
|||
|
|
@ -14,12 +14,14 @@ class AppsFlyerDeepLinkListener @Inject constructor(
|
|||
override fun onDeepLinking(p0: DeepLinkResult) {
|
||||
when (p0.status) {
|
||||
DeepLinkResult.Status.FOUND -> {
|
||||
referralParamsHandler.handle(deepLink = p0.deepLink)
|
||||
referralParamsHandler.handleDeeplink(deepLink = p0.deepLink)
|
||||
}
|
||||
DeepLinkResult.Status.NOT_FOUND -> {
|
||||
referralParamsHandler.handleNoDeeplink()
|
||||
TangemLogger.i("No deep link found")
|
||||
}
|
||||
DeepLinkResult.Status.ERROR -> {
|
||||
referralParamsHandler.handleNoDeeplink()
|
||||
TangemLogger.e("Deep link error: ${p0.error}")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
package com.tangem.tap.common.analytics.appsflyer
|
||||
|
||||
import com.appsflyer.deeplink.DeepLink
|
||||
import com.tangem.datasource.local.appsflyer.AppsFlyerDeeplinkSource
|
||||
import com.tangem.datasource.local.appsflyer.AppsFlyerStore
|
||||
import com.tangem.domain.wallets.models.AppsFlyerConversionData
|
||||
import com.tangem.feature.referral.domain.SetShouldShowMobileWalletPromoUseCase
|
||||
import com.tangem.utils.coroutines.AppCoroutineScope
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
|
|
@ -22,14 +24,7 @@ class AppsFlyerReferralParamsHandler @Inject constructor(
|
|||
) {
|
||||
|
||||
private val mutex = Mutex()
|
||||
|
||||
fun handle(deepLink: DeepLink) {
|
||||
handle(
|
||||
deepLinkValue = deepLink.deepLinkValue,
|
||||
deepLinkSub1 = deepLink.getStringValue(DEEP_LINK_SUB_1),
|
||||
deepLinkSub2 = deepLink.getStringValue(DEEP_LINK_SUB_2),
|
||||
)
|
||||
}
|
||||
private val deepLinkDeferred = CompletableDeferred<String?>()
|
||||
|
||||
fun handle(params: Map<String?, Any?>) {
|
||||
handle(
|
||||
|
|
@ -39,12 +34,48 @@ class AppsFlyerReferralParamsHandler @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun handle(deepLinkValue: String?, deepLinkSub1: String?, deepLinkSub2: String?) {
|
||||
if (deepLinkValue != REFERRAL_DEEP_LINK_VALUE) {
|
||||
TangemLogger.i("Ignoring deep link with value: ${deepLinkValue ?: "null"}")
|
||||
return
|
||||
}
|
||||
fun handleDeeplink(deepLink: DeepLink) {
|
||||
handle(
|
||||
deepLinkValue = deepLink.deepLinkValue,
|
||||
deepLinkSub1 = deepLink.getStringValue(DEEP_LINK_SUB_1),
|
||||
deepLinkSub2 = deepLink.getStringValue(DEEP_LINK_SUB_2),
|
||||
)
|
||||
deepLinkDeferred.complete(deepLink.deepLinkValue)
|
||||
}
|
||||
|
||||
fun handleNoDeeplink() {
|
||||
deepLinkDeferred.complete(null)
|
||||
}
|
||||
|
||||
suspend fun waitForDeeplink(deeplinkSource: AppsFlyerDeeplinkSource): String? {
|
||||
val deeplinkFromCache = appsFlyerStore.getDeeplink(deeplinkSource)
|
||||
return if (deeplinkFromCache == null) {
|
||||
val value = when (deeplinkSource) {
|
||||
AppsFlyerDeeplinkSource.TangemPayHotWalletOnboarding -> TANGEM_PAY_HOT_WALLET_ONBOARDING_DEEP_LINK_VALUE
|
||||
}
|
||||
deepLinkDeferred.await().takeIf { it == value }
|
||||
} else {
|
||||
deeplinkFromCache
|
||||
}
|
||||
}
|
||||
|
||||
private fun handle(deepLinkValue: String?, deepLinkSub1: String?, deepLinkSub2: String?) {
|
||||
TangemLogger.i("AppsFlyer deeplink received: value=$deepLinkValue")
|
||||
when (deepLinkValue) {
|
||||
REFERRAL_DEEP_LINK_VALUE -> handleReferral(deepLinkSub1, deepLinkSub2)
|
||||
TANGEM_PAY_HOT_WALLET_ONBOARDING_DEEP_LINK_VALUE -> handleTangemPayHotWalletOnboarding(deepLinkValue)
|
||||
else -> TangemLogger.i("Ignoring deep link with value: ${deepLinkValue ?: "null"}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleTangemPayHotWalletOnboarding(deepLinkValue: String) {
|
||||
coroutineScope.launch {
|
||||
appsFlyerStore.storeDeeplink(AppsFlyerDeeplinkSource.TangemPayHotWalletOnboarding, deepLinkValue)
|
||||
TangemLogger.i("[TangemPay][HWO] Deep link stored")
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleReferral(deepLinkSub1: String?, deepLinkSub2: String?) {
|
||||
@Suppress("NullableToStringCall")
|
||||
TangemLogger.i("refcode=$deepLinkSub1\ncampaign=$deepLinkSub2")
|
||||
|
||||
|
|
@ -80,6 +111,8 @@ class AppsFlyerReferralParamsHandler @Inject constructor(
|
|||
private companion object {
|
||||
|
||||
const val REFERRAL_DEEP_LINK_VALUE = "referral"
|
||||
const val TANGEM_PAY_HOT_WALLET_ONBOARDING_DEEP_LINK_VALUE = "tpay_mobileonboard"
|
||||
|
||||
const val DEEP_LINK_VALUE = "deep_link_value"
|
||||
const val DEEP_LINK_SUB_1 = "deep_link_sub1"
|
||||
const val DEEP_LINK_SUB_2 = "deep_link_sub2"
|
||||
|
|
|
|||
|
|
@ -66,12 +66,6 @@ sealed class AnalyticsParam {
|
|||
data object BlockchainSdk : Error("Blockchain Sdk Error")
|
||||
}
|
||||
|
||||
sealed class WalletCreationType(val value: String) {
|
||||
data object PrivateKey : WalletCreationType(value = "Private Key")
|
||||
data object NewSeed : WalletCreationType(value = "New Seed")
|
||||
data object SeedImport : WalletCreationType(value = "Seed Import")
|
||||
}
|
||||
|
||||
sealed class AppTheme(val value: String) {
|
||||
data object System : AppTheme("System")
|
||||
data object Dark : AppTheme("Dark")
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
package com.tangem.tap.common.analytics.events
|
||||
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
sealed class Onboarding(
|
||||
category: String,
|
||||
event: String,
|
||||
params: Map<String, String> = emptyMap(),
|
||||
) : AnalyticsEvent(category, event, params) {
|
||||
|
||||
class Finished : Onboarding("Onboarding", "Onboarding Finished")
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
package com.tangem.tap.common.analytics.events
|
||||
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
sealed class SignIn(
|
||||
event: String,
|
||||
params: Map<String, String> = emptyMap(),
|
||||
) : AnalyticsEvent("Sign In", event, params) {
|
||||
|
||||
class ButtonBiometricSignIn : SignIn(event = "Button - Biometric Sign In")
|
||||
class ButtonCardSignIn : SignIn(event = "Button - Card Sign In")
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.tap.common.libs.blockchainsdk
|
||||
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import com.tangem.Message
|
||||
import com.tangem.TangemSdk
|
||||
import com.tangem.blockchain.common.TransactionSigner
|
||||
|
|
@ -7,22 +8,70 @@ import com.tangem.core.analytics.models.Basic.TransactionSent.WalletForm
|
|||
import com.tangem.core.analytics.store.LastSignedWalletFormStore
|
||||
import com.tangem.data.card.TransactionSignerFactory
|
||||
import com.tangem.domain.card.models.TwinKey
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.common.wallets.update
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.tap.domain.TangemSigner
|
||||
import com.tangem.tap.domain.TangemSignerResponse
|
||||
import com.tangem.utils.coroutines.AppCoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
internal class DefaultTransactionSignerFactory(
|
||||
private val lastSignedWalletFormStore: LastSignedWalletFormStore,
|
||||
private val userWalletsListRepository: UserWalletsListRepository,
|
||||
private val coroutineScope: AppCoroutineScope,
|
||||
) : TransactionSignerFactory {
|
||||
|
||||
override fun createTransactionSigner(cardId: String?, sdk: TangemSdk, twinKey: TwinKey?): TransactionSigner {
|
||||
override fun createTransactionSigner(
|
||||
cardId: String?,
|
||||
sdk: TangemSdk,
|
||||
twinKey: TwinKey?,
|
||||
userWalletId: UserWalletId,
|
||||
): TransactionSigner {
|
||||
return TangemSigner(
|
||||
cardId = cardId,
|
||||
tangemSdk = sdk,
|
||||
initialMessage = Message(),
|
||||
twinKey = twinKey,
|
||||
) { signResponse ->
|
||||
lastSignedWalletFormStore.update(
|
||||
if (signResponse.isRing) WalletForm.Ring else WalletForm.Card,
|
||||
)
|
||||
onSignerResponse(userWalletId, signResponse)
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal fun onSignerResponse(userWalletId: UserWalletId, signResponse: TangemSignerResponse) {
|
||||
lastSignedWalletFormStore.update(
|
||||
if (signResponse.isRing) WalletForm.Ring else WalletForm.Card,
|
||||
)
|
||||
|
||||
coroutineScope.launch {
|
||||
userWalletsListRepository.update(userWalletId) { userWallet ->
|
||||
userWallet.updateSignedHashes(signResponse)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun UserWallet.updateSignedHashes(signResponse: TangemSignerResponse): UserWallet {
|
||||
if (this !is UserWallet.Cold) return this
|
||||
|
||||
return copy(
|
||||
scanResponse = scanResponse.copy(
|
||||
card = scanResponse.card.copy(
|
||||
wallets = scanResponse.card.wallets.map { wallet ->
|
||||
if (wallet.publicKey.contentEquals(signResponse.signedWalletPublicKey)) {
|
||||
wallet.copy(
|
||||
// Keep previously known counters if the signer response does not provide them,
|
||||
// otherwise we would regress the UI counters to null.
|
||||
totalSignedHashes = signResponse.totalSignedHashes ?: wallet.totalSignedHashes,
|
||||
remainingSignatures = signResponse.remainingSignatures ?: wallet.remainingSignatures,
|
||||
)
|
||||
} else {
|
||||
wallet
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package com.tangem.tap.common.pushes
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.core.net.toUri
|
||||
import com.tangem.common.routing.DeepLinkRoute
|
||||
import com.tangem.common.routing.deeplink.PayloadToDeeplinkConverter
|
||||
import com.tangem.utils.extensions.uriValidate
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Routes pushes received while the app is running to the matching in-app handler.
|
||||
*
|
||||
* Converts the push payload to a deeplink (via [PayloadToDeeplinkConverter]) and routes by its
|
||||
* [host][Uri.getHost] — the same routing key [DeepLinkFactory][com.tangem.tap.routing.utils.DeepLinkFactory] uses
|
||||
* for tapped deeplinks. Handlers receive the deeplink query params (not the raw payload), so both flat-key and
|
||||
* `deeplink`-style payloads are handled uniformly. Each handler owns its own reaction; add a `when` branch per
|
||||
* push type as new in-app reactions appear.
|
||||
*/
|
||||
internal class PushMessageHandler @Inject constructor(
|
||||
private val tokenDetailsPushHandler: TokenDetailsPushHandler,
|
||||
) {
|
||||
|
||||
fun onMessageReceived(data: Map<String, String>) {
|
||||
val deeplink = PayloadToDeeplinkConverter.convert(data)?.toUri() ?: return
|
||||
val queryParams = deeplink.getQueryParams()
|
||||
when (deeplink.host) {
|
||||
DeepLinkRoute.TokenDetails.host -> tokenDetailsPushHandler.handle(queryParams)
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
private fun Uri.getQueryParams(): Map<String, String> {
|
||||
val params = mutableMapOf<String, String>()
|
||||
queryParameterNames.forEach { name ->
|
||||
val value = getQueryParameter(name)
|
||||
if (name.uriValidate() && value?.uriValidate() == true) {
|
||||
params[name] = value
|
||||
}
|
||||
}
|
||||
return params
|
||||
}
|
||||
}
|
||||
|
|
@ -4,11 +4,17 @@ import android.annotation.SuppressLint
|
|||
import com.google.firebase.messaging.FirebaseMessagingService
|
||||
import com.google.firebase.messaging.RemoteMessage
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.customer.messagingpush.CustomerIOFirebaseMessagingService
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
@SuppressLint("MissingFirebaseInstanceTokenRefresh")
|
||||
internal class TangemPushNotificationService : FirebaseMessagingService() {
|
||||
|
||||
@Inject
|
||||
lateinit var pushMessageHandler: PushMessageHandler
|
||||
|
||||
private val pushNotificationDelegate: PushNotificationDelegate by lazy {
|
||||
PushNotificationDelegate(applicationContext)
|
||||
}
|
||||
|
|
@ -29,6 +35,8 @@ internal class TangemPushNotificationService : FirebaseMessagingService() {
|
|||
handleNotificationTrigger = false,
|
||||
)
|
||||
|
||||
pushMessageHandler.onMessageReceived(message.data)
|
||||
|
||||
val notification = message.notification ?: return
|
||||
val channelId = notification.channelId ?: TANGEM_CHANNEL_ID
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
package com.tangem.tap.common.pushes
|
||||
|
||||
import com.tangem.common.routing.deeplink.DeeplinkConst.DERIVATION_PATH_KEY
|
||||
import com.tangem.common.routing.deeplink.DeeplinkConst.NETWORK_ID_KEY
|
||||
import com.tangem.common.routing.deeplink.DeeplinkConst.TOKEN_ID_KEY
|
||||
import com.tangem.common.routing.deeplink.DeeplinkConst.WALLET_ID_KEY
|
||||
import com.tangem.domain.account.fetcher.SingleAccountListFetcher
|
||||
import com.tangem.domain.account.supplier.SingleAccountListSupplier
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.models.wallet.isLocked
|
||||
import com.tangem.domain.models.wallet.isMultiCurrency
|
||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.tap.ForegroundActivityObserver
|
||||
import com.tangem.utils.coroutines.AppCoroutineScope
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Handles a received token-details push (same payload as
|
||||
* [com.tangem.features.tokendetails.deeplink.TokenDetailsDeepLinkHandler]).
|
||||
*
|
||||
* When the app is open and the pushed token is not yet present in the wallet's portfolio (e.g. it was just added
|
||||
* on the backend), refreshes the wallet accounts so it appears locally — the open portfolio screen then updates
|
||||
* reactively via [SingleAccountListSupplier]. Does nothing else.
|
||||
*/
|
||||
class TokenDetailsPushHandler @Inject constructor(
|
||||
private val appCoroutineScope: AppCoroutineScope,
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||
private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
|
||||
private val singleAccountListSupplier: SingleAccountListSupplier,
|
||||
private val singleAccountListFetcher: SingleAccountListFetcher,
|
||||
) {
|
||||
|
||||
fun handle(queryParams: Map<String, String>) {
|
||||
// Only when the app is open: a token just added on the backend should appear in the already-open portfolio.
|
||||
// On cold start the fresh list is loaded by the regular auth flow instead.
|
||||
if (ForegroundActivityObserver.foregroundActivity == null) return
|
||||
appCoroutineScope.launch { refreshPortfolioIfTokenMissing(queryParams) }
|
||||
}
|
||||
|
||||
internal suspend fun refreshPortfolioIfTokenMissing(queryParams: Map<String, String>) {
|
||||
val networkId = queryParams[NETWORK_ID_KEY] ?: return
|
||||
val tokenId = queryParams[TOKEN_ID_KEY] ?: return
|
||||
val derivationPath = queryParams[DERIVATION_PATH_KEY]
|
||||
|
||||
val userWallet = resolveUserWallet(queryParams[WALLET_ID_KEY]) ?: return
|
||||
// Token list refresh only makes sense for an unlocked multi-currency wallet.
|
||||
if (userWallet.isLocked || !userWallet.isMultiCurrency) return
|
||||
|
||||
val isTokenPresent = singleAccountListSupplier.getSyncOrNull(userWallet.walletId)
|
||||
?.flattenCurrencies()
|
||||
?.any { it.matches(networkId = networkId, tokenId = tokenId, derivationPath = derivationPath) } == true
|
||||
|
||||
if (isTokenPresent) return
|
||||
|
||||
singleAccountListFetcher(SingleAccountListFetcher.Params(userWalletId = userWallet.walletId))
|
||||
.onLeft { TangemLogger.e("Error on refreshing portfolio from push", it) }
|
||||
}
|
||||
|
||||
private fun resolveUserWallet(walletId: String?): UserWallet? {
|
||||
val userWalletId = walletId?.let(::UserWalletId)
|
||||
return if (userWalletId != null) {
|
||||
getUserWalletUseCase(userWalletId).getOrNull()
|
||||
} else {
|
||||
getSelectedWalletSyncUseCase().getOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
private fun CryptoCurrency.matches(networkId: String, tokenId: String, derivationPath: String?): Boolean {
|
||||
val isNetwork = network.rawId.equals(networkId, ignoreCase = true)
|
||||
val isCurrency = id.rawCurrencyId?.value?.equals(tokenId, ignoreCase = true) == true
|
||||
val isDefaultDerivation = network.derivationPath is Network.DerivationPath.Card
|
||||
val isCustomDerivation = derivationPath?.equals(network.derivationPath.value) == true
|
||||
return isNetwork && isCurrency && (isDefaultDerivation || isCustomDerivation)
|
||||
}
|
||||
}
|
||||
|
|
@ -20,6 +20,21 @@ internal class IntentSettingsManager(val context: Context) : SettingsManager {
|
|||
open(intent = intent)
|
||||
}
|
||||
|
||||
override fun openAppNotificationSettings() {
|
||||
val intent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply {
|
||||
putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName)
|
||||
}
|
||||
} else {
|
||||
Intent(
|
||||
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
|
||||
Uri.fromParts("package", context.packageName, null),
|
||||
)
|
||||
}
|
||||
|
||||
open(intent = intent)
|
||||
}
|
||||
|
||||
override fun openBiometricSettings() {
|
||||
val settingsAction = when {
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> Settings.ACTION_BIOMETRIC_ENROLL
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
package com.tangem.tap.di.domain
|
||||
|
||||
import com.tangem.domain.dynamicaddresses.CreateConsolidationTransactionUseCase
|
||||
import com.tangem.domain.dynamicaddresses.DisableDynamicAddressesUseCase
|
||||
import com.tangem.domain.dynamicaddresses.DynamicAddressesFeatureToggles
|
||||
import com.tangem.domain.dynamicaddresses.EnableDynamicAddressesUseCase
|
||||
import com.tangem.domain.dynamicaddresses.GetDynamicAddressesStatusUseCase
|
||||
import com.tangem.domain.dynamicaddresses.GetDynamicReceiveAddressUseCase
|
||||
import com.tangem.domain.dynamicaddresses.GetDerivedXpubUseCase
|
||||
import com.tangem.domain.dynamicaddresses.IsDynamicAddressesAvailableUseCase
|
||||
import com.tangem.domain.dynamicaddresses.IsDynamicAddressesConsolidationRequiredUseCase
|
||||
import com.tangem.domain.dynamicaddresses.IsXpubSupportedUseCase
|
||||
import com.tangem.domain.dynamicaddresses.repository.ConsolidationRepository
|
||||
import com.tangem.domain.dynamicaddresses.repository.DynamicAddressesRepository
|
||||
|
|
@ -31,10 +33,10 @@ internal object DynamicAddressesDomainModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideDisableDynamicAddressesUseCase(
|
||||
fun provideIsDynamicAddressesConsolidationRequiredUseCase(
|
||||
dynamicAddressesRepository: DynamicAddressesRepository,
|
||||
): DisableDynamicAddressesUseCase {
|
||||
return DisableDynamicAddressesUseCase(dynamicAddressesRepository)
|
||||
): IsDynamicAddressesConsolidationRequiredUseCase {
|
||||
return IsDynamicAddressesConsolidationRequiredUseCase(dynamicAddressesRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
@ -67,6 +69,14 @@ internal object DynamicAddressesDomainModule {
|
|||
return IsXpubSupportedUseCase(walletManagersFacade)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideIsDynamicAddressesAvailableUseCase(
|
||||
featureToggles: DynamicAddressesFeatureToggles,
|
||||
): IsDynamicAddressesAvailableUseCase {
|
||||
return IsDynamicAddressesAvailableUseCase(featureToggles)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetDerivedXpubUseCase(
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import com.tangem.domain.offramp.GetOfframpUrlUseCase
|
|||
import com.tangem.domain.offramp.repository.OfframpRepository
|
||||
import com.tangem.domain.onramp.*
|
||||
import com.tangem.domain.onramp.repositories.*
|
||||
import com.tangem.domain.promo.PromoRepository
|
||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||
import com.tangem.tap.data.DefaultOfframpRepository
|
||||
import com.tangem.tap.network.exchangeServices.SellService
|
||||
|
|
@ -130,12 +129,6 @@ internal object OnrampDomainModule {
|
|||
return OnrampSaveTransactionUseCase(onrampTransactionRepository, onrampErrorResolver)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideOnrampSepaAvailableUseCase(onrampRepository: OnrampRepository): OnrampSepaAvailableUseCase {
|
||||
return OnrampSepaAvailableUseCase(onrampRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideOnrampUpdateTransactionStatusUseCase(
|
||||
|
|
@ -269,14 +262,12 @@ internal object OnrampDomainModule {
|
|||
onrampErrorResolver: OnrampErrorResolver,
|
||||
onrampTransactionRepository: OnrampTransactionRepository,
|
||||
settingsRepository: SettingsRepository,
|
||||
promoRepository: PromoRepository,
|
||||
): GetOnrampOffersUseCase {
|
||||
return GetOnrampOffersUseCase(
|
||||
onrampRepository = onrampRepository,
|
||||
errorResolver = onrampErrorResolver,
|
||||
onrampTransactionRepository = onrampTransactionRepository,
|
||||
settingsRepository = settingsRepository,
|
||||
promoRepository = promoRepository,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,54 +0,0 @@
|
|||
package com.tangem.tap.di.domain
|
||||
|
||||
import com.tangem.domain.promo.GetStoryContentUseCase
|
||||
import com.tangem.domain.promo.PromoRepository
|
||||
import com.tangem.domain.promo.ShouldShowPromoTokenUseCase
|
||||
import com.tangem.domain.promo.ShouldShowPromoWalletUseCase
|
||||
import com.tangem.domain.promo.ShouldShowStoriesUseCase
|
||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||
import com.tangem.features.promobanners.api.NewPromoBannersFeatureToggles
|
||||
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 PromoDomainModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideShouldShowSwapPromoWalletUseCase(
|
||||
promoRepository: PromoRepository,
|
||||
settingsRepository: SettingsRepository,
|
||||
newPromoBannersFeatureToggles: NewPromoBannersFeatureToggles,
|
||||
): ShouldShowPromoWalletUseCase {
|
||||
return ShouldShowPromoWalletUseCase(
|
||||
promoRepository,
|
||||
settingsRepository,
|
||||
newPromoBannersFeatureToggles.isNewPromoBannersEnabled,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideShouldShowSwapPromoTokenUseCase(promoRepository: PromoRepository): ShouldShowPromoTokenUseCase {
|
||||
return ShouldShowPromoTokenUseCase(promoRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideShouldShowSwapStoriesUseCase(promoRepository: PromoRepository): ShouldShowStoriesUseCase {
|
||||
return ShouldShowStoriesUseCase(promoRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetStoryContentUseCase(
|
||||
promoRepository: PromoRepository,
|
||||
settingsRepository: SettingsRepository,
|
||||
): GetStoryContentUseCase {
|
||||
return GetStoryContentUseCase(promoRepository, settingsRepository)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package com.tangem.tap.di.domain
|
||||
|
||||
import com.tangem.domain.pushnotificationpreferences.ObserveWalletPushNotificationPreferencesUseCase
|
||||
import com.tangem.domain.pushnotificationpreferences.PreloadWalletPushNotificationPreferencesUseCase
|
||||
import com.tangem.domain.pushnotificationpreferences.UpdateWalletPushNotificationPreferenceUseCase
|
||||
import com.tangem.domain.pushnotificationpreferences.repository.WalletPushNotificationPreferencesRepository
|
||||
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 PushNotificationPreferencesDomainModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesPreloadWalletPushNotificationPreferencesUseCase(
|
||||
repository: WalletPushNotificationPreferencesRepository,
|
||||
): PreloadWalletPushNotificationPreferencesUseCase {
|
||||
return PreloadWalletPushNotificationPreferencesUseCase(repository = repository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesObserveWalletPushNotificationPreferencesUseCase(
|
||||
repository: WalletPushNotificationPreferencesRepository,
|
||||
): ObserveWalletPushNotificationPreferencesUseCase {
|
||||
return ObserveWalletPushNotificationPreferencesUseCase(repository = repository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesUpdateWalletPushNotificationPreferenceUseCase(
|
||||
repository: WalletPushNotificationPreferencesRepository,
|
||||
): UpdateWalletPushNotificationPreferenceUseCase {
|
||||
return UpdateWalletPushNotificationPreferenceUseCase(repository = repository)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package com.tangem.tap.di.domain
|
||||
|
||||
import com.tangem.domain.stories.GetStoryContentUseCase
|
||||
import com.tangem.domain.stories.StoriesRepository
|
||||
import com.tangem.domain.stories.ShouldShowStoriesUseCase
|
||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||
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 StoriesDomainModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideShouldShowStoriesUseCase(storiesRepository: StoriesRepository): ShouldShowStoriesUseCase {
|
||||
return ShouldShowStoriesUseCase(storiesRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetStoryContentUseCase(
|
||||
storiesRepository: StoriesRepository,
|
||||
settingsRepository: SettingsRepository,
|
||||
): GetStoryContentUseCase {
|
||||
return GetStoryContentUseCase(storiesRepository, settingsRepository)
|
||||
}
|
||||
}
|
||||
|
|
@ -108,4 +108,8 @@ internal object SwapDomainModule {
|
|||
swapErrorResolver = swapErrorResolver,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideCalculateAmountUseCase(): CalculateAmountUseCase = CalculateAmountUseCase()
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@ import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher
|
|||
import com.tangem.domain.networks.repository.NetworksRepository
|
||||
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
|
||||
import com.tangem.domain.pay.flow.PaymentAccountStatusFetcher
|
||||
import com.tangem.domain.promo.PromoRepository
|
||||
import com.tangem.domain.stories.StoriesRepository
|
||||
import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher
|
||||
import com.tangem.domain.staking.StakingIdFactory
|
||||
import com.tangem.domain.staking.multi.MultiStakingBalanceFetcher
|
||||
|
|
@ -55,14 +55,14 @@ internal object TokensDomainModule {
|
|||
rampStateManager: RampStateManager,
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
stakingRepository: StakingRepository,
|
||||
promoRepository: PromoRepository,
|
||||
storiesRepository: StoriesRepository,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): GetCryptoCurrencyActionsUseCase {
|
||||
return GetCryptoCurrencyActionsUseCase(
|
||||
rampManager = rampStateManager,
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
stakingRepository = stakingRepository,
|
||||
promoRepository = promoRepository,
|
||||
storiesRepository = storiesRepository,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ internal object WalletConnectDomainModule {
|
|||
cardSdkConfigRepository.getCommonSigner(
|
||||
cardId = card.cardId.takeIf { isCardNotBackedUp },
|
||||
twinKey = TwinKey.getOrNull(scanResponse = wallet.scanResponse),
|
||||
userWalletId = wallet.walletId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,11 @@ import com.tangem.domain.transaction.error.FeeErrorResolver
|
|||
import com.tangem.domain.yield.supply.YieldSupplyErrorResolver
|
||||
import com.tangem.domain.yield.supply.YieldSupplyRepository
|
||||
import com.tangem.domain.yield.supply.YieldSupplyTransactionRepository
|
||||
import com.tangem.domain.yield.supply.promo.YieldPromoRepository
|
||||
import com.tangem.domain.yield.supply.promo.usecase.GetBoostedApyUseCase
|
||||
import com.tangem.domain.yield.supply.promo.usecase.GetYieldBoostStatusUseCase
|
||||
import com.tangem.domain.yield.supply.promo.usecase.IsYieldBoostPromoEnabledForTokenUseCase
|
||||
import com.tangem.domain.yield.supply.promo.usecase.ShouldShowYieldBoostMainBannerUseCase
|
||||
import com.tangem.domain.yield.supply.usecase.*
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
|
|
@ -260,4 +265,32 @@ internal object YieldSupplyDomainModule {
|
|||
coroutineScope = appScope,
|
||||
)
|
||||
}
|
||||
|
||||
// region yield-boost promo ([REDACTED_TASK_KEY])
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetBoostedApyUseCase(): GetBoostedApyUseCase = GetBoostedApyUseCase()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetYieldBoostStatusUseCase(repository: YieldPromoRepository): GetYieldBoostStatusUseCase {
|
||||
return GetYieldBoostStatusUseCase(repository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideIsYieldBoostPromoEnabledForTokenUseCase(
|
||||
repository: YieldPromoRepository,
|
||||
): IsYieldBoostPromoEnabledForTokenUseCase {
|
||||
return IsYieldBoostPromoEnabledForTokenUseCase(repository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideShouldShowYieldBoostMainBannerUseCase(
|
||||
repository: YieldPromoRepository,
|
||||
): ShouldShowYieldBoostMainBannerUseCase {
|
||||
return ShouldShowYieldBoostMainBannerUseCase(repository)
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
|
@ -2,7 +2,9 @@ package com.tangem.tap.di.libs.blockchainsdk
|
|||
|
||||
import com.tangem.core.analytics.store.LastSignedWalletFormStore
|
||||
import com.tangem.data.card.TransactionSignerFactory
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.tap.common.libs.blockchainsdk.DefaultTransactionSignerFactory
|
||||
import com.tangem.utils.coroutines.AppCoroutineScope
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -20,7 +22,13 @@ internal class TransactionSignerFactoryModule {
|
|||
@Singleton
|
||||
fun provideTransactionSignerFactory(
|
||||
lastSignedWalletFormStore: LastSignedWalletFormStore,
|
||||
userWalletsListRepository: UserWalletsListRepository,
|
||||
appCoroutineScope: AppCoroutineScope,
|
||||
): TransactionSignerFactory {
|
||||
return DefaultTransactionSignerFactory(lastSignedWalletFormStore)
|
||||
return DefaultTransactionSignerFactory(
|
||||
lastSignedWalletFormStore = lastSignedWalletFormStore,
|
||||
userWalletsListRepository = userWalletsListRepository,
|
||||
coroutineScope = appCoroutineScope,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -40,6 +40,7 @@ class TangemSigner(
|
|||
totalSignedHashes = result.data.totalSignedHashes,
|
||||
remainingSignatures = result.data.remainingSignatures,
|
||||
isRing = result.data.batchId?.let(::isRing) == true,
|
||||
signedWalletPublicKey = publicKey.seedKey,
|
||||
),
|
||||
)
|
||||
if (continuation.isActive) {
|
||||
|
|
@ -86,6 +87,7 @@ class TangemSigner(
|
|||
totalSignedHashes = result.data.totalSignedHashes,
|
||||
remainingSignatures = result.data.remainingSignatures,
|
||||
isRing = result.data.batchId?.let(::isRing) == true,
|
||||
signedWalletPublicKey = publicKey.seedKey,
|
||||
),
|
||||
)
|
||||
if (continuation.isActive) {
|
||||
|
|
@ -102,8 +104,10 @@ class TangemSigner(
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("ArrayInDataClass")
|
||||
data class TangemSignerResponse(
|
||||
val totalSignedHashes: Int?,
|
||||
val remainingSignatures: Int?,
|
||||
val isRing: Boolean,
|
||||
val signedWalletPublicKey: ByteArray,
|
||||
)
|
||||
|
|
@ -9,15 +9,15 @@ import kotlinx.coroutines.withContext
|
|||
import kotlin.coroutines.resume
|
||||
|
||||
internal suspend fun showMockCardPicker(activity: AppCompatActivity): MockContent? = withContext(Dispatchers.Main) {
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
val selected = suspendCancellableCoroutine { continuation ->
|
||||
val mocks = MockProvider.availableMocks
|
||||
val names = mocks.map { it.first }.toTypedArray()
|
||||
val names = mocks.map { it.title }.toTypedArray()
|
||||
|
||||
val dialog = AlertDialog.Builder(activity)
|
||||
.setTitle(R.string.mock_card_picker_title)
|
||||
.setItems(names) { _, which ->
|
||||
if (continuation.isActive) {
|
||||
continuation.resume(mocks[which].second)
|
||||
continuation.resume(mocks[which])
|
||||
}
|
||||
}
|
||||
.setOnCancelListener {
|
||||
|
|
@ -30,4 +30,6 @@ internal suspend fun showMockCardPicker(activity: AppCompatActivity): MockConten
|
|||
continuation.invokeOnCancellation { activity.runOnUiThread { dialog.dismiss() } }
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
selected?.resolve?.invoke(activity)
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
package com.tangem.tap.domain.sdk.mocks
|
||||
|
||||
import android.text.InputFilter
|
||||
import android.text.InputType
|
||||
import android.view.Gravity
|
||||
import android.view.ViewGroup
|
||||
import android.widget.EditText
|
||||
import android.widget.LinearLayout
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.tangem.tap.domain.sdk.mocks.content.CobrandMockContent
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
private const val BATCH_ID_MAX_LENGTH = 8
|
||||
private const val MIN_CARD_COUNT = 2
|
||||
private const val MAX_CARD_COUNT = 3
|
||||
private const val FIELD_PADDING_DP = 16
|
||||
private val BATCH_ID_REGEX = Regex("[0-9A-F]{4}|[0-9A-F]{8}")
|
||||
|
||||
internal suspend fun showCobrandConfigDialog(activity: AppCompatActivity): CobrandMockContent? =
|
||||
withContext(Dispatchers.Main) {
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
val density = activity.resources.displayMetrics.density
|
||||
val paddingPx = (FIELD_PADDING_DP * density).toInt()
|
||||
|
||||
val batchInput = EditText(activity).apply {
|
||||
hint = activity.getString(R.string.mock_cobrand_batch_hint)
|
||||
inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS
|
||||
filters = arrayOf(InputFilter.LengthFilter(BATCH_ID_MAX_LENGTH), InputFilter.AllCaps())
|
||||
}
|
||||
val countInput = EditText(activity).apply {
|
||||
hint = activity.getString(R.string.mock_cobrand_card_count_hint)
|
||||
inputType = InputType.TYPE_CLASS_NUMBER
|
||||
filters = arrayOf(InputFilter.LengthFilter(1))
|
||||
}
|
||||
val container = LinearLayout(activity).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
gravity = Gravity.CENTER_HORIZONTAL
|
||||
setPadding(paddingPx, paddingPx, paddingPx, 0)
|
||||
val lp = LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
)
|
||||
addView(batchInput, lp)
|
||||
addView(countInput, lp)
|
||||
}
|
||||
|
||||
val dialog = AlertDialog.Builder(activity)
|
||||
.setTitle(R.string.mock_cobrand_dialog_title)
|
||||
.setView(container)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||
if (continuation.isActive) continuation.resume(null)
|
||||
}
|
||||
.setOnCancelListener {
|
||||
if (continuation.isActive) continuation.resume(null)
|
||||
}
|
||||
.create()
|
||||
|
||||
dialog.setOnShowListener {
|
||||
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
|
||||
val batch = batchInput.text.toString().trim()
|
||||
val count = countInput.text.toString().toIntOrNull()
|
||||
batchInput.error = null
|
||||
countInput.error = null
|
||||
when {
|
||||
!batch.matches(BATCH_ID_REGEX) -> {
|
||||
batchInput.error = activity.getString(R.string.mock_cobrand_batch_error)
|
||||
}
|
||||
count == null || count !in MIN_CARD_COUNT..MAX_CARD_COUNT -> {
|
||||
countInput.error = activity.getString(R.string.mock_cobrand_card_count_error)
|
||||
}
|
||||
else -> {
|
||||
dialog.dismiss()
|
||||
if (continuation.isActive) {
|
||||
continuation.resume(CobrandMockContent(batch, count))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
continuation.invokeOnCancellation { activity.runOnUiThread { dialog.dismiss() } }
|
||||
dialog.show()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.tap.domain.sdk.mocks
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
|
||||
class MockOption(
|
||||
val title: String,
|
||||
val resolve: suspend (AppCompatActivity) -> MockContent?,
|
||||
)
|
||||
|
|
@ -18,32 +18,25 @@ object MockProvider {
|
|||
|
||||
private var emulatedError: TangemError = TangemSdkError.TagLost()
|
||||
|
||||
val availableMocks: List<Pair<String, MockContent>> = listOf(
|
||||
"Wallet" to WalletMockContent,
|
||||
"Note" to NoteMockContent,
|
||||
"Twins" to TwinsMockContent,
|
||||
"Ring" to RingMockContent,
|
||||
"Wallet 2" to Wallet2MockContent,
|
||||
"Wallet 2 (No Backup)" to Wallet2NoBackupMockContent,
|
||||
"Wallet 2 (No Backup, No Wallets)" to Wallet2NoBackupNoWalletsMockContent,
|
||||
"Wallet 2 (Seed Phrase)" to Wallet2WithSeedPhraseMockContent,
|
||||
"Wallet 2 (With derivations)" to Wallet2WithDerivationsMockContent,
|
||||
"Shiba" to ShibaMockContent,
|
||||
"Shiba (No Backup)" to ShibaNoBackupMockContent,
|
||||
"Shiba (No Backup, No Wallets)" to ShibaNoBackupNoWalletsMockContent,
|
||||
"Ed25519 Curve" to EdCurveMockContent,
|
||||
"Secp256k1 Curve" to Secpk1CurveMockContent,
|
||||
"Backup Wallet" to BackupWalletMockContent,
|
||||
"Dev Wallet" to DevWalletMockContent,
|
||||
"Firmware 4.12" to Firmware412MockContent,
|
||||
"French Blue (Triple)" to FrenchBlueMockContent,
|
||||
"French White (Double)" to FrenchWhiteMockContent,
|
||||
"Football Black (Double)" to FootballBlackMockContent,
|
||||
"Football Dark Green (Triple)" to FootballDarkGreenMockContent,
|
||||
"Metaplanet (Triple)" to MetaplanetMockContent,
|
||||
"Metaplanet (Double)" to MetaplanetDoubleMockContent,
|
||||
"Red Panda (Triple)" to RedPandaMockContent,
|
||||
"Red Panda (Double)" to RedPandaDoubleMockContent,
|
||||
val availableMocks: List<MockOption> = listOf(
|
||||
MockOption("Wallet") { WalletMockContent },
|
||||
MockOption("Note") { NoteMockContent },
|
||||
MockOption("Twins") { TwinsMockContent },
|
||||
MockOption("Ring") { RingMockContent },
|
||||
MockOption("Wallet 2") { Wallet2MockContent },
|
||||
MockOption("Wallet 2 (No Backup)") { Wallet2NoBackupMockContent },
|
||||
MockOption("Wallet 2 (No Backup, No Wallets)") { Wallet2NoBackupNoWalletsMockContent },
|
||||
MockOption("Wallet 2 (Seed Phrase)") { Wallet2WithSeedPhraseMockContent },
|
||||
MockOption("Wallet 2 (With derivations)") { Wallet2WithDerivationsMockContent },
|
||||
MockOption("Shiba") { ShibaMockContent },
|
||||
MockOption("Shiba (No Backup)") { ShibaNoBackupMockContent },
|
||||
MockOption("Shiba (No Backup, No Wallets)") { ShibaNoBackupNoWalletsMockContent },
|
||||
MockOption("Ed25519 Curve") { EdCurveMockContent },
|
||||
MockOption("Secp256k1 Curve") { Secpk1CurveMockContent },
|
||||
MockOption("Backup Wallet") { BackupWalletMockContent },
|
||||
MockOption("Dev Wallet") { DevWalletMockContent },
|
||||
MockOption("Firmware 4.12") { Firmware412MockContent },
|
||||
MockOption("Cobrand") { showCobrandConfigDialog(it) },
|
||||
)
|
||||
|
||||
fun setEmulateError(error: TangemError? = null) {
|
||||
|
|
|
|||
|
|
@ -17,11 +17,18 @@ import com.tangem.sdk.api.CreateProductWalletTaskResponse
|
|||
import com.tangem.tap.domain.sdk.mocks.MockContent
|
||||
import java.util.Date
|
||||
|
||||
object FrenchWhiteMockContent : MockContent {
|
||||
class CobrandMockContent(
|
||||
batchId: String,
|
||||
cardCount: Int,
|
||||
) : MockContent {
|
||||
|
||||
private val resolvedBatchId: String = batchId
|
||||
private val resolvedCardId: String = batchId.padEnd(CARD_ID_LENGTH, '0')
|
||||
private val backupCount: Int = cardCount - 1
|
||||
|
||||
private val primaryCard = PrimaryCard(
|
||||
cardId = "AF99008500000000",
|
||||
batchId = "AF990085",
|
||||
cardId = resolvedCardId,
|
||||
batchId = resolvedBatchId,
|
||||
cardPublicKey = byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5),
|
||||
linkingKey = byteArrayOf( //
|
||||
2, 121, 98, 127, -70, 14, 5, -23, -76, 115, -30, -26, 111, 17, 110, 34, -100, -121,
|
||||
|
|
@ -58,8 +65,8 @@ object FrenchWhiteMockContent : MockContent {
|
|||
)
|
||||
|
||||
override val cardDto = CardDTO(
|
||||
cardId = "AF99008500000000",
|
||||
batchId = "AF990085",
|
||||
cardId = resolvedCardId,
|
||||
batchId = resolvedBatchId,
|
||||
cardPublicKey = byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5),
|
||||
firmwareVersion = CardDTO.FirmwareVersion(
|
||||
major = 6,
|
||||
|
|
@ -201,7 +208,7 @@ object FrenchWhiteMockContent : MockContent {
|
|||
firmwareAttestation = Attestation.Status.Skipped,
|
||||
cardUniquenessAttestation = Attestation.Status.Skipped,
|
||||
),
|
||||
backupStatus = CardDTO.BackupStatus.Active(1),
|
||||
backupStatus = CardDTO.BackupStatus.Active(backupCount),
|
||||
)
|
||||
|
||||
override val scanResponse = ScanResponse(
|
||||
|
|
@ -262,7 +269,7 @@ object FrenchWhiteMockContent : MockContent {
|
|||
childNumber = 0,
|
||||
)
|
||||
|
||||
override val successResponse = SuccessResponse(cardId = "AF99008500000000")
|
||||
override val successResponse = SuccessResponse(cardId = resolvedCardId)
|
||||
|
||||
override val createProductWalletTaskResponse = CreateProductWalletTaskResponse(
|
||||
card = cardDto,
|
||||
|
|
@ -304,4 +311,8 @@ object FrenchWhiteMockContent : MockContent {
|
|||
|
||||
override val finalizeTwinResponse: ScanResponse
|
||||
get() = error("Available only for Twin")
|
||||
|
||||
private companion object {
|
||||
const val CARD_ID_LENGTH = 16
|
||||
}
|
||||
}
|
||||
|
|
@ -1,307 +0,0 @@
|
|||
package com.tangem.tap.domain.sdk.mocks.content
|
||||
|
||||
import com.tangem.common.SuccessResponse
|
||||
import com.tangem.common.card.*
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ProductType
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.operations.attestation.Attestation
|
||||
import com.tangem.operations.backup.PrimaryCard
|
||||
import com.tangem.operations.derivation.DerivationTaskResponse
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||
import com.tangem.operations.wallet.CreateWalletResponse
|
||||
import com.tangem.sdk.api.CreateProductWalletTaskResponse
|
||||
import com.tangem.tap.domain.sdk.mocks.MockContent
|
||||
import java.util.Date
|
||||
|
||||
object FootballBlackMockContent : MockContent {
|
||||
|
||||
private val primaryCard = PrimaryCard(
|
||||
cardId = "AF99009000000000",
|
||||
batchId = "AF990090",
|
||||
cardPublicKey = byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5),
|
||||
linkingKey = byteArrayOf( //
|
||||
2, 121, 98, 127, -70, 14, 5, -23, -76, 115, -30, -26, 111, 17, 110, 34, -100, -121,
|
||||
-57, -123, 74, 3, -91, 56, -20, 56, 50, -40, -101, 96, 82, 70, -91,
|
||||
),
|
||||
existingWalletsCount = 5, isHDWalletAllowed = true,
|
||||
issuer = Card.Issuer(
|
||||
name = "TANGEM SDK",
|
||||
publicKey = byteArrayOf(2, 95, 22, -67, 29, 46, -81, -28, 99, -26, 42, 51, 90, 9, -26, -78, -69, -53, -48, 68, 82, 82, 104, -123, -53, 103, -97, -60, -46, 122, -15, -67, 34),
|
||||
),
|
||||
manufacturer = Card.Manufacturer(
|
||||
name = "TANGEM",
|
||||
manufactureDate = Date(1743759687),
|
||||
signature = byteArrayOf(),
|
||||
),
|
||||
walletCurves = listOf(
|
||||
EllipticCurve.Secp256k1,
|
||||
EllipticCurve.Ed25519,
|
||||
EllipticCurve.Bls12381G2Aug,
|
||||
EllipticCurve.Secp256r1,
|
||||
EllipticCurve.Ed25519Slip0010,
|
||||
EllipticCurve.Bls12381G2,
|
||||
EllipticCurve.Bls12381G2Pop,
|
||||
EllipticCurve.Bip0340,
|
||||
),
|
||||
firmwareVersion = FirmwareVersion(
|
||||
major = 6,
|
||||
minor = 33,
|
||||
patch = 0,
|
||||
type = FirmwareVersion.FirmwareType.Release,
|
||||
),
|
||||
isKeysImportAllowed = false,
|
||||
certificate = null,
|
||||
)
|
||||
|
||||
override val cardDto = CardDTO(
|
||||
cardId = "AF99009000000000",
|
||||
batchId = "AF990090",
|
||||
cardPublicKey = byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5),
|
||||
firmwareVersion = CardDTO.FirmwareVersion(
|
||||
major = 6,
|
||||
minor = 33,
|
||||
patch = 0,
|
||||
type = FirmwareVersion.FirmwareType.Release,
|
||||
),
|
||||
manufacturer = CardDTO.Manufacturer(
|
||||
name = "TANGEM",
|
||||
manufactureDate = Date(1698094800000),
|
||||
signature = byteArrayOf(51, -12, 14, -56, 7, -39, 5, 63, 59, 24, 102, 99, -126, 124, -127, -108, -118, 71, -19, -71, 4, -47, -121, -46, 49, -51, -31, 100, -56, -15, 96, -37, 25, 82, 94, -88, 48, 98, -105, -97, -40, 41, 27, 116, 65, 26, 78, -85, 66, -94, -92, 15, 50, -2, 7, -69, 41, 56, -75, 59, 86, 68, -38, -3),
|
||||
),
|
||||
issuer = CardDTO.Issuer(
|
||||
name = "TANGEM SDK",
|
||||
publicKey = byteArrayOf(2, 95, 22, -67, 29, 46, -81, -28, 99, -26, 42, 51, 90, 9, -26, -78, -69, -53, -48, 68, 82, 82, 104, -123, -53, 103, -97, -60, -46, 122, -15, -67, 34),
|
||||
),
|
||||
settings = CardDTO.Settings(
|
||||
securityDelay = 15000,
|
||||
maxWalletsCount = 20,
|
||||
isSettingAccessCodeAllowed = true,
|
||||
isSettingPasscodeAllowed = true,
|
||||
isResettingUserCodesAllowed = false,
|
||||
isLinkedTerminalEnabled = true,
|
||||
isBackupAllowed = true,
|
||||
supportedEncryptionModes = listOf(EncryptionMode.Strong, EncryptionMode.Fast, EncryptionMode.None),
|
||||
isFilesAllowed = true,
|
||||
isHDWalletAllowed = true,
|
||||
isKeysImportAllowed = true,
|
||||
),
|
||||
userSettings = CardDTO.UserSettings(isUserCodeRecoveryAllowed = true),
|
||||
linkedTerminalStatus = CardDTO.LinkedTerminalStatus.None,
|
||||
isAccessCodeSet = true,
|
||||
isPasscodeSet = false,
|
||||
supportedCurves = listOf(
|
||||
EllipticCurve.Secp256k1,
|
||||
EllipticCurve.Ed25519,
|
||||
EllipticCurve.Bls12381G2Aug,
|
||||
EllipticCurve.Secp256r1,
|
||||
EllipticCurve.Ed25519Slip0010,
|
||||
EllipticCurve.Bls12381G2,
|
||||
EllipticCurve.Bls12381G2Pop,
|
||||
EllipticCurve.Bip0340,
|
||||
),
|
||||
wallets = listOf(
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(3, 17, 59, -102, -56, 66, 10, 36, 97, -106, -92, -117, -105, -88, -2, -3, -95, -75, -54, -2, 57, 27, -90, -19, 82, 103, -12, -52, -126, -105, -120, -55, -2),
|
||||
chainCode = byteArrayOf(-34, -28, -28, -12, 80, -109, -90, -31, -74, 36, -78, -21, 39, -125, -39, -91, 63, 40, -26, 3, 66, 27, -62, -55, -97, 52, -65, -11, 26, -80, 33, -45),
|
||||
curve = EllipticCurve.Secp256k1,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 0,
|
||||
hasBackup = true,
|
||||
derivedKeys = mapOf(
|
||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, 55, -114, -94, -73, -61, -50, 51, -115, 55, -79, 63, -96, -44, -64, -24, -36, -122, -123, -38, 81, -15, -127, -97, -42, 72, -85, -62, -98, 46, 119, 16, -55),
|
||||
chainCode = byteArrayOf(31, 17, 71, -28, -29, 17, 72, -29, -98, 112, 31, -8, 72, -75, 4, -11, 60, -100, 9, 35, 58, -42, -38, 96, -71, -68, 24, -119, -43, -18, -122, 72),
|
||||
),
|
||||
DerivationPath("m/84'/0'/0'/0/0") to ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, -26, 103, 96, 112, 127, -125, 0, 7, 53, 30, -12, -82, 45, 14, 107, -9, 126, 75, 104, -67, -49, 35, -12, -82, -90, 101, -101, 125, -76, 88, -54, 99),
|
||||
chainCode = byteArrayOf(-42, 36, 97, 65, -64, -113, 76, -91, -9, 11, 89, 123, -9, -3, 21, 103, -113, -60, 48, -31, -34, 108, 111, -38, -110, -80, 109, 17, -29, 2, 45, -71),
|
||||
),
|
||||
DerivationPath("m/44'/1'/0'/0/0") to ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, -96, -3, -83, 101, 63, -25, -125, -4, -65, -42, -56, 24, -52, 118, -11, -104, -105, 40, -59, 20, -109, -97, 29, -95, -6, -80, 2, 67, 103, -80, -22, -94),
|
||||
chainCode = byteArrayOf(-125, 27, -91, 38, -66, 109, -92, 16, -37, 93, 107, -29, -128, -1, 115, -64, 108, -63, 17, 27, 58, 78, -2, 39, 88, -39, 44, 89, 32, -38, -16, -38),
|
||||
),
|
||||
),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(-109, 42, -80, -115, -12, 44, 48, -118, -89, 10, 21, 59, 98, -110, -86, -14, 123, 105, -30, -49, -40, -4, -7, 32, 60, 67, -88, -52, -96, -10, -14, 123),
|
||||
chainCode = byteArrayOf(-101, -10, -92, 72, 19, 121, -38, 105, -55, -82, -68, 13, -6, 17, 66, -10, -75, 58, 119, -127, 74, 68, 82, 25, 45, -110, 3, 3, 108, -97, -51, -127),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(-109, 42, -80, -115, -12, 44, 48, -118, -89, 10, 21, 59, 98, -110, -86, -14, 123, 105, -30, -49, -40, -4, -7, 32, 60, 67, -88, -52, -96, -10, -14, 123),
|
||||
chainCode = byteArrayOf(-101, -10, -92, 72, 19, 121, -38, 105, -55, -82, -68, 13, -6, 17, 66, -10, -75, 58, 119, -127, 74, 68, 82, 25, 45, -110, 3, 3, 108, -97, -51, -127),
|
||||
curve = EllipticCurve.Ed25519,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 1,
|
||||
hasBackup = true,
|
||||
derivedKeys = emptyMap(),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(-64, 18, -128, 121, -89, -37, -99, 44, -125, -72, -111, -79, 7, 85, 40, 67, -39, 117, 123, 11, 105, -6, -5, -79, 19, -10, -29, 20, -14, -40, 5, 90),
|
||||
chainCode = byteArrayOf(33, -97, 53, -112, 61, 112, -24, 74, -87, -85, -124, -4, 103, -94, -97, 76, -41, -27, 118, 33, 55, 121, -17, -52, 60, 122, 27, 25, 29, -76, 78, 11),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(-118, -77, -109, 8, -119, 101, -91, 46, -44, 15, 52, -64, -92, 5, -102, 126, 52, 121, 63, -76, -54, 80, -82, -5, 31, -35, 105, -119, -96, -54, 38, 2, -6, 109, 117, -26, -47, -20, 108, 106, -69, 72, -37, -117, -30, -9, 104, -123),
|
||||
chainCode = null,
|
||||
curve = EllipticCurve.Bls12381G2Aug,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 1,
|
||||
remainingSignatures = null,
|
||||
index = 2,
|
||||
hasBackup = true,
|
||||
derivedKeys = emptyMap(),
|
||||
extendedPublicKey = null,
|
||||
isImported = false,
|
||||
),
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(17, -78, 53, 101, 96, -110, -10, -48, -84, 88, -64, 58, -26, -13, -2, -6, -25, 23, -79, -45, 58, 77, 52, -75, -123, 121, 32, 92, 84, -74, -111, -8),
|
||||
chainCode = byteArrayOf(-110, 73, -124, -65, -1, -70, 64, -89, 95, -74, 15, 46, -110, 87, 15, -61, 120, -51, 111, 111, -45, 37, 123, -114, 65, -116, 21, 39, -77, 86, -3, -26),
|
||||
curve = EllipticCurve.Bip0340,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 3,
|
||||
hasBackup = true,
|
||||
derivedKeys = emptyMap(),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(115, 14, 11, 0, -93, 81, -103, -95, -75, -84, 18, -120, -31, 76, -83, -81, 91, 25, -75, 36, -99, -53, -25, -15, -1, -57, 14, -39, 98, -116, -63, -123),
|
||||
chainCode = byteArrayOf(23, 5, 38, -48, 67, -42, -31, -21, 89, 11, 22, -28, 44, -19, -115, -78, 123, -27, 57, 57, -24, -86, 55, 15, 104, 114, -36, 80, 81, -108, -41, 112),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(-52, -92, 64, 108, -8, -100, 87, -86, -60, 18, -18, -81, 114, -84, 76, -24, 84, 52, -34, 79, -30, -66, -112, -55, -35, -119, 127, -109, 35, 18, -29, -25),
|
||||
chainCode = byteArrayOf(94, -33, -94, -63, 40, -20, -26, 71, 111, 9, 87, -36, -42, -33, 40, -53, 85, 31, -36, -29, 65, -121, 2, -23, -119, -51, 114, -17, -39, -74, 23, -97),
|
||||
curve = EllipticCurve.Ed25519Slip0010,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 4,
|
||||
hasBackup = true,
|
||||
derivedKeys = emptyMap(),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(-43, 1, -81, -47, -8, -103, -66, 42, 37, -7, 65, 54, 57, -24, 127, -89, 69, -112, 42, -46, -128, 36, -117, -28, 30, -48, 37, 52, 93, -47, 92, -47),
|
||||
chainCode = byteArrayOf(4, -97, 81, -37, 76, -67, -87, -4, -82, -36, -45, -28, -117, -59, -62, 93, -73, 50, 65, -91, -83, 25, -95, 89, -64, -40, 113, 28, 59, 113, -99, 89),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
),
|
||||
attestation = Attestation(
|
||||
cardKeyAttestation = Attestation.Status.Verified,
|
||||
walletKeysAttestation = Attestation.Status.Skipped,
|
||||
firmwareAttestation = Attestation.Status.Skipped,
|
||||
cardUniquenessAttestation = Attestation.Status.Skipped,
|
||||
),
|
||||
backupStatus = CardDTO.BackupStatus.Active(1),
|
||||
)
|
||||
|
||||
override val scanResponse = ScanResponse(
|
||||
card = cardDto,
|
||||
productType = ProductType.Wallet2,
|
||||
walletData = null,
|
||||
secondTwinPublicKey = null,
|
||||
derivedKeys = emptyMap(),
|
||||
primaryCard = null,
|
||||
)
|
||||
|
||||
override val derivationTaskResponse = DerivationTaskResponse(
|
||||
entries = mapOf(
|
||||
ByteArrayKey(
|
||||
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),
|
||||
)
|
||||
to
|
||||
ExtendedPublicKeysMap(
|
||||
mapOf(
|
||||
DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc
|
||||
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
|
||||
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),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
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),
|
||||
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'/145'/0'/0/0") to ExtendedPublicKey( // bch
|
||||
publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121),
|
||||
chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge
|
||||
publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124),
|
||||
chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
override val extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, -93, -36, -105, 121, -52, -30, -43, -67, -7, -31, -26, -35, 25, 99, 25, -20, 118, -20, -125, -89, 12, -101, -86, 74, -91, 23, -24, 93, -86, 20, -53, -8),
|
||||
chainCode = byteArrayOf(32, 60, 63, -96, 97, 58, 121, 108, 75, 59, 63, -113, 60, -49, 47, 33, 15, -65, -69, 45, -7, -26, 65, -5, -91, -55, -42, -102, -127, -104, -111, 96),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
)
|
||||
|
||||
override val successResponse = SuccessResponse(cardId = "AF99009000000000")
|
||||
|
||||
override val createProductWalletTaskResponse = CreateProductWalletTaskResponse(
|
||||
card = cardDto,
|
||||
derivedKeys = mapOf(
|
||||
ByteArrayKey(
|
||||
byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5),
|
||||
)
|
||||
to
|
||||
ExtendedPublicKeysMap(
|
||||
mapOf(
|
||||
DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc
|
||||
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
|
||||
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),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
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),
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
primaryCard = primaryCard,
|
||||
)
|
||||
|
||||
override val importWalletResponse: CreateProductWalletTaskResponse
|
||||
get() = TODO("Not yet implemented")
|
||||
|
||||
override val createFirstTwinResponse: CreateWalletResponse
|
||||
get() = error("Available only for Twin")
|
||||
|
||||
override val createSecondTwinResponse: CreateWalletResponse
|
||||
get() = error("Available only for Twin")
|
||||
|
||||
override val finalizeTwinResponse: ScanResponse
|
||||
get() = error("Available only for Twin")
|
||||
}
|
||||
|
|
@ -1,307 +0,0 @@
|
|||
package com.tangem.tap.domain.sdk.mocks.content
|
||||
|
||||
import com.tangem.common.SuccessResponse
|
||||
import com.tangem.common.card.*
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ProductType
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.operations.attestation.Attestation
|
||||
import com.tangem.operations.backup.PrimaryCard
|
||||
import com.tangem.operations.derivation.DerivationTaskResponse
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||
import com.tangem.operations.wallet.CreateWalletResponse
|
||||
import com.tangem.sdk.api.CreateProductWalletTaskResponse
|
||||
import com.tangem.tap.domain.sdk.mocks.MockContent
|
||||
import java.util.Date
|
||||
|
||||
object FootballDarkGreenMockContent : MockContent {
|
||||
|
||||
private val primaryCard = PrimaryCard(
|
||||
cardId = "AF99008900000000",
|
||||
batchId = "AF990089",
|
||||
cardPublicKey = byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5),
|
||||
linkingKey = byteArrayOf( //
|
||||
2, 121, 98, 127, -70, 14, 5, -23, -76, 115, -30, -26, 111, 17, 110, 34, -100, -121,
|
||||
-57, -123, 74, 3, -91, 56, -20, 56, 50, -40, -101, 96, 82, 70, -91,
|
||||
),
|
||||
existingWalletsCount = 5, isHDWalletAllowed = true,
|
||||
issuer = Card.Issuer(
|
||||
name = "TANGEM SDK",
|
||||
publicKey = byteArrayOf(2, 95, 22, -67, 29, 46, -81, -28, 99, -26, 42, 51, 90, 9, -26, -78, -69, -53, -48, 68, 82, 82, 104, -123, -53, 103, -97, -60, -46, 122, -15, -67, 34),
|
||||
),
|
||||
manufacturer = Card.Manufacturer(
|
||||
name = "TANGEM",
|
||||
manufactureDate = Date(1743759687),
|
||||
signature = byteArrayOf(),
|
||||
),
|
||||
walletCurves = listOf(
|
||||
EllipticCurve.Secp256k1,
|
||||
EllipticCurve.Ed25519,
|
||||
EllipticCurve.Bls12381G2Aug,
|
||||
EllipticCurve.Secp256r1,
|
||||
EllipticCurve.Ed25519Slip0010,
|
||||
EllipticCurve.Bls12381G2,
|
||||
EllipticCurve.Bls12381G2Pop,
|
||||
EllipticCurve.Bip0340,
|
||||
),
|
||||
firmwareVersion = FirmwareVersion(
|
||||
major = 6,
|
||||
minor = 33,
|
||||
patch = 0,
|
||||
type = FirmwareVersion.FirmwareType.Release,
|
||||
),
|
||||
isKeysImportAllowed = false,
|
||||
certificate = null,
|
||||
)
|
||||
|
||||
override val cardDto = CardDTO(
|
||||
cardId = "AF99008900000000",
|
||||
batchId = "AF990089",
|
||||
cardPublicKey = byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5),
|
||||
firmwareVersion = CardDTO.FirmwareVersion(
|
||||
major = 6,
|
||||
minor = 33,
|
||||
patch = 0,
|
||||
type = FirmwareVersion.FirmwareType.Release,
|
||||
),
|
||||
manufacturer = CardDTO.Manufacturer(
|
||||
name = "TANGEM",
|
||||
manufactureDate = Date(1698094800000),
|
||||
signature = byteArrayOf(51, -12, 14, -56, 7, -39, 5, 63, 59, 24, 102, 99, -126, 124, -127, -108, -118, 71, -19, -71, 4, -47, -121, -46, 49, -51, -31, 100, -56, -15, 96, -37, 25, 82, 94, -88, 48, 98, -105, -97, -40, 41, 27, 116, 65, 26, 78, -85, 66, -94, -92, 15, 50, -2, 7, -69, 41, 56, -75, 59, 86, 68, -38, -3),
|
||||
),
|
||||
issuer = CardDTO.Issuer(
|
||||
name = "TANGEM SDK",
|
||||
publicKey = byteArrayOf(2, 95, 22, -67, 29, 46, -81, -28, 99, -26, 42, 51, 90, 9, -26, -78, -69, -53, -48, 68, 82, 82, 104, -123, -53, 103, -97, -60, -46, 122, -15, -67, 34),
|
||||
),
|
||||
settings = CardDTO.Settings(
|
||||
securityDelay = 15000,
|
||||
maxWalletsCount = 20,
|
||||
isSettingAccessCodeAllowed = true,
|
||||
isSettingPasscodeAllowed = true,
|
||||
isResettingUserCodesAllowed = false,
|
||||
isLinkedTerminalEnabled = true,
|
||||
isBackupAllowed = true,
|
||||
supportedEncryptionModes = listOf(EncryptionMode.Strong, EncryptionMode.Fast, EncryptionMode.None),
|
||||
isFilesAllowed = true,
|
||||
isHDWalletAllowed = true,
|
||||
isKeysImportAllowed = true,
|
||||
),
|
||||
userSettings = CardDTO.UserSettings(isUserCodeRecoveryAllowed = true),
|
||||
linkedTerminalStatus = CardDTO.LinkedTerminalStatus.None,
|
||||
isAccessCodeSet = true,
|
||||
isPasscodeSet = false,
|
||||
supportedCurves = listOf(
|
||||
EllipticCurve.Secp256k1,
|
||||
EllipticCurve.Ed25519,
|
||||
EllipticCurve.Bls12381G2Aug,
|
||||
EllipticCurve.Secp256r1,
|
||||
EllipticCurve.Ed25519Slip0010,
|
||||
EllipticCurve.Bls12381G2,
|
||||
EllipticCurve.Bls12381G2Pop,
|
||||
EllipticCurve.Bip0340,
|
||||
),
|
||||
wallets = listOf(
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(3, 17, 59, -102, -56, 66, 10, 36, 97, -106, -92, -117, -105, -88, -2, -3, -95, -75, -54, -2, 57, 27, -90, -19, 82, 103, -12, -52, -126, -105, -120, -55, -2),
|
||||
chainCode = byteArrayOf(-34, -28, -28, -12, 80, -109, -90, -31, -74, 36, -78, -21, 39, -125, -39, -91, 63, 40, -26, 3, 66, 27, -62, -55, -97, 52, -65, -11, 26, -80, 33, -45),
|
||||
curve = EllipticCurve.Secp256k1,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 0,
|
||||
hasBackup = true,
|
||||
derivedKeys = mapOf(
|
||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, 55, -114, -94, -73, -61, -50, 51, -115, 55, -79, 63, -96, -44, -64, -24, -36, -122, -123, -38, 81, -15, -127, -97, -42, 72, -85, -62, -98, 46, 119, 16, -55),
|
||||
chainCode = byteArrayOf(31, 17, 71, -28, -29, 17, 72, -29, -98, 112, 31, -8, 72, -75, 4, -11, 60, -100, 9, 35, 58, -42, -38, 96, -71, -68, 24, -119, -43, -18, -122, 72),
|
||||
),
|
||||
DerivationPath("m/84'/0'/0'/0/0") to ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, -26, 103, 96, 112, 127, -125, 0, 7, 53, 30, -12, -82, 45, 14, 107, -9, 126, 75, 104, -67, -49, 35, -12, -82, -90, 101, -101, 125, -76, 88, -54, 99),
|
||||
chainCode = byteArrayOf(-42, 36, 97, 65, -64, -113, 76, -91, -9, 11, 89, 123, -9, -3, 21, 103, -113, -60, 48, -31, -34, 108, 111, -38, -110, -80, 109, 17, -29, 2, 45, -71),
|
||||
),
|
||||
DerivationPath("m/44'/1'/0'/0/0") to ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, -96, -3, -83, 101, 63, -25, -125, -4, -65, -42, -56, 24, -52, 118, -11, -104, -105, 40, -59, 20, -109, -97, 29, -95, -6, -80, 2, 67, 103, -80, -22, -94),
|
||||
chainCode = byteArrayOf(-125, 27, -91, 38, -66, 109, -92, 16, -37, 93, 107, -29, -128, -1, 115, -64, 108, -63, 17, 27, 58, 78, -2, 39, 88, -39, 44, 89, 32, -38, -16, -38),
|
||||
),
|
||||
),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(-109, 42, -80, -115, -12, 44, 48, -118, -89, 10, 21, 59, 98, -110, -86, -14, 123, 105, -30, -49, -40, -4, -7, 32, 60, 67, -88, -52, -96, -10, -14, 123),
|
||||
chainCode = byteArrayOf(-101, -10, -92, 72, 19, 121, -38, 105, -55, -82, -68, 13, -6, 17, 66, -10, -75, 58, 119, -127, 74, 68, 82, 25, 45, -110, 3, 3, 108, -97, -51, -127),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(-109, 42, -80, -115, -12, 44, 48, -118, -89, 10, 21, 59, 98, -110, -86, -14, 123, 105, -30, -49, -40, -4, -7, 32, 60, 67, -88, -52, -96, -10, -14, 123),
|
||||
chainCode = byteArrayOf(-101, -10, -92, 72, 19, 121, -38, 105, -55, -82, -68, 13, -6, 17, 66, -10, -75, 58, 119, -127, 74, 68, 82, 25, 45, -110, 3, 3, 108, -97, -51, -127),
|
||||
curve = EllipticCurve.Ed25519,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 1,
|
||||
hasBackup = true,
|
||||
derivedKeys = emptyMap(),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(-64, 18, -128, 121, -89, -37, -99, 44, -125, -72, -111, -79, 7, 85, 40, 67, -39, 117, 123, 11, 105, -6, -5, -79, 19, -10, -29, 20, -14, -40, 5, 90),
|
||||
chainCode = byteArrayOf(33, -97, 53, -112, 61, 112, -24, 74, -87, -85, -124, -4, 103, -94, -97, 76, -41, -27, 118, 33, 55, 121, -17, -52, 60, 122, 27, 25, 29, -76, 78, 11),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(-118, -77, -109, 8, -119, 101, -91, 46, -44, 15, 52, -64, -92, 5, -102, 126, 52, 121, 63, -76, -54, 80, -82, -5, 31, -35, 105, -119, -96, -54, 38, 2, -6, 109, 117, -26, -47, -20, 108, 106, -69, 72, -37, -117, -30, -9, 104, -123),
|
||||
chainCode = null,
|
||||
curve = EllipticCurve.Bls12381G2Aug,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 1,
|
||||
remainingSignatures = null,
|
||||
index = 2,
|
||||
hasBackup = true,
|
||||
derivedKeys = emptyMap(),
|
||||
extendedPublicKey = null,
|
||||
isImported = false,
|
||||
),
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(17, -78, 53, 101, 96, -110, -10, -48, -84, 88, -64, 58, -26, -13, -2, -6, -25, 23, -79, -45, 58, 77, 52, -75, -123, 121, 32, 92, 84, -74, -111, -8),
|
||||
chainCode = byteArrayOf(-110, 73, -124, -65, -1, -70, 64, -89, 95, -74, 15, 46, -110, 87, 15, -61, 120, -51, 111, 111, -45, 37, 123, -114, 65, -116, 21, 39, -77, 86, -3, -26),
|
||||
curve = EllipticCurve.Bip0340,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 3,
|
||||
hasBackup = true,
|
||||
derivedKeys = emptyMap(),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(115, 14, 11, 0, -93, 81, -103, -95, -75, -84, 18, -120, -31, 76, -83, -81, 91, 25, -75, 36, -99, -53, -25, -15, -1, -57, 14, -39, 98, -116, -63, -123),
|
||||
chainCode = byteArrayOf(23, 5, 38, -48, 67, -42, -31, -21, 89, 11, 22, -28, 44, -19, -115, -78, 123, -27, 57, 57, -24, -86, 55, 15, 104, 114, -36, 80, 81, -108, -41, 112),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(-52, -92, 64, 108, -8, -100, 87, -86, -60, 18, -18, -81, 114, -84, 76, -24, 84, 52, -34, 79, -30, -66, -112, -55, -35, -119, 127, -109, 35, 18, -29, -25),
|
||||
chainCode = byteArrayOf(94, -33, -94, -63, 40, -20, -26, 71, 111, 9, 87, -36, -42, -33, 40, -53, 85, 31, -36, -29, 65, -121, 2, -23, -119, -51, 114, -17, -39, -74, 23, -97),
|
||||
curve = EllipticCurve.Ed25519Slip0010,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 4,
|
||||
hasBackup = true,
|
||||
derivedKeys = emptyMap(),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(-43, 1, -81, -47, -8, -103, -66, 42, 37, -7, 65, 54, 57, -24, 127, -89, 69, -112, 42, -46, -128, 36, -117, -28, 30, -48, 37, 52, 93, -47, 92, -47),
|
||||
chainCode = byteArrayOf(4, -97, 81, -37, 76, -67, -87, -4, -82, -36, -45, -28, -117, -59, -62, 93, -73, 50, 65, -91, -83, 25, -95, 89, -64, -40, 113, 28, 59, 113, -99, 89),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
),
|
||||
attestation = Attestation(
|
||||
cardKeyAttestation = Attestation.Status.Verified,
|
||||
walletKeysAttestation = Attestation.Status.Skipped,
|
||||
firmwareAttestation = Attestation.Status.Skipped,
|
||||
cardUniquenessAttestation = Attestation.Status.Skipped,
|
||||
),
|
||||
backupStatus = CardDTO.BackupStatus.Active(2),
|
||||
)
|
||||
|
||||
override val scanResponse = ScanResponse(
|
||||
card = cardDto,
|
||||
productType = ProductType.Wallet2,
|
||||
walletData = null,
|
||||
secondTwinPublicKey = null,
|
||||
derivedKeys = emptyMap(),
|
||||
primaryCard = null,
|
||||
)
|
||||
|
||||
override val derivationTaskResponse = DerivationTaskResponse(
|
||||
entries = mapOf(
|
||||
ByteArrayKey(
|
||||
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),
|
||||
)
|
||||
to
|
||||
ExtendedPublicKeysMap(
|
||||
mapOf(
|
||||
DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc
|
||||
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
|
||||
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),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
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),
|
||||
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'/145'/0'/0/0") to ExtendedPublicKey( // bch
|
||||
publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121),
|
||||
chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge
|
||||
publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124),
|
||||
chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
override val extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, -93, -36, -105, 121, -52, -30, -43, -67, -7, -31, -26, -35, 25, 99, 25, -20, 118, -20, -125, -89, 12, -101, -86, 74, -91, 23, -24, 93, -86, 20, -53, -8),
|
||||
chainCode = byteArrayOf(32, 60, 63, -96, 97, 58, 121, 108, 75, 59, 63, -113, 60, -49, 47, 33, 15, -65, -69, 45, -7, -26, 65, -5, -91, -55, -42, -102, -127, -104, -111, 96),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
)
|
||||
|
||||
override val successResponse = SuccessResponse(cardId = "AF99008900000000")
|
||||
|
||||
override val createProductWalletTaskResponse = CreateProductWalletTaskResponse(
|
||||
card = cardDto,
|
||||
derivedKeys = mapOf(
|
||||
ByteArrayKey(
|
||||
byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5),
|
||||
)
|
||||
to
|
||||
ExtendedPublicKeysMap(
|
||||
mapOf(
|
||||
DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc
|
||||
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
|
||||
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),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
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),
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
primaryCard = primaryCard,
|
||||
)
|
||||
|
||||
override val importWalletResponse: CreateProductWalletTaskResponse
|
||||
get() = TODO("Not yet implemented")
|
||||
|
||||
override val createFirstTwinResponse: CreateWalletResponse
|
||||
get() = error("Available only for Twin")
|
||||
|
||||
override val createSecondTwinResponse: CreateWalletResponse
|
||||
get() = error("Available only for Twin")
|
||||
|
||||
override val finalizeTwinResponse: ScanResponse
|
||||
get() = error("Available only for Twin")
|
||||
}
|
||||
|
|
@ -1,307 +0,0 @@
|
|||
package com.tangem.tap.domain.sdk.mocks.content
|
||||
|
||||
import com.tangem.common.SuccessResponse
|
||||
import com.tangem.common.card.*
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ProductType
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.operations.attestation.Attestation
|
||||
import com.tangem.operations.backup.PrimaryCard
|
||||
import com.tangem.operations.derivation.DerivationTaskResponse
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||
import com.tangem.operations.wallet.CreateWalletResponse
|
||||
import com.tangem.sdk.api.CreateProductWalletTaskResponse
|
||||
import com.tangem.tap.domain.sdk.mocks.MockContent
|
||||
import java.util.Date
|
||||
|
||||
object FrenchBlueMockContent : MockContent {
|
||||
|
||||
private val primaryCard = PrimaryCard(
|
||||
cardId = "AF99008400000000",
|
||||
batchId = "AF990084",
|
||||
cardPublicKey = byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5),
|
||||
linkingKey = byteArrayOf( //
|
||||
2, 121, 98, 127, -70, 14, 5, -23, -76, 115, -30, -26, 111, 17, 110, 34, -100, -121,
|
||||
-57, -123, 74, 3, -91, 56, -20, 56, 50, -40, -101, 96, 82, 70, -91,
|
||||
),
|
||||
existingWalletsCount = 5, isHDWalletAllowed = true,
|
||||
issuer = Card.Issuer(
|
||||
name = "TANGEM SDK",
|
||||
publicKey = byteArrayOf(2, 95, 22, -67, 29, 46, -81, -28, 99, -26, 42, 51, 90, 9, -26, -78, -69, -53, -48, 68, 82, 82, 104, -123, -53, 103, -97, -60, -46, 122, -15, -67, 34),
|
||||
),
|
||||
manufacturer = Card.Manufacturer(
|
||||
name = "TANGEM",
|
||||
manufactureDate = Date(1743759687),
|
||||
signature = byteArrayOf(),
|
||||
),
|
||||
walletCurves = listOf(
|
||||
EllipticCurve.Secp256k1,
|
||||
EllipticCurve.Ed25519,
|
||||
EllipticCurve.Bls12381G2Aug,
|
||||
EllipticCurve.Secp256r1,
|
||||
EllipticCurve.Ed25519Slip0010,
|
||||
EllipticCurve.Bls12381G2,
|
||||
EllipticCurve.Bls12381G2Pop,
|
||||
EllipticCurve.Bip0340,
|
||||
),
|
||||
firmwareVersion = FirmwareVersion(
|
||||
major = 6,
|
||||
minor = 33,
|
||||
patch = 0,
|
||||
type = FirmwareVersion.FirmwareType.Release,
|
||||
),
|
||||
isKeysImportAllowed = false,
|
||||
certificate = null,
|
||||
)
|
||||
|
||||
override val cardDto = CardDTO(
|
||||
cardId = "AF99008400000000",
|
||||
batchId = "AF990084",
|
||||
cardPublicKey = byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5),
|
||||
firmwareVersion = CardDTO.FirmwareVersion(
|
||||
major = 6,
|
||||
minor = 33,
|
||||
patch = 0,
|
||||
type = FirmwareVersion.FirmwareType.Release,
|
||||
),
|
||||
manufacturer = CardDTO.Manufacturer(
|
||||
name = "TANGEM",
|
||||
manufactureDate = Date(1698094800000),
|
||||
signature = byteArrayOf(51, -12, 14, -56, 7, -39, 5, 63, 59, 24, 102, 99, -126, 124, -127, -108, -118, 71, -19, -71, 4, -47, -121, -46, 49, -51, -31, 100, -56, -15, 96, -37, 25, 82, 94, -88, 48, 98, -105, -97, -40, 41, 27, 116, 65, 26, 78, -85, 66, -94, -92, 15, 50, -2, 7, -69, 41, 56, -75, 59, 86, 68, -38, -3),
|
||||
),
|
||||
issuer = CardDTO.Issuer(
|
||||
name = "TANGEM SDK",
|
||||
publicKey = byteArrayOf(2, 95, 22, -67, 29, 46, -81, -28, 99, -26, 42, 51, 90, 9, -26, -78, -69, -53, -48, 68, 82, 82, 104, -123, -53, 103, -97, -60, -46, 122, -15, -67, 34),
|
||||
),
|
||||
settings = CardDTO.Settings(
|
||||
securityDelay = 15000,
|
||||
maxWalletsCount = 20,
|
||||
isSettingAccessCodeAllowed = true,
|
||||
isSettingPasscodeAllowed = true,
|
||||
isResettingUserCodesAllowed = false,
|
||||
isLinkedTerminalEnabled = true,
|
||||
isBackupAllowed = true,
|
||||
supportedEncryptionModes = listOf(EncryptionMode.Strong, EncryptionMode.Fast, EncryptionMode.None),
|
||||
isFilesAllowed = true,
|
||||
isHDWalletAllowed = true,
|
||||
isKeysImportAllowed = true,
|
||||
),
|
||||
userSettings = CardDTO.UserSettings(isUserCodeRecoveryAllowed = true),
|
||||
linkedTerminalStatus = CardDTO.LinkedTerminalStatus.None,
|
||||
isAccessCodeSet = true,
|
||||
isPasscodeSet = false,
|
||||
supportedCurves = listOf(
|
||||
EllipticCurve.Secp256k1,
|
||||
EllipticCurve.Ed25519,
|
||||
EllipticCurve.Bls12381G2Aug,
|
||||
EllipticCurve.Secp256r1,
|
||||
EllipticCurve.Ed25519Slip0010,
|
||||
EllipticCurve.Bls12381G2,
|
||||
EllipticCurve.Bls12381G2Pop,
|
||||
EllipticCurve.Bip0340,
|
||||
),
|
||||
wallets = listOf(
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(3, 17, 59, -102, -56, 66, 10, 36, 97, -106, -92, -117, -105, -88, -2, -3, -95, -75, -54, -2, 57, 27, -90, -19, 82, 103, -12, -52, -126, -105, -120, -55, -2),
|
||||
chainCode = byteArrayOf(-34, -28, -28, -12, 80, -109, -90, -31, -74, 36, -78, -21, 39, -125, -39, -91, 63, 40, -26, 3, 66, 27, -62, -55, -97, 52, -65, -11, 26, -80, 33, -45),
|
||||
curve = EllipticCurve.Secp256k1,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 0,
|
||||
hasBackup = true,
|
||||
derivedKeys = mapOf(
|
||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, 55, -114, -94, -73, -61, -50, 51, -115, 55, -79, 63, -96, -44, -64, -24, -36, -122, -123, -38, 81, -15, -127, -97, -42, 72, -85, -62, -98, 46, 119, 16, -55),
|
||||
chainCode = byteArrayOf(31, 17, 71, -28, -29, 17, 72, -29, -98, 112, 31, -8, 72, -75, 4, -11, 60, -100, 9, 35, 58, -42, -38, 96, -71, -68, 24, -119, -43, -18, -122, 72),
|
||||
),
|
||||
DerivationPath("m/84'/0'/0'/0/0") to ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, -26, 103, 96, 112, 127, -125, 0, 7, 53, 30, -12, -82, 45, 14, 107, -9, 126, 75, 104, -67, -49, 35, -12, -82, -90, 101, -101, 125, -76, 88, -54, 99),
|
||||
chainCode = byteArrayOf(-42, 36, 97, 65, -64, -113, 76, -91, -9, 11, 89, 123, -9, -3, 21, 103, -113, -60, 48, -31, -34, 108, 111, -38, -110, -80, 109, 17, -29, 2, 45, -71),
|
||||
),
|
||||
DerivationPath("m/44'/1'/0'/0/0") to ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, -96, -3, -83, 101, 63, -25, -125, -4, -65, -42, -56, 24, -52, 118, -11, -104, -105, 40, -59, 20, -109, -97, 29, -95, -6, -80, 2, 67, 103, -80, -22, -94),
|
||||
chainCode = byteArrayOf(-125, 27, -91, 38, -66, 109, -92, 16, -37, 93, 107, -29, -128, -1, 115, -64, 108, -63, 17, 27, 58, 78, -2, 39, 88, -39, 44, 89, 32, -38, -16, -38),
|
||||
),
|
||||
),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(-109, 42, -80, -115, -12, 44, 48, -118, -89, 10, 21, 59, 98, -110, -86, -14, 123, 105, -30, -49, -40, -4, -7, 32, 60, 67, -88, -52, -96, -10, -14, 123),
|
||||
chainCode = byteArrayOf(-101, -10, -92, 72, 19, 121, -38, 105, -55, -82, -68, 13, -6, 17, 66, -10, -75, 58, 119, -127, 74, 68, 82, 25, 45, -110, 3, 3, 108, -97, -51, -127),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(-109, 42, -80, -115, -12, 44, 48, -118, -89, 10, 21, 59, 98, -110, -86, -14, 123, 105, -30, -49, -40, -4, -7, 32, 60, 67, -88, -52, -96, -10, -14, 123),
|
||||
chainCode = byteArrayOf(-101, -10, -92, 72, 19, 121, -38, 105, -55, -82, -68, 13, -6, 17, 66, -10, -75, 58, 119, -127, 74, 68, 82, 25, 45, -110, 3, 3, 108, -97, -51, -127),
|
||||
curve = EllipticCurve.Ed25519,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 1,
|
||||
hasBackup = true,
|
||||
derivedKeys = emptyMap(),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(-64, 18, -128, 121, -89, -37, -99, 44, -125, -72, -111, -79, 7, 85, 40, 67, -39, 117, 123, 11, 105, -6, -5, -79, 19, -10, -29, 20, -14, -40, 5, 90),
|
||||
chainCode = byteArrayOf(33, -97, 53, -112, 61, 112, -24, 74, -87, -85, -124, -4, 103, -94, -97, 76, -41, -27, 118, 33, 55, 121, -17, -52, 60, 122, 27, 25, 29, -76, 78, 11),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(-118, -77, -109, 8, -119, 101, -91, 46, -44, 15, 52, -64, -92, 5, -102, 126, 52, 121, 63, -76, -54, 80, -82, -5, 31, -35, 105, -119, -96, -54, 38, 2, -6, 109, 117, -26, -47, -20, 108, 106, -69, 72, -37, -117, -30, -9, 104, -123),
|
||||
chainCode = null,
|
||||
curve = EllipticCurve.Bls12381G2Aug,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 1,
|
||||
remainingSignatures = null,
|
||||
index = 2,
|
||||
hasBackup = true,
|
||||
derivedKeys = emptyMap(),
|
||||
extendedPublicKey = null,
|
||||
isImported = false,
|
||||
),
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(17, -78, 53, 101, 96, -110, -10, -48, -84, 88, -64, 58, -26, -13, -2, -6, -25, 23, -79, -45, 58, 77, 52, -75, -123, 121, 32, 92, 84, -74, -111, -8),
|
||||
chainCode = byteArrayOf(-110, 73, -124, -65, -1, -70, 64, -89, 95, -74, 15, 46, -110, 87, 15, -61, 120, -51, 111, 111, -45, 37, 123, -114, 65, -116, 21, 39, -77, 86, -3, -26),
|
||||
curve = EllipticCurve.Bip0340,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 3,
|
||||
hasBackup = true,
|
||||
derivedKeys = emptyMap(),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(115, 14, 11, 0, -93, 81, -103, -95, -75, -84, 18, -120, -31, 76, -83, -81, 91, 25, -75, 36, -99, -53, -25, -15, -1, -57, 14, -39, 98, -116, -63, -123),
|
||||
chainCode = byteArrayOf(23, 5, 38, -48, 67, -42, -31, -21, 89, 11, 22, -28, 44, -19, -115, -78, 123, -27, 57, 57, -24, -86, 55, 15, 104, 114, -36, 80, 81, -108, -41, 112),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(-52, -92, 64, 108, -8, -100, 87, -86, -60, 18, -18, -81, 114, -84, 76, -24, 84, 52, -34, 79, -30, -66, -112, -55, -35, -119, 127, -109, 35, 18, -29, -25),
|
||||
chainCode = byteArrayOf(94, -33, -94, -63, 40, -20, -26, 71, 111, 9, 87, -36, -42, -33, 40, -53, 85, 31, -36, -29, 65, -121, 2, -23, -119, -51, 114, -17, -39, -74, 23, -97),
|
||||
curve = EllipticCurve.Ed25519Slip0010,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 4,
|
||||
hasBackup = true,
|
||||
derivedKeys = emptyMap(),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(-43, 1, -81, -47, -8, -103, -66, 42, 37, -7, 65, 54, 57, -24, 127, -89, 69, -112, 42, -46, -128, 36, -117, -28, 30, -48, 37, 52, 93, -47, 92, -47),
|
||||
chainCode = byteArrayOf(4, -97, 81, -37, 76, -67, -87, -4, -82, -36, -45, -28, -117, -59, -62, 93, -73, 50, 65, -91, -83, 25, -95, 89, -64, -40, 113, 28, 59, 113, -99, 89),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
),
|
||||
attestation = Attestation(
|
||||
cardKeyAttestation = Attestation.Status.Verified,
|
||||
walletKeysAttestation = Attestation.Status.Skipped,
|
||||
firmwareAttestation = Attestation.Status.Skipped,
|
||||
cardUniquenessAttestation = Attestation.Status.Skipped,
|
||||
),
|
||||
backupStatus = CardDTO.BackupStatus.Active(2),
|
||||
)
|
||||
|
||||
override val scanResponse = ScanResponse(
|
||||
card = cardDto,
|
||||
productType = ProductType.Wallet2,
|
||||
walletData = null,
|
||||
secondTwinPublicKey = null,
|
||||
derivedKeys = emptyMap(),
|
||||
primaryCard = null,
|
||||
)
|
||||
|
||||
override val derivationTaskResponse = DerivationTaskResponse(
|
||||
entries = mapOf(
|
||||
ByteArrayKey(
|
||||
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),
|
||||
)
|
||||
to
|
||||
ExtendedPublicKeysMap(
|
||||
mapOf(
|
||||
DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc
|
||||
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
|
||||
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),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
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),
|
||||
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'/145'/0'/0/0") to ExtendedPublicKey( // bch
|
||||
publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121),
|
||||
chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge
|
||||
publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124),
|
||||
chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
override val extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, -93, -36, -105, 121, -52, -30, -43, -67, -7, -31, -26, -35, 25, 99, 25, -20, 118, -20, -125, -89, 12, -101, -86, 74, -91, 23, -24, 93, -86, 20, -53, -8),
|
||||
chainCode = byteArrayOf(32, 60, 63, -96, 97, 58, 121, 108, 75, 59, 63, -113, 60, -49, 47, 33, 15, -65, -69, 45, -7, -26, 65, -5, -91, -55, -42, -102, -127, -104, -111, 96),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
)
|
||||
|
||||
override val successResponse = SuccessResponse(cardId = "AF99008400000000")
|
||||
|
||||
override val createProductWalletTaskResponse = CreateProductWalletTaskResponse(
|
||||
card = cardDto,
|
||||
derivedKeys = mapOf(
|
||||
ByteArrayKey(
|
||||
byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5),
|
||||
)
|
||||
to
|
||||
ExtendedPublicKeysMap(
|
||||
mapOf(
|
||||
DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc
|
||||
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
|
||||
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),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
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),
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
primaryCard = primaryCard,
|
||||
)
|
||||
|
||||
override val importWalletResponse: CreateProductWalletTaskResponse
|
||||
get() = TODO("Not yet implemented")
|
||||
|
||||
override val createFirstTwinResponse: CreateWalletResponse
|
||||
get() = error("Available only for Twin")
|
||||
|
||||
override val createSecondTwinResponse: CreateWalletResponse
|
||||
get() = error("Available only for Twin")
|
||||
|
||||
override val finalizeTwinResponse: ScanResponse
|
||||
get() = error("Available only for Twin")
|
||||
}
|
||||
|
|
@ -1,307 +0,0 @@
|
|||
package com.tangem.tap.domain.sdk.mocks.content
|
||||
|
||||
import com.tangem.common.SuccessResponse
|
||||
import com.tangem.common.card.*
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ProductType
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.operations.attestation.Attestation
|
||||
import com.tangem.operations.backup.PrimaryCard
|
||||
import com.tangem.operations.derivation.DerivationTaskResponse
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||
import com.tangem.operations.wallet.CreateWalletResponse
|
||||
import com.tangem.sdk.api.CreateProductWalletTaskResponse
|
||||
import com.tangem.tap.domain.sdk.mocks.MockContent
|
||||
import java.util.Date
|
||||
|
||||
object MetaplanetDoubleMockContent : MockContent {
|
||||
|
||||
private val primaryCard = PrimaryCard(
|
||||
cardId = "BB00004000000000",
|
||||
batchId = "BB000040",
|
||||
cardPublicKey = byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5),
|
||||
linkingKey = byteArrayOf( //
|
||||
2, 121, 98, 127, -70, 14, 5, -23, -76, 115, -30, -26, 111, 17, 110, 34, -100, -121,
|
||||
-57, -123, 74, 3, -91, 56, -20, 56, 50, -40, -101, 96, 82, 70, -91,
|
||||
),
|
||||
existingWalletsCount = 5, isHDWalletAllowed = true,
|
||||
issuer = Card.Issuer(
|
||||
name = "TANGEM SDK",
|
||||
publicKey = byteArrayOf(2, 95, 22, -67, 29, 46, -81, -28, 99, -26, 42, 51, 90, 9, -26, -78, -69, -53, -48, 68, 82, 82, 104, -123, -53, 103, -97, -60, -46, 122, -15, -67, 34),
|
||||
),
|
||||
manufacturer = Card.Manufacturer(
|
||||
name = "TANGEM",
|
||||
manufactureDate = Date(1743759687),
|
||||
signature = byteArrayOf(),
|
||||
),
|
||||
walletCurves = listOf(
|
||||
EllipticCurve.Secp256k1,
|
||||
EllipticCurve.Ed25519,
|
||||
EllipticCurve.Bls12381G2Aug,
|
||||
EllipticCurve.Secp256r1,
|
||||
EllipticCurve.Ed25519Slip0010,
|
||||
EllipticCurve.Bls12381G2,
|
||||
EllipticCurve.Bls12381G2Pop,
|
||||
EllipticCurve.Bip0340,
|
||||
),
|
||||
firmwareVersion = FirmwareVersion(
|
||||
major = 6,
|
||||
minor = 33,
|
||||
patch = 0,
|
||||
type = FirmwareVersion.FirmwareType.Release,
|
||||
),
|
||||
isKeysImportAllowed = false,
|
||||
certificate = null,
|
||||
)
|
||||
|
||||
override val cardDto = CardDTO(
|
||||
cardId = "BB00004000000000",
|
||||
batchId = "BB000040",
|
||||
cardPublicKey = byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5),
|
||||
firmwareVersion = CardDTO.FirmwareVersion(
|
||||
major = 6,
|
||||
minor = 33,
|
||||
patch = 0,
|
||||
type = FirmwareVersion.FirmwareType.Release,
|
||||
),
|
||||
manufacturer = CardDTO.Manufacturer(
|
||||
name = "TANGEM",
|
||||
manufactureDate = Date(1698094800000),
|
||||
signature = byteArrayOf(51, -12, 14, -56, 7, -39, 5, 63, 59, 24, 102, 99, -126, 124, -127, -108, -118, 71, -19, -71, 4, -47, -121, -46, 49, -51, -31, 100, -56, -15, 96, -37, 25, 82, 94, -88, 48, 98, -105, -97, -40, 41, 27, 116, 65, 26, 78, -85, 66, -94, -92, 15, 50, -2, 7, -69, 41, 56, -75, 59, 86, 68, -38, -3),
|
||||
),
|
||||
issuer = CardDTO.Issuer(
|
||||
name = "TANGEM SDK",
|
||||
publicKey = byteArrayOf(2, 95, 22, -67, 29, 46, -81, -28, 99, -26, 42, 51, 90, 9, -26, -78, -69, -53, -48, 68, 82, 82, 104, -123, -53, 103, -97, -60, -46, 122, -15, -67, 34),
|
||||
),
|
||||
settings = CardDTO.Settings(
|
||||
securityDelay = 15000,
|
||||
maxWalletsCount = 20,
|
||||
isSettingAccessCodeAllowed = true,
|
||||
isSettingPasscodeAllowed = true,
|
||||
isResettingUserCodesAllowed = false,
|
||||
isLinkedTerminalEnabled = true,
|
||||
isBackupAllowed = true,
|
||||
supportedEncryptionModes = listOf(EncryptionMode.Strong, EncryptionMode.Fast, EncryptionMode.None),
|
||||
isFilesAllowed = true,
|
||||
isHDWalletAllowed = true,
|
||||
isKeysImportAllowed = true,
|
||||
),
|
||||
userSettings = CardDTO.UserSettings(isUserCodeRecoveryAllowed = true),
|
||||
linkedTerminalStatus = CardDTO.LinkedTerminalStatus.None,
|
||||
isAccessCodeSet = true,
|
||||
isPasscodeSet = false,
|
||||
supportedCurves = listOf(
|
||||
EllipticCurve.Secp256k1,
|
||||
EllipticCurve.Ed25519,
|
||||
EllipticCurve.Bls12381G2Aug,
|
||||
EllipticCurve.Secp256r1,
|
||||
EllipticCurve.Ed25519Slip0010,
|
||||
EllipticCurve.Bls12381G2,
|
||||
EllipticCurve.Bls12381G2Pop,
|
||||
EllipticCurve.Bip0340,
|
||||
),
|
||||
wallets = listOf(
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(3, 17, 59, -102, -56, 66, 10, 36, 97, -106, -92, -117, -105, -88, -2, -3, -95, -75, -54, -2, 57, 27, -90, -19, 82, 103, -12, -52, -126, -105, -120, -55, -2),
|
||||
chainCode = byteArrayOf(-34, -28, -28, -12, 80, -109, -90, -31, -74, 36, -78, -21, 39, -125, -39, -91, 63, 40, -26, 3, 66, 27, -62, -55, -97, 52, -65, -11, 26, -80, 33, -45),
|
||||
curve = EllipticCurve.Secp256k1,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 0,
|
||||
hasBackup = true,
|
||||
derivedKeys = mapOf(
|
||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, 55, -114, -94, -73, -61, -50, 51, -115, 55, -79, 63, -96, -44, -64, -24, -36, -122, -123, -38, 81, -15, -127, -97, -42, 72, -85, -62, -98, 46, 119, 16, -55),
|
||||
chainCode = byteArrayOf(31, 17, 71, -28, -29, 17, 72, -29, -98, 112, 31, -8, 72, -75, 4, -11, 60, -100, 9, 35, 58, -42, -38, 96, -71, -68, 24, -119, -43, -18, -122, 72),
|
||||
),
|
||||
DerivationPath("m/84'/0'/0'/0/0") to ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, -26, 103, 96, 112, 127, -125, 0, 7, 53, 30, -12, -82, 45, 14, 107, -9, 126, 75, 104, -67, -49, 35, -12, -82, -90, 101, -101, 125, -76, 88, -54, 99),
|
||||
chainCode = byteArrayOf(-42, 36, 97, 65, -64, -113, 76, -91, -9, 11, 89, 123, -9, -3, 21, 103, -113, -60, 48, -31, -34, 108, 111, -38, -110, -80, 109, 17, -29, 2, 45, -71),
|
||||
),
|
||||
DerivationPath("m/44'/1'/0'/0/0") to ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, -96, -3, -83, 101, 63, -25, -125, -4, -65, -42, -56, 24, -52, 118, -11, -104, -105, 40, -59, 20, -109, -97, 29, -95, -6, -80, 2, 67, 103, -80, -22, -94),
|
||||
chainCode = byteArrayOf(-125, 27, -91, 38, -66, 109, -92, 16, -37, 93, 107, -29, -128, -1, 115, -64, 108, -63, 17, 27, 58, 78, -2, 39, 88, -39, 44, 89, 32, -38, -16, -38),
|
||||
),
|
||||
),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(-109, 42, -80, -115, -12, 44, 48, -118, -89, 10, 21, 59, 98, -110, -86, -14, 123, 105, -30, -49, -40, -4, -7, 32, 60, 67, -88, -52, -96, -10, -14, 123),
|
||||
chainCode = byteArrayOf(-101, -10, -92, 72, 19, 121, -38, 105, -55, -82, -68, 13, -6, 17, 66, -10, -75, 58, 119, -127, 74, 68, 82, 25, 45, -110, 3, 3, 108, -97, -51, -127),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(-109, 42, -80, -115, -12, 44, 48, -118, -89, 10, 21, 59, 98, -110, -86, -14, 123, 105, -30, -49, -40, -4, -7, 32, 60, 67, -88, -52, -96, -10, -14, 123),
|
||||
chainCode = byteArrayOf(-101, -10, -92, 72, 19, 121, -38, 105, -55, -82, -68, 13, -6, 17, 66, -10, -75, 58, 119, -127, 74, 68, 82, 25, 45, -110, 3, 3, 108, -97, -51, -127),
|
||||
curve = EllipticCurve.Ed25519,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 1,
|
||||
hasBackup = true,
|
||||
derivedKeys = emptyMap(),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(-64, 18, -128, 121, -89, -37, -99, 44, -125, -72, -111, -79, 7, 85, 40, 67, -39, 117, 123, 11, 105, -6, -5, -79, 19, -10, -29, 20, -14, -40, 5, 90),
|
||||
chainCode = byteArrayOf(33, -97, 53, -112, 61, 112, -24, 74, -87, -85, -124, -4, 103, -94, -97, 76, -41, -27, 118, 33, 55, 121, -17, -52, 60, 122, 27, 25, 29, -76, 78, 11),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(-118, -77, -109, 8, -119, 101, -91, 46, -44, 15, 52, -64, -92, 5, -102, 126, 52, 121, 63, -76, -54, 80, -82, -5, 31, -35, 105, -119, -96, -54, 38, 2, -6, 109, 117, -26, -47, -20, 108, 106, -69, 72, -37, -117, -30, -9, 104, -123),
|
||||
chainCode = null,
|
||||
curve = EllipticCurve.Bls12381G2Aug,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 1,
|
||||
remainingSignatures = null,
|
||||
index = 2,
|
||||
hasBackup = true,
|
||||
derivedKeys = emptyMap(),
|
||||
extendedPublicKey = null,
|
||||
isImported = false,
|
||||
),
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(17, -78, 53, 101, 96, -110, -10, -48, -84, 88, -64, 58, -26, -13, -2, -6, -25, 23, -79, -45, 58, 77, 52, -75, -123, 121, 32, 92, 84, -74, -111, -8),
|
||||
chainCode = byteArrayOf(-110, 73, -124, -65, -1, -70, 64, -89, 95, -74, 15, 46, -110, 87, 15, -61, 120, -51, 111, 111, -45, 37, 123, -114, 65, -116, 21, 39, -77, 86, -3, -26),
|
||||
curve = EllipticCurve.Bip0340,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 3,
|
||||
hasBackup = true,
|
||||
derivedKeys = emptyMap(),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(115, 14, 11, 0, -93, 81, -103, -95, -75, -84, 18, -120, -31, 76, -83, -81, 91, 25, -75, 36, -99, -53, -25, -15, -1, -57, 14, -39, 98, -116, -63, -123),
|
||||
chainCode = byteArrayOf(23, 5, 38, -48, 67, -42, -31, -21, 89, 11, 22, -28, 44, -19, -115, -78, 123, -27, 57, 57, -24, -86, 55, 15, 104, 114, -36, 80, 81, -108, -41, 112),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(-52, -92, 64, 108, -8, -100, 87, -86, -60, 18, -18, -81, 114, -84, 76, -24, 84, 52, -34, 79, -30, -66, -112, -55, -35, -119, 127, -109, 35, 18, -29, -25),
|
||||
chainCode = byteArrayOf(94, -33, -94, -63, 40, -20, -26, 71, 111, 9, 87, -36, -42, -33, 40, -53, 85, 31, -36, -29, 65, -121, 2, -23, -119, -51, 114, -17, -39, -74, 23, -97),
|
||||
curve = EllipticCurve.Ed25519Slip0010,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 4,
|
||||
hasBackup = true,
|
||||
derivedKeys = emptyMap(),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(-43, 1, -81, -47, -8, -103, -66, 42, 37, -7, 65, 54, 57, -24, 127, -89, 69, -112, 42, -46, -128, 36, -117, -28, 30, -48, 37, 52, 93, -47, 92, -47),
|
||||
chainCode = byteArrayOf(4, -97, 81, -37, 76, -67, -87, -4, -82, -36, -45, -28, -117, -59, -62, 93, -73, 50, 65, -91, -83, 25, -95, 89, -64, -40, 113, 28, 59, 113, -99, 89),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
),
|
||||
attestation = Attestation(
|
||||
cardKeyAttestation = Attestation.Status.Verified,
|
||||
walletKeysAttestation = Attestation.Status.Skipped,
|
||||
firmwareAttestation = Attestation.Status.Skipped,
|
||||
cardUniquenessAttestation = Attestation.Status.Skipped,
|
||||
),
|
||||
backupStatus = CardDTO.BackupStatus.Active(1),
|
||||
)
|
||||
|
||||
override val scanResponse = ScanResponse(
|
||||
card = cardDto,
|
||||
productType = ProductType.Wallet2,
|
||||
walletData = null,
|
||||
secondTwinPublicKey = null,
|
||||
derivedKeys = emptyMap(),
|
||||
primaryCard = null,
|
||||
)
|
||||
|
||||
override val derivationTaskResponse = DerivationTaskResponse(
|
||||
entries = mapOf(
|
||||
ByteArrayKey(
|
||||
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),
|
||||
)
|
||||
to
|
||||
ExtendedPublicKeysMap(
|
||||
mapOf(
|
||||
DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc
|
||||
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
|
||||
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),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
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),
|
||||
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'/145'/0'/0/0") to ExtendedPublicKey( // bch
|
||||
publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121),
|
||||
chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge
|
||||
publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124),
|
||||
chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
override val extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, -93, -36, -105, 121, -52, -30, -43, -67, -7, -31, -26, -35, 25, 99, 25, -20, 118, -20, -125, -89, 12, -101, -86, 74, -91, 23, -24, 93, -86, 20, -53, -8),
|
||||
chainCode = byteArrayOf(32, 60, 63, -96, 97, 58, 121, 108, 75, 59, 63, -113, 60, -49, 47, 33, 15, -65, -69, 45, -7, -26, 65, -5, -91, -55, -42, -102, -127, -104, -111, 96),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
)
|
||||
|
||||
override val successResponse = SuccessResponse(cardId = "BB00004000000000")
|
||||
|
||||
override val createProductWalletTaskResponse = CreateProductWalletTaskResponse(
|
||||
card = cardDto,
|
||||
derivedKeys = mapOf(
|
||||
ByteArrayKey(
|
||||
byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5),
|
||||
)
|
||||
to
|
||||
ExtendedPublicKeysMap(
|
||||
mapOf(
|
||||
DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc
|
||||
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
|
||||
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),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
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),
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
primaryCard = primaryCard,
|
||||
)
|
||||
|
||||
override val importWalletResponse: CreateProductWalletTaskResponse
|
||||
get() = TODO("Not yet implemented")
|
||||
|
||||
override val createFirstTwinResponse: CreateWalletResponse
|
||||
get() = error("Available only for Twin")
|
||||
|
||||
override val createSecondTwinResponse: CreateWalletResponse
|
||||
get() = error("Available only for Twin")
|
||||
|
||||
override val finalizeTwinResponse: ScanResponse
|
||||
get() = error("Available only for Twin")
|
||||
}
|
||||
|
|
@ -1,307 +0,0 @@
|
|||
package com.tangem.tap.domain.sdk.mocks.content
|
||||
|
||||
import com.tangem.common.SuccessResponse
|
||||
import com.tangem.common.card.*
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ProductType
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.operations.attestation.Attestation
|
||||
import com.tangem.operations.backup.PrimaryCard
|
||||
import com.tangem.operations.derivation.DerivationTaskResponse
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||
import com.tangem.operations.wallet.CreateWalletResponse
|
||||
import com.tangem.sdk.api.CreateProductWalletTaskResponse
|
||||
import com.tangem.tap.domain.sdk.mocks.MockContent
|
||||
import java.util.Date
|
||||
|
||||
object MetaplanetMockContent : MockContent {
|
||||
|
||||
private val primaryCard = PrimaryCard(
|
||||
cardId = "BB00004000000000",
|
||||
batchId = "BB000040",
|
||||
cardPublicKey = byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5),
|
||||
linkingKey = byteArrayOf( //
|
||||
2, 121, 98, 127, -70, 14, 5, -23, -76, 115, -30, -26, 111, 17, 110, 34, -100, -121,
|
||||
-57, -123, 74, 3, -91, 56, -20, 56, 50, -40, -101, 96, 82, 70, -91,
|
||||
),
|
||||
existingWalletsCount = 5, isHDWalletAllowed = true,
|
||||
issuer = Card.Issuer(
|
||||
name = "TANGEM SDK",
|
||||
publicKey = byteArrayOf(2, 95, 22, -67, 29, 46, -81, -28, 99, -26, 42, 51, 90, 9, -26, -78, -69, -53, -48, 68, 82, 82, 104, -123, -53, 103, -97, -60, -46, 122, -15, -67, 34),
|
||||
),
|
||||
manufacturer = Card.Manufacturer(
|
||||
name = "TANGEM",
|
||||
manufactureDate = Date(1743759687),
|
||||
signature = byteArrayOf(),
|
||||
),
|
||||
walletCurves = listOf(
|
||||
EllipticCurve.Secp256k1,
|
||||
EllipticCurve.Ed25519,
|
||||
EllipticCurve.Bls12381G2Aug,
|
||||
EllipticCurve.Secp256r1,
|
||||
EllipticCurve.Ed25519Slip0010,
|
||||
EllipticCurve.Bls12381G2,
|
||||
EllipticCurve.Bls12381G2Pop,
|
||||
EllipticCurve.Bip0340,
|
||||
),
|
||||
firmwareVersion = FirmwareVersion(
|
||||
major = 6,
|
||||
minor = 33,
|
||||
patch = 0,
|
||||
type = FirmwareVersion.FirmwareType.Release,
|
||||
),
|
||||
isKeysImportAllowed = false,
|
||||
certificate = null,
|
||||
)
|
||||
|
||||
override val cardDto = CardDTO(
|
||||
cardId = "BB00004000000000",
|
||||
batchId = "BB000040",
|
||||
cardPublicKey = byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5),
|
||||
firmwareVersion = CardDTO.FirmwareVersion(
|
||||
major = 6,
|
||||
minor = 33,
|
||||
patch = 0,
|
||||
type = FirmwareVersion.FirmwareType.Release,
|
||||
),
|
||||
manufacturer = CardDTO.Manufacturer(
|
||||
name = "TANGEM",
|
||||
manufactureDate = Date(1698094800000),
|
||||
signature = byteArrayOf(51, -12, 14, -56, 7, -39, 5, 63, 59, 24, 102, 99, -126, 124, -127, -108, -118, 71, -19, -71, 4, -47, -121, -46, 49, -51, -31, 100, -56, -15, 96, -37, 25, 82, 94, -88, 48, 98, -105, -97, -40, 41, 27, 116, 65, 26, 78, -85, 66, -94, -92, 15, 50, -2, 7, -69, 41, 56, -75, 59, 86, 68, -38, -3),
|
||||
),
|
||||
issuer = CardDTO.Issuer(
|
||||
name = "TANGEM SDK",
|
||||
publicKey = byteArrayOf(2, 95, 22, -67, 29, 46, -81, -28, 99, -26, 42, 51, 90, 9, -26, -78, -69, -53, -48, 68, 82, 82, 104, -123, -53, 103, -97, -60, -46, 122, -15, -67, 34),
|
||||
),
|
||||
settings = CardDTO.Settings(
|
||||
securityDelay = 15000,
|
||||
maxWalletsCount = 20,
|
||||
isSettingAccessCodeAllowed = true,
|
||||
isSettingPasscodeAllowed = true,
|
||||
isResettingUserCodesAllowed = false,
|
||||
isLinkedTerminalEnabled = true,
|
||||
isBackupAllowed = true,
|
||||
supportedEncryptionModes = listOf(EncryptionMode.Strong, EncryptionMode.Fast, EncryptionMode.None),
|
||||
isFilesAllowed = true,
|
||||
isHDWalletAllowed = true,
|
||||
isKeysImportAllowed = true,
|
||||
),
|
||||
userSettings = CardDTO.UserSettings(isUserCodeRecoveryAllowed = true),
|
||||
linkedTerminalStatus = CardDTO.LinkedTerminalStatus.None,
|
||||
isAccessCodeSet = true,
|
||||
isPasscodeSet = false,
|
||||
supportedCurves = listOf(
|
||||
EllipticCurve.Secp256k1,
|
||||
EllipticCurve.Ed25519,
|
||||
EllipticCurve.Bls12381G2Aug,
|
||||
EllipticCurve.Secp256r1,
|
||||
EllipticCurve.Ed25519Slip0010,
|
||||
EllipticCurve.Bls12381G2,
|
||||
EllipticCurve.Bls12381G2Pop,
|
||||
EllipticCurve.Bip0340,
|
||||
),
|
||||
wallets = listOf(
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(3, 17, 59, -102, -56, 66, 10, 36, 97, -106, -92, -117, -105, -88, -2, -3, -95, -75, -54, -2, 57, 27, -90, -19, 82, 103, -12, -52, -126, -105, -120, -55, -2),
|
||||
chainCode = byteArrayOf(-34, -28, -28, -12, 80, -109, -90, -31, -74, 36, -78, -21, 39, -125, -39, -91, 63, 40, -26, 3, 66, 27, -62, -55, -97, 52, -65, -11, 26, -80, 33, -45),
|
||||
curve = EllipticCurve.Secp256k1,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 0,
|
||||
hasBackup = true,
|
||||
derivedKeys = mapOf(
|
||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, 55, -114, -94, -73, -61, -50, 51, -115, 55, -79, 63, -96, -44, -64, -24, -36, -122, -123, -38, 81, -15, -127, -97, -42, 72, -85, -62, -98, 46, 119, 16, -55),
|
||||
chainCode = byteArrayOf(31, 17, 71, -28, -29, 17, 72, -29, -98, 112, 31, -8, 72, -75, 4, -11, 60, -100, 9, 35, 58, -42, -38, 96, -71, -68, 24, -119, -43, -18, -122, 72),
|
||||
),
|
||||
DerivationPath("m/84'/0'/0'/0/0") to ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, -26, 103, 96, 112, 127, -125, 0, 7, 53, 30, -12, -82, 45, 14, 107, -9, 126, 75, 104, -67, -49, 35, -12, -82, -90, 101, -101, 125, -76, 88, -54, 99),
|
||||
chainCode = byteArrayOf(-42, 36, 97, 65, -64, -113, 76, -91, -9, 11, 89, 123, -9, -3, 21, 103, -113, -60, 48, -31, -34, 108, 111, -38, -110, -80, 109, 17, -29, 2, 45, -71),
|
||||
),
|
||||
DerivationPath("m/44'/1'/0'/0/0") to ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, -96, -3, -83, 101, 63, -25, -125, -4, -65, -42, -56, 24, -52, 118, -11, -104, -105, 40, -59, 20, -109, -97, 29, -95, -6, -80, 2, 67, 103, -80, -22, -94),
|
||||
chainCode = byteArrayOf(-125, 27, -91, 38, -66, 109, -92, 16, -37, 93, 107, -29, -128, -1, 115, -64, 108, -63, 17, 27, 58, 78, -2, 39, 88, -39, 44, 89, 32, -38, -16, -38),
|
||||
),
|
||||
),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(-109, 42, -80, -115, -12, 44, 48, -118, -89, 10, 21, 59, 98, -110, -86, -14, 123, 105, -30, -49, -40, -4, -7, 32, 60, 67, -88, -52, -96, -10, -14, 123),
|
||||
chainCode = byteArrayOf(-101, -10, -92, 72, 19, 121, -38, 105, -55, -82, -68, 13, -6, 17, 66, -10, -75, 58, 119, -127, 74, 68, 82, 25, 45, -110, 3, 3, 108, -97, -51, -127),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(-109, 42, -80, -115, -12, 44, 48, -118, -89, 10, 21, 59, 98, -110, -86, -14, 123, 105, -30, -49, -40, -4, -7, 32, 60, 67, -88, -52, -96, -10, -14, 123),
|
||||
chainCode = byteArrayOf(-101, -10, -92, 72, 19, 121, -38, 105, -55, -82, -68, 13, -6, 17, 66, -10, -75, 58, 119, -127, 74, 68, 82, 25, 45, -110, 3, 3, 108, -97, -51, -127),
|
||||
curve = EllipticCurve.Ed25519,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 1,
|
||||
hasBackup = true,
|
||||
derivedKeys = emptyMap(),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(-64, 18, -128, 121, -89, -37, -99, 44, -125, -72, -111, -79, 7, 85, 40, 67, -39, 117, 123, 11, 105, -6, -5, -79, 19, -10, -29, 20, -14, -40, 5, 90),
|
||||
chainCode = byteArrayOf(33, -97, 53, -112, 61, 112, -24, 74, -87, -85, -124, -4, 103, -94, -97, 76, -41, -27, 118, 33, 55, 121, -17, -52, 60, 122, 27, 25, 29, -76, 78, 11),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(-118, -77, -109, 8, -119, 101, -91, 46, -44, 15, 52, -64, -92, 5, -102, 126, 52, 121, 63, -76, -54, 80, -82, -5, 31, -35, 105, -119, -96, -54, 38, 2, -6, 109, 117, -26, -47, -20, 108, 106, -69, 72, -37, -117, -30, -9, 104, -123),
|
||||
chainCode = null,
|
||||
curve = EllipticCurve.Bls12381G2Aug,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 1,
|
||||
remainingSignatures = null,
|
||||
index = 2,
|
||||
hasBackup = true,
|
||||
derivedKeys = emptyMap(),
|
||||
extendedPublicKey = null,
|
||||
isImported = false,
|
||||
),
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(17, -78, 53, 101, 96, -110, -10, -48, -84, 88, -64, 58, -26, -13, -2, -6, -25, 23, -79, -45, 58, 77, 52, -75, -123, 121, 32, 92, 84, -74, -111, -8),
|
||||
chainCode = byteArrayOf(-110, 73, -124, -65, -1, -70, 64, -89, 95, -74, 15, 46, -110, 87, 15, -61, 120, -51, 111, 111, -45, 37, 123, -114, 65, -116, 21, 39, -77, 86, -3, -26),
|
||||
curve = EllipticCurve.Bip0340,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 3,
|
||||
hasBackup = true,
|
||||
derivedKeys = emptyMap(),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(115, 14, 11, 0, -93, 81, -103, -95, -75, -84, 18, -120, -31, 76, -83, -81, 91, 25, -75, 36, -99, -53, -25, -15, -1, -57, 14, -39, 98, -116, -63, -123),
|
||||
chainCode = byteArrayOf(23, 5, 38, -48, 67, -42, -31, -21, 89, 11, 22, -28, 44, -19, -115, -78, 123, -27, 57, 57, -24, -86, 55, 15, 104, 114, -36, 80, 81, -108, -41, 112),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(-52, -92, 64, 108, -8, -100, 87, -86, -60, 18, -18, -81, 114, -84, 76, -24, 84, 52, -34, 79, -30, -66, -112, -55, -35, -119, 127, -109, 35, 18, -29, -25),
|
||||
chainCode = byteArrayOf(94, -33, -94, -63, 40, -20, -26, 71, 111, 9, 87, -36, -42, -33, 40, -53, 85, 31, -36, -29, 65, -121, 2, -23, -119, -51, 114, -17, -39, -74, 23, -97),
|
||||
curve = EllipticCurve.Ed25519Slip0010,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 4,
|
||||
hasBackup = true,
|
||||
derivedKeys = emptyMap(),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(-43, 1, -81, -47, -8, -103, -66, 42, 37, -7, 65, 54, 57, -24, 127, -89, 69, -112, 42, -46, -128, 36, -117, -28, 30, -48, 37, 52, 93, -47, 92, -47),
|
||||
chainCode = byteArrayOf(4, -97, 81, -37, 76, -67, -87, -4, -82, -36, -45, -28, -117, -59, -62, 93, -73, 50, 65, -91, -83, 25, -95, 89, -64, -40, 113, 28, 59, 113, -99, 89),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
),
|
||||
attestation = Attestation(
|
||||
cardKeyAttestation = Attestation.Status.Verified,
|
||||
walletKeysAttestation = Attestation.Status.Skipped,
|
||||
firmwareAttestation = Attestation.Status.Skipped,
|
||||
cardUniquenessAttestation = Attestation.Status.Skipped,
|
||||
),
|
||||
backupStatus = CardDTO.BackupStatus.Active(2),
|
||||
)
|
||||
|
||||
override val scanResponse = ScanResponse(
|
||||
card = cardDto,
|
||||
productType = ProductType.Wallet2,
|
||||
walletData = null,
|
||||
secondTwinPublicKey = null,
|
||||
derivedKeys = emptyMap(),
|
||||
primaryCard = null,
|
||||
)
|
||||
|
||||
override val derivationTaskResponse = DerivationTaskResponse(
|
||||
entries = mapOf(
|
||||
ByteArrayKey(
|
||||
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),
|
||||
)
|
||||
to
|
||||
ExtendedPublicKeysMap(
|
||||
mapOf(
|
||||
DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc
|
||||
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
|
||||
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),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
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),
|
||||
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'/145'/0'/0/0") to ExtendedPublicKey( // bch
|
||||
publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121),
|
||||
chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge
|
||||
publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124),
|
||||
chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
override val extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, -93, -36, -105, 121, -52, -30, -43, -67, -7, -31, -26, -35, 25, 99, 25, -20, 118, -20, -125, -89, 12, -101, -86, 74, -91, 23, -24, 93, -86, 20, -53, -8),
|
||||
chainCode = byteArrayOf(32, 60, 63, -96, 97, 58, 121, 108, 75, 59, 63, -113, 60, -49, 47, 33, 15, -65, -69, 45, -7, -26, 65, -5, -91, -55, -42, -102, -127, -104, -111, 96),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
)
|
||||
|
||||
override val successResponse = SuccessResponse(cardId = "BB00004000000000")
|
||||
|
||||
override val createProductWalletTaskResponse = CreateProductWalletTaskResponse(
|
||||
card = cardDto,
|
||||
derivedKeys = mapOf(
|
||||
ByteArrayKey(
|
||||
byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5),
|
||||
)
|
||||
to
|
||||
ExtendedPublicKeysMap(
|
||||
mapOf(
|
||||
DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc
|
||||
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
|
||||
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),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
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),
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
primaryCard = primaryCard,
|
||||
)
|
||||
|
||||
override val importWalletResponse: CreateProductWalletTaskResponse
|
||||
get() = TODO("Not yet implemented")
|
||||
|
||||
override val createFirstTwinResponse: CreateWalletResponse
|
||||
get() = error("Available only for Twin")
|
||||
|
||||
override val createSecondTwinResponse: CreateWalletResponse
|
||||
get() = error("Available only for Twin")
|
||||
|
||||
override val finalizeTwinResponse: ScanResponse
|
||||
get() = error("Available only for Twin")
|
||||
}
|
||||
|
|
@ -1,307 +0,0 @@
|
|||
package com.tangem.tap.domain.sdk.mocks.content
|
||||
|
||||
import com.tangem.common.SuccessResponse
|
||||
import com.tangem.common.card.*
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ProductType
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.operations.attestation.Attestation
|
||||
import com.tangem.operations.backup.PrimaryCard
|
||||
import com.tangem.operations.derivation.DerivationTaskResponse
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||
import com.tangem.operations.wallet.CreateWalletResponse
|
||||
import com.tangem.sdk.api.CreateProductWalletTaskResponse
|
||||
import com.tangem.tap.domain.sdk.mocks.MockContent
|
||||
import java.util.Date
|
||||
|
||||
object RedPandaDoubleMockContent : MockContent {
|
||||
|
||||
private val primaryCard = PrimaryCard(
|
||||
cardId = "BB00003800000000",
|
||||
batchId = "BB000038",
|
||||
cardPublicKey = byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5),
|
||||
linkingKey = byteArrayOf( //
|
||||
2, 121, 98, 127, -70, 14, 5, -23, -76, 115, -30, -26, 111, 17, 110, 34, -100, -121,
|
||||
-57, -123, 74, 3, -91, 56, -20, 56, 50, -40, -101, 96, 82, 70, -91,
|
||||
),
|
||||
existingWalletsCount = 5, isHDWalletAllowed = true,
|
||||
issuer = Card.Issuer(
|
||||
name = "TANGEM SDK",
|
||||
publicKey = byteArrayOf(2, 95, 22, -67, 29, 46, -81, -28, 99, -26, 42, 51, 90, 9, -26, -78, -69, -53, -48, 68, 82, 82, 104, -123, -53, 103, -97, -60, -46, 122, -15, -67, 34),
|
||||
),
|
||||
manufacturer = Card.Manufacturer(
|
||||
name = "TANGEM",
|
||||
manufactureDate = Date(1743759687),
|
||||
signature = byteArrayOf(),
|
||||
),
|
||||
walletCurves = listOf(
|
||||
EllipticCurve.Secp256k1,
|
||||
EllipticCurve.Ed25519,
|
||||
EllipticCurve.Bls12381G2Aug,
|
||||
EllipticCurve.Secp256r1,
|
||||
EllipticCurve.Ed25519Slip0010,
|
||||
EllipticCurve.Bls12381G2,
|
||||
EllipticCurve.Bls12381G2Pop,
|
||||
EllipticCurve.Bip0340,
|
||||
),
|
||||
firmwareVersion = FirmwareVersion(
|
||||
major = 6,
|
||||
minor = 33,
|
||||
patch = 0,
|
||||
type = FirmwareVersion.FirmwareType.Release,
|
||||
),
|
||||
isKeysImportAllowed = false,
|
||||
certificate = null,
|
||||
)
|
||||
|
||||
override val cardDto = CardDTO(
|
||||
cardId = "BB00003800000000",
|
||||
batchId = "BB000038",
|
||||
cardPublicKey = byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5),
|
||||
firmwareVersion = CardDTO.FirmwareVersion(
|
||||
major = 6,
|
||||
minor = 33,
|
||||
patch = 0,
|
||||
type = FirmwareVersion.FirmwareType.Release,
|
||||
),
|
||||
manufacturer = CardDTO.Manufacturer(
|
||||
name = "TANGEM",
|
||||
manufactureDate = Date(1698094800000),
|
||||
signature = byteArrayOf(51, -12, 14, -56, 7, -39, 5, 63, 59, 24, 102, 99, -126, 124, -127, -108, -118, 71, -19, -71, 4, -47, -121, -46, 49, -51, -31, 100, -56, -15, 96, -37, 25, 82, 94, -88, 48, 98, -105, -97, -40, 41, 27, 116, 65, 26, 78, -85, 66, -94, -92, 15, 50, -2, 7, -69, 41, 56, -75, 59, 86, 68, -38, -3),
|
||||
),
|
||||
issuer = CardDTO.Issuer(
|
||||
name = "TANGEM SDK",
|
||||
publicKey = byteArrayOf(2, 95, 22, -67, 29, 46, -81, -28, 99, -26, 42, 51, 90, 9, -26, -78, -69, -53, -48, 68, 82, 82, 104, -123, -53, 103, -97, -60, -46, 122, -15, -67, 34),
|
||||
),
|
||||
settings = CardDTO.Settings(
|
||||
securityDelay = 15000,
|
||||
maxWalletsCount = 20,
|
||||
isSettingAccessCodeAllowed = true,
|
||||
isSettingPasscodeAllowed = true,
|
||||
isResettingUserCodesAllowed = false,
|
||||
isLinkedTerminalEnabled = true,
|
||||
isBackupAllowed = true,
|
||||
supportedEncryptionModes = listOf(EncryptionMode.Strong, EncryptionMode.Fast, EncryptionMode.None),
|
||||
isFilesAllowed = true,
|
||||
isHDWalletAllowed = true,
|
||||
isKeysImportAllowed = true,
|
||||
),
|
||||
userSettings = CardDTO.UserSettings(isUserCodeRecoveryAllowed = true),
|
||||
linkedTerminalStatus = CardDTO.LinkedTerminalStatus.None,
|
||||
isAccessCodeSet = true,
|
||||
isPasscodeSet = false,
|
||||
supportedCurves = listOf(
|
||||
EllipticCurve.Secp256k1,
|
||||
EllipticCurve.Ed25519,
|
||||
EllipticCurve.Bls12381G2Aug,
|
||||
EllipticCurve.Secp256r1,
|
||||
EllipticCurve.Ed25519Slip0010,
|
||||
EllipticCurve.Bls12381G2,
|
||||
EllipticCurve.Bls12381G2Pop,
|
||||
EllipticCurve.Bip0340,
|
||||
),
|
||||
wallets = listOf(
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(3, 17, 59, -102, -56, 66, 10, 36, 97, -106, -92, -117, -105, -88, -2, -3, -95, -75, -54, -2, 57, 27, -90, -19, 82, 103, -12, -52, -126, -105, -120, -55, -2),
|
||||
chainCode = byteArrayOf(-34, -28, -28, -12, 80, -109, -90, -31, -74, 36, -78, -21, 39, -125, -39, -91, 63, 40, -26, 3, 66, 27, -62, -55, -97, 52, -65, -11, 26, -80, 33, -45),
|
||||
curve = EllipticCurve.Secp256k1,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 0,
|
||||
hasBackup = true,
|
||||
derivedKeys = mapOf(
|
||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, 55, -114, -94, -73, -61, -50, 51, -115, 55, -79, 63, -96, -44, -64, -24, -36, -122, -123, -38, 81, -15, -127, -97, -42, 72, -85, -62, -98, 46, 119, 16, -55),
|
||||
chainCode = byteArrayOf(31, 17, 71, -28, -29, 17, 72, -29, -98, 112, 31, -8, 72, -75, 4, -11, 60, -100, 9, 35, 58, -42, -38, 96, -71, -68, 24, -119, -43, -18, -122, 72),
|
||||
),
|
||||
DerivationPath("m/84'/0'/0'/0/0") to ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, -26, 103, 96, 112, 127, -125, 0, 7, 53, 30, -12, -82, 45, 14, 107, -9, 126, 75, 104, -67, -49, 35, -12, -82, -90, 101, -101, 125, -76, 88, -54, 99),
|
||||
chainCode = byteArrayOf(-42, 36, 97, 65, -64, -113, 76, -91, -9, 11, 89, 123, -9, -3, 21, 103, -113, -60, 48, -31, -34, 108, 111, -38, -110, -80, 109, 17, -29, 2, 45, -71),
|
||||
),
|
||||
DerivationPath("m/44'/1'/0'/0/0") to ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, -96, -3, -83, 101, 63, -25, -125, -4, -65, -42, -56, 24, -52, 118, -11, -104, -105, 40, -59, 20, -109, -97, 29, -95, -6, -80, 2, 67, 103, -80, -22, -94),
|
||||
chainCode = byteArrayOf(-125, 27, -91, 38, -66, 109, -92, 16, -37, 93, 107, -29, -128, -1, 115, -64, 108, -63, 17, 27, 58, 78, -2, 39, 88, -39, 44, 89, 32, -38, -16, -38),
|
||||
),
|
||||
),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(-109, 42, -80, -115, -12, 44, 48, -118, -89, 10, 21, 59, 98, -110, -86, -14, 123, 105, -30, -49, -40, -4, -7, 32, 60, 67, -88, -52, -96, -10, -14, 123),
|
||||
chainCode = byteArrayOf(-101, -10, -92, 72, 19, 121, -38, 105, -55, -82, -68, 13, -6, 17, 66, -10, -75, 58, 119, -127, 74, 68, 82, 25, 45, -110, 3, 3, 108, -97, -51, -127),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(-109, 42, -80, -115, -12, 44, 48, -118, -89, 10, 21, 59, 98, -110, -86, -14, 123, 105, -30, -49, -40, -4, -7, 32, 60, 67, -88, -52, -96, -10, -14, 123),
|
||||
chainCode = byteArrayOf(-101, -10, -92, 72, 19, 121, -38, 105, -55, -82, -68, 13, -6, 17, 66, -10, -75, 58, 119, -127, 74, 68, 82, 25, 45, -110, 3, 3, 108, -97, -51, -127),
|
||||
curve = EllipticCurve.Ed25519,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 1,
|
||||
hasBackup = true,
|
||||
derivedKeys = emptyMap(),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(-64, 18, -128, 121, -89, -37, -99, 44, -125, -72, -111, -79, 7, 85, 40, 67, -39, 117, 123, 11, 105, -6, -5, -79, 19, -10, -29, 20, -14, -40, 5, 90),
|
||||
chainCode = byteArrayOf(33, -97, 53, -112, 61, 112, -24, 74, -87, -85, -124, -4, 103, -94, -97, 76, -41, -27, 118, 33, 55, 121, -17, -52, 60, 122, 27, 25, 29, -76, 78, 11),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(-118, -77, -109, 8, -119, 101, -91, 46, -44, 15, 52, -64, -92, 5, -102, 126, 52, 121, 63, -76, -54, 80, -82, -5, 31, -35, 105, -119, -96, -54, 38, 2, -6, 109, 117, -26, -47, -20, 108, 106, -69, 72, -37, -117, -30, -9, 104, -123),
|
||||
chainCode = null,
|
||||
curve = EllipticCurve.Bls12381G2Aug,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 1,
|
||||
remainingSignatures = null,
|
||||
index = 2,
|
||||
hasBackup = true,
|
||||
derivedKeys = emptyMap(),
|
||||
extendedPublicKey = null,
|
||||
isImported = false,
|
||||
),
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(17, -78, 53, 101, 96, -110, -10, -48, -84, 88, -64, 58, -26, -13, -2, -6, -25, 23, -79, -45, 58, 77, 52, -75, -123, 121, 32, 92, 84, -74, -111, -8),
|
||||
chainCode = byteArrayOf(-110, 73, -124, -65, -1, -70, 64, -89, 95, -74, 15, 46, -110, 87, 15, -61, 120, -51, 111, 111, -45, 37, 123, -114, 65, -116, 21, 39, -77, 86, -3, -26),
|
||||
curve = EllipticCurve.Bip0340,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 3,
|
||||
hasBackup = true,
|
||||
derivedKeys = emptyMap(),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(115, 14, 11, 0, -93, 81, -103, -95, -75, -84, 18, -120, -31, 76, -83, -81, 91, 25, -75, 36, -99, -53, -25, -15, -1, -57, 14, -39, 98, -116, -63, -123),
|
||||
chainCode = byteArrayOf(23, 5, 38, -48, 67, -42, -31, -21, 89, 11, 22, -28, 44, -19, -115, -78, 123, -27, 57, 57, -24, -86, 55, 15, 104, 114, -36, 80, 81, -108, -41, 112),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(-52, -92, 64, 108, -8, -100, 87, -86, -60, 18, -18, -81, 114, -84, 76, -24, 84, 52, -34, 79, -30, -66, -112, -55, -35, -119, 127, -109, 35, 18, -29, -25),
|
||||
chainCode = byteArrayOf(94, -33, -94, -63, 40, -20, -26, 71, 111, 9, 87, -36, -42, -33, 40, -53, 85, 31, -36, -29, 65, -121, 2, -23, -119, -51, 114, -17, -39, -74, 23, -97),
|
||||
curve = EllipticCurve.Ed25519Slip0010,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 4,
|
||||
hasBackup = true,
|
||||
derivedKeys = emptyMap(),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(-43, 1, -81, -47, -8, -103, -66, 42, 37, -7, 65, 54, 57, -24, 127, -89, 69, -112, 42, -46, -128, 36, -117, -28, 30, -48, 37, 52, 93, -47, 92, -47),
|
||||
chainCode = byteArrayOf(4, -97, 81, -37, 76, -67, -87, -4, -82, -36, -45, -28, -117, -59, -62, 93, -73, 50, 65, -91, -83, 25, -95, 89, -64, -40, 113, 28, 59, 113, -99, 89),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
),
|
||||
attestation = Attestation(
|
||||
cardKeyAttestation = Attestation.Status.Verified,
|
||||
walletKeysAttestation = Attestation.Status.Skipped,
|
||||
firmwareAttestation = Attestation.Status.Skipped,
|
||||
cardUniquenessAttestation = Attestation.Status.Skipped,
|
||||
),
|
||||
backupStatus = CardDTO.BackupStatus.Active(1),
|
||||
)
|
||||
|
||||
override val scanResponse = ScanResponse(
|
||||
card = cardDto,
|
||||
productType = ProductType.Wallet2,
|
||||
walletData = null,
|
||||
secondTwinPublicKey = null,
|
||||
derivedKeys = emptyMap(),
|
||||
primaryCard = null,
|
||||
)
|
||||
|
||||
override val derivationTaskResponse = DerivationTaskResponse(
|
||||
entries = mapOf(
|
||||
ByteArrayKey(
|
||||
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),
|
||||
)
|
||||
to
|
||||
ExtendedPublicKeysMap(
|
||||
mapOf(
|
||||
DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc
|
||||
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
|
||||
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),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
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),
|
||||
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'/145'/0'/0/0") to ExtendedPublicKey( // bch
|
||||
publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121),
|
||||
chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge
|
||||
publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124),
|
||||
chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
override val extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, -93, -36, -105, 121, -52, -30, -43, -67, -7, -31, -26, -35, 25, 99, 25, -20, 118, -20, -125, -89, 12, -101, -86, 74, -91, 23, -24, 93, -86, 20, -53, -8),
|
||||
chainCode = byteArrayOf(32, 60, 63, -96, 97, 58, 121, 108, 75, 59, 63, -113, 60, -49, 47, 33, 15, -65, -69, 45, -7, -26, 65, -5, -91, -55, -42, -102, -127, -104, -111, 96),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
)
|
||||
|
||||
override val successResponse = SuccessResponse(cardId = "BB00003800000000")
|
||||
|
||||
override val createProductWalletTaskResponse = CreateProductWalletTaskResponse(
|
||||
card = cardDto,
|
||||
derivedKeys = mapOf(
|
||||
ByteArrayKey(
|
||||
byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5),
|
||||
)
|
||||
to
|
||||
ExtendedPublicKeysMap(
|
||||
mapOf(
|
||||
DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc
|
||||
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
|
||||
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),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
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),
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
primaryCard = primaryCard,
|
||||
)
|
||||
|
||||
override val importWalletResponse: CreateProductWalletTaskResponse
|
||||
get() = TODO("Not yet implemented")
|
||||
|
||||
override val createFirstTwinResponse: CreateWalletResponse
|
||||
get() = error("Available only for Twin")
|
||||
|
||||
override val createSecondTwinResponse: CreateWalletResponse
|
||||
get() = error("Available only for Twin")
|
||||
|
||||
override val finalizeTwinResponse: ScanResponse
|
||||
get() = error("Available only for Twin")
|
||||
}
|
||||
|
|
@ -1,307 +0,0 @@
|
|||
package com.tangem.tap.domain.sdk.mocks.content
|
||||
|
||||
import com.tangem.common.SuccessResponse
|
||||
import com.tangem.common.card.*
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ProductType
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.operations.attestation.Attestation
|
||||
import com.tangem.operations.backup.PrimaryCard
|
||||
import com.tangem.operations.derivation.DerivationTaskResponse
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||
import com.tangem.operations.wallet.CreateWalletResponse
|
||||
import com.tangem.sdk.api.CreateProductWalletTaskResponse
|
||||
import com.tangem.tap.domain.sdk.mocks.MockContent
|
||||
import java.util.Date
|
||||
|
||||
object RedPandaMockContent : MockContent {
|
||||
|
||||
private val primaryCard = PrimaryCard(
|
||||
cardId = "BB00003800000000",
|
||||
batchId = "BB000038",
|
||||
cardPublicKey = byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5),
|
||||
linkingKey = byteArrayOf( //
|
||||
2, 121, 98, 127, -70, 14, 5, -23, -76, 115, -30, -26, 111, 17, 110, 34, -100, -121,
|
||||
-57, -123, 74, 3, -91, 56, -20, 56, 50, -40, -101, 96, 82, 70, -91,
|
||||
),
|
||||
existingWalletsCount = 5, isHDWalletAllowed = true,
|
||||
issuer = Card.Issuer(
|
||||
name = "TANGEM SDK",
|
||||
publicKey = byteArrayOf(2, 95, 22, -67, 29, 46, -81, -28, 99, -26, 42, 51, 90, 9, -26, -78, -69, -53, -48, 68, 82, 82, 104, -123, -53, 103, -97, -60, -46, 122, -15, -67, 34),
|
||||
),
|
||||
manufacturer = Card.Manufacturer(
|
||||
name = "TANGEM",
|
||||
manufactureDate = Date(1743759687),
|
||||
signature = byteArrayOf(),
|
||||
),
|
||||
walletCurves = listOf(
|
||||
EllipticCurve.Secp256k1,
|
||||
EllipticCurve.Ed25519,
|
||||
EllipticCurve.Bls12381G2Aug,
|
||||
EllipticCurve.Secp256r1,
|
||||
EllipticCurve.Ed25519Slip0010,
|
||||
EllipticCurve.Bls12381G2,
|
||||
EllipticCurve.Bls12381G2Pop,
|
||||
EllipticCurve.Bip0340,
|
||||
),
|
||||
firmwareVersion = FirmwareVersion(
|
||||
major = 6,
|
||||
minor = 33,
|
||||
patch = 0,
|
||||
type = FirmwareVersion.FirmwareType.Release,
|
||||
),
|
||||
isKeysImportAllowed = false,
|
||||
certificate = null,
|
||||
)
|
||||
|
||||
override val cardDto = CardDTO(
|
||||
cardId = "BB00003800000000",
|
||||
batchId = "BB000038",
|
||||
cardPublicKey = byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5),
|
||||
firmwareVersion = CardDTO.FirmwareVersion(
|
||||
major = 6,
|
||||
minor = 33,
|
||||
patch = 0,
|
||||
type = FirmwareVersion.FirmwareType.Release,
|
||||
),
|
||||
manufacturer = CardDTO.Manufacturer(
|
||||
name = "TANGEM",
|
||||
manufactureDate = Date(1698094800000),
|
||||
signature = byteArrayOf(51, -12, 14, -56, 7, -39, 5, 63, 59, 24, 102, 99, -126, 124, -127, -108, -118, 71, -19, -71, 4, -47, -121, -46, 49, -51, -31, 100, -56, -15, 96, -37, 25, 82, 94, -88, 48, 98, -105, -97, -40, 41, 27, 116, 65, 26, 78, -85, 66, -94, -92, 15, 50, -2, 7, -69, 41, 56, -75, 59, 86, 68, -38, -3),
|
||||
),
|
||||
issuer = CardDTO.Issuer(
|
||||
name = "TANGEM SDK",
|
||||
publicKey = byteArrayOf(2, 95, 22, -67, 29, 46, -81, -28, 99, -26, 42, 51, 90, 9, -26, -78, -69, -53, -48, 68, 82, 82, 104, -123, -53, 103, -97, -60, -46, 122, -15, -67, 34),
|
||||
),
|
||||
settings = CardDTO.Settings(
|
||||
securityDelay = 15000,
|
||||
maxWalletsCount = 20,
|
||||
isSettingAccessCodeAllowed = true,
|
||||
isSettingPasscodeAllowed = true,
|
||||
isResettingUserCodesAllowed = false,
|
||||
isLinkedTerminalEnabled = true,
|
||||
isBackupAllowed = true,
|
||||
supportedEncryptionModes = listOf(EncryptionMode.Strong, EncryptionMode.Fast, EncryptionMode.None),
|
||||
isFilesAllowed = true,
|
||||
isHDWalletAllowed = true,
|
||||
isKeysImportAllowed = true,
|
||||
),
|
||||
userSettings = CardDTO.UserSettings(isUserCodeRecoveryAllowed = true),
|
||||
linkedTerminalStatus = CardDTO.LinkedTerminalStatus.None,
|
||||
isAccessCodeSet = true,
|
||||
isPasscodeSet = false,
|
||||
supportedCurves = listOf(
|
||||
EllipticCurve.Secp256k1,
|
||||
EllipticCurve.Ed25519,
|
||||
EllipticCurve.Bls12381G2Aug,
|
||||
EllipticCurve.Secp256r1,
|
||||
EllipticCurve.Ed25519Slip0010,
|
||||
EllipticCurve.Bls12381G2,
|
||||
EllipticCurve.Bls12381G2Pop,
|
||||
EllipticCurve.Bip0340,
|
||||
),
|
||||
wallets = listOf(
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(3, 17, 59, -102, -56, 66, 10, 36, 97, -106, -92, -117, -105, -88, -2, -3, -95, -75, -54, -2, 57, 27, -90, -19, 82, 103, -12, -52, -126, -105, -120, -55, -2),
|
||||
chainCode = byteArrayOf(-34, -28, -28, -12, 80, -109, -90, -31, -74, 36, -78, -21, 39, -125, -39, -91, 63, 40, -26, 3, 66, 27, -62, -55, -97, 52, -65, -11, 26, -80, 33, -45),
|
||||
curve = EllipticCurve.Secp256k1,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 0,
|
||||
hasBackup = true,
|
||||
derivedKeys = mapOf(
|
||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, 55, -114, -94, -73, -61, -50, 51, -115, 55, -79, 63, -96, -44, -64, -24, -36, -122, -123, -38, 81, -15, -127, -97, -42, 72, -85, -62, -98, 46, 119, 16, -55),
|
||||
chainCode = byteArrayOf(31, 17, 71, -28, -29, 17, 72, -29, -98, 112, 31, -8, 72, -75, 4, -11, 60, -100, 9, 35, 58, -42, -38, 96, -71, -68, 24, -119, -43, -18, -122, 72),
|
||||
),
|
||||
DerivationPath("m/84'/0'/0'/0/0") to ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, -26, 103, 96, 112, 127, -125, 0, 7, 53, 30, -12, -82, 45, 14, 107, -9, 126, 75, 104, -67, -49, 35, -12, -82, -90, 101, -101, 125, -76, 88, -54, 99),
|
||||
chainCode = byteArrayOf(-42, 36, 97, 65, -64, -113, 76, -91, -9, 11, 89, 123, -9, -3, 21, 103, -113, -60, 48, -31, -34, 108, 111, -38, -110, -80, 109, 17, -29, 2, 45, -71),
|
||||
),
|
||||
DerivationPath("m/44'/1'/0'/0/0") to ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, -96, -3, -83, 101, 63, -25, -125, -4, -65, -42, -56, 24, -52, 118, -11, -104, -105, 40, -59, 20, -109, -97, 29, -95, -6, -80, 2, 67, 103, -80, -22, -94),
|
||||
chainCode = byteArrayOf(-125, 27, -91, 38, -66, 109, -92, 16, -37, 93, 107, -29, -128, -1, 115, -64, 108, -63, 17, 27, 58, 78, -2, 39, 88, -39, 44, 89, 32, -38, -16, -38),
|
||||
),
|
||||
),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(-109, 42, -80, -115, -12, 44, 48, -118, -89, 10, 21, 59, 98, -110, -86, -14, 123, 105, -30, -49, -40, -4, -7, 32, 60, 67, -88, -52, -96, -10, -14, 123),
|
||||
chainCode = byteArrayOf(-101, -10, -92, 72, 19, 121, -38, 105, -55, -82, -68, 13, -6, 17, 66, -10, -75, 58, 119, -127, 74, 68, 82, 25, 45, -110, 3, 3, 108, -97, -51, -127),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(-109, 42, -80, -115, -12, 44, 48, -118, -89, 10, 21, 59, 98, -110, -86, -14, 123, 105, -30, -49, -40, -4, -7, 32, 60, 67, -88, -52, -96, -10, -14, 123),
|
||||
chainCode = byteArrayOf(-101, -10, -92, 72, 19, 121, -38, 105, -55, -82, -68, 13, -6, 17, 66, -10, -75, 58, 119, -127, 74, 68, 82, 25, 45, -110, 3, 3, 108, -97, -51, -127),
|
||||
curve = EllipticCurve.Ed25519,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 1,
|
||||
hasBackup = true,
|
||||
derivedKeys = emptyMap(),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(-64, 18, -128, 121, -89, -37, -99, 44, -125, -72, -111, -79, 7, 85, 40, 67, -39, 117, 123, 11, 105, -6, -5, -79, 19, -10, -29, 20, -14, -40, 5, 90),
|
||||
chainCode = byteArrayOf(33, -97, 53, -112, 61, 112, -24, 74, -87, -85, -124, -4, 103, -94, -97, 76, -41, -27, 118, 33, 55, 121, -17, -52, 60, 122, 27, 25, 29, -76, 78, 11),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(-118, -77, -109, 8, -119, 101, -91, 46, -44, 15, 52, -64, -92, 5, -102, 126, 52, 121, 63, -76, -54, 80, -82, -5, 31, -35, 105, -119, -96, -54, 38, 2, -6, 109, 117, -26, -47, -20, 108, 106, -69, 72, -37, -117, -30, -9, 104, -123),
|
||||
chainCode = null,
|
||||
curve = EllipticCurve.Bls12381G2Aug,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 1,
|
||||
remainingSignatures = null,
|
||||
index = 2,
|
||||
hasBackup = true,
|
||||
derivedKeys = emptyMap(),
|
||||
extendedPublicKey = null,
|
||||
isImported = false,
|
||||
),
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(17, -78, 53, 101, 96, -110, -10, -48, -84, 88, -64, 58, -26, -13, -2, -6, -25, 23, -79, -45, 58, 77, 52, -75, -123, 121, 32, 92, 84, -74, -111, -8),
|
||||
chainCode = byteArrayOf(-110, 73, -124, -65, -1, -70, 64, -89, 95, -74, 15, 46, -110, 87, 15, -61, 120, -51, 111, 111, -45, 37, 123, -114, 65, -116, 21, 39, -77, 86, -3, -26),
|
||||
curve = EllipticCurve.Bip0340,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 3,
|
||||
hasBackup = true,
|
||||
derivedKeys = emptyMap(),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(115, 14, 11, 0, -93, 81, -103, -95, -75, -84, 18, -120, -31, 76, -83, -81, 91, 25, -75, 36, -99, -53, -25, -15, -1, -57, 14, -39, 98, -116, -63, -123),
|
||||
chainCode = byteArrayOf(23, 5, 38, -48, 67, -42, -31, -21, 89, 11, 22, -28, 44, -19, -115, -78, 123, -27, 57, 57, -24, -86, 55, 15, 104, 114, -36, 80, 81, -108, -41, 112),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(-52, -92, 64, 108, -8, -100, 87, -86, -60, 18, -18, -81, 114, -84, 76, -24, 84, 52, -34, 79, -30, -66, -112, -55, -35, -119, 127, -109, 35, 18, -29, -25),
|
||||
chainCode = byteArrayOf(94, -33, -94, -63, 40, -20, -26, 71, 111, 9, 87, -36, -42, -33, 40, -53, 85, 31, -36, -29, 65, -121, 2, -23, -119, -51, 114, -17, -39, -74, 23, -97),
|
||||
curve = EllipticCurve.Ed25519Slip0010,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 4,
|
||||
hasBackup = true,
|
||||
derivedKeys = emptyMap(),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(-43, 1, -81, -47, -8, -103, -66, 42, 37, -7, 65, 54, 57, -24, 127, -89, 69, -112, 42, -46, -128, 36, -117, -28, 30, -48, 37, 52, 93, -47, 92, -47),
|
||||
chainCode = byteArrayOf(4, -97, 81, -37, 76, -67, -87, -4, -82, -36, -45, -28, -117, -59, -62, 93, -73, 50, 65, -91, -83, 25, -95, 89, -64, -40, 113, 28, 59, 113, -99, 89),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
),
|
||||
attestation = Attestation(
|
||||
cardKeyAttestation = Attestation.Status.Verified,
|
||||
walletKeysAttestation = Attestation.Status.Skipped,
|
||||
firmwareAttestation = Attestation.Status.Skipped,
|
||||
cardUniquenessAttestation = Attestation.Status.Skipped,
|
||||
),
|
||||
backupStatus = CardDTO.BackupStatus.Active(2),
|
||||
)
|
||||
|
||||
override val scanResponse = ScanResponse(
|
||||
card = cardDto,
|
||||
productType = ProductType.Wallet2,
|
||||
walletData = null,
|
||||
secondTwinPublicKey = null,
|
||||
derivedKeys = emptyMap(),
|
||||
primaryCard = null,
|
||||
)
|
||||
|
||||
override val derivationTaskResponse = DerivationTaskResponse(
|
||||
entries = mapOf(
|
||||
ByteArrayKey(
|
||||
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),
|
||||
)
|
||||
to
|
||||
ExtendedPublicKeysMap(
|
||||
mapOf(
|
||||
DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc
|
||||
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
|
||||
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),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
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),
|
||||
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'/145'/0'/0/0") to ExtendedPublicKey( // bch
|
||||
publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121),
|
||||
chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge
|
||||
publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124),
|
||||
chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
override val extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, -93, -36, -105, 121, -52, -30, -43, -67, -7, -31, -26, -35, 25, 99, 25, -20, 118, -20, -125, -89, 12, -101, -86, 74, -91, 23, -24, 93, -86, 20, -53, -8),
|
||||
chainCode = byteArrayOf(32, 60, 63, -96, 97, 58, 121, 108, 75, 59, 63, -113, 60, -49, 47, 33, 15, -65, -69, 45, -7, -26, 65, -5, -91, -55, -42, -102, -127, -104, -111, 96),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
)
|
||||
|
||||
override val successResponse = SuccessResponse(cardId = "BB00003800000000")
|
||||
|
||||
override val createProductWalletTaskResponse = CreateProductWalletTaskResponse(
|
||||
card = cardDto,
|
||||
derivedKeys = mapOf(
|
||||
ByteArrayKey(
|
||||
byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5),
|
||||
)
|
||||
to
|
||||
ExtendedPublicKeysMap(
|
||||
mapOf(
|
||||
DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc
|
||||
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
|
||||
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),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
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),
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
primaryCard = primaryCard,
|
||||
)
|
||||
|
||||
override val importWalletResponse: CreateProductWalletTaskResponse
|
||||
get() = TODO("Not yet implemented")
|
||||
|
||||
override val createFirstTwinResponse: CreateWalletResponse
|
||||
get() = error("Available only for Twin")
|
||||
|
||||
override val createSecondTwinResponse: CreateWalletResponse
|
||||
get() = error("Available only for Twin")
|
||||
|
||||
override val finalizeTwinResponse: ScanResponse
|
||||
get() = error("Available only for Twin")
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import arrow.core.right
|
|||
import com.tangem.common.*
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.analytics.utils.TrackingContextProxy
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
|
|
@ -19,10 +20,7 @@ import com.tangem.domain.common.wallets.UserWalletsListRepository
|
|||
import com.tangem.domain.common.wallets.UserWalletsListRepository.LockMethod
|
||||
import com.tangem.domain.common.wallets.error.*
|
||||
import com.tangem.domain.hotwallet.repository.HotWalletRepository
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.models.wallet.isImported
|
||||
import com.tangem.domain.models.wallet.isLocked
|
||||
import com.tangem.domain.models.wallet.*
|
||||
import com.tangem.domain.wallets.R
|
||||
import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents
|
||||
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
|
||||
|
|
@ -261,7 +259,7 @@ internal class DefaultUserWalletsListRepository(
|
|||
when (unlockMethod) {
|
||||
UserWalletsListRepository.UnlockMethod.Biometric -> {
|
||||
unlockAllWallets().bind()
|
||||
trackSignInEvent(userWallet, Basic.SignedIn.SignInType.Biometric)
|
||||
trackSignInEvent(userWallet, AnalyticsParam.SignInType.Biometric)
|
||||
select(userWalletId)
|
||||
}
|
||||
UserWalletsListRepository.UnlockMethod.AccessCode -> {
|
||||
|
|
@ -292,7 +290,7 @@ internal class DefaultUserWalletsListRepository(
|
|||
sensitiveInformationRepository.getAll(listOf(encryptionKey))
|
||||
.doOnSuccess { sensitiveInfo ->
|
||||
updateWallets { it?.updateWith(sensitiveInfo) }
|
||||
trackSignInEvent(userWallet, Basic.SignedIn.SignInType.AccessCode)
|
||||
trackSignInEvent(userWallet, AnalyticsParam.SignInType.AccessCode)
|
||||
}
|
||||
.doOnFailure { error -> raise(UnlockWalletError.UnableToUnlock.RawException(error)) }
|
||||
}
|
||||
|
|
@ -333,7 +331,7 @@ internal class DefaultUserWalletsListRepository(
|
|||
walletIdToDerivedKeys = mapOf(userWallet.walletId to scanResponse.derivedKeys),
|
||||
)
|
||||
}
|
||||
trackSignInEvent(userWallet, Basic.SignedIn.SignInType.Card)
|
||||
trackSignInEvent(userWallet, AnalyticsParam.SignInType.Card)
|
||||
}
|
||||
.doOnFailure { error -> raise(UnlockWalletError.UnableToUnlock.RawException(error)) }
|
||||
}
|
||||
|
|
@ -376,7 +374,7 @@ internal class DefaultUserWalletsListRepository(
|
|||
.doOnSuccess { sensitiveInfo ->
|
||||
updateWallets { wallets -> wallets?.updateWith(sensitiveInfo) }
|
||||
selectedUserWallet.value?.let {
|
||||
trackSignInEvent(it, Basic.SignedIn.SignInType.Biometric)
|
||||
trackSignInEvent(it, AnalyticsParam.SignInType.Biometric)
|
||||
}
|
||||
}
|
||||
.doOnFailure { error -> raise(UnlockWalletError.UnableToUnlock.RawException(error)) }
|
||||
|
|
@ -605,18 +603,14 @@ internal class DefaultUserWalletsListRepository(
|
|||
return lastOrNull()
|
||||
}
|
||||
|
||||
private fun trackSignInEvent(userWallet: UserWallet, type: Basic.SignedIn.SignInType) {
|
||||
private fun trackSignInEvent(userWallet: UserWallet, type: AnalyticsParam.SignInType) {
|
||||
trackingContextProxy.addContext(userWallet)
|
||||
val isBackedUp = when (userWallet) {
|
||||
is UserWallet.Cold -> userWallet.scanResponse.card.backupStatus?.isActive == true
|
||||
is UserWallet.Hot -> userWallet.backedUp
|
||||
}
|
||||
analyticsEventHandler.send(
|
||||
event = Basic.SignedIn(
|
||||
signInType = type,
|
||||
walletsCount = userWallets.value?.size ?: 0,
|
||||
isImported = userWallet.isImported(),
|
||||
hasBackup = isBackedUp,
|
||||
isBackedUp = userWallet.isBackedUpForAnalytics(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ import com.tangem.domain.notifications.SendPushTokenUseCase
|
|||
import com.tangem.domain.notifications.models.ApplicationId
|
||||
import com.tangem.domain.notifications.models.NotificationsError
|
||||
import com.tangem.domain.onramp.FetchHotCryptoUseCase
|
||||
import com.tangem.domain.promo.GetStoryContentUseCase
|
||||
import com.tangem.domain.promo.models.StoryContentIds
|
||||
import com.tangem.domain.stories.GetStoryContentUseCase
|
||||
import com.tangem.domain.stories.models.StoryContentIds
|
||||
import com.tangem.domain.quotes.multi.MultiQuoteUpdater
|
||||
import com.tangem.domain.settings.DeleteDeprecatedLogsUseCase
|
||||
import com.tangem.domain.settings.IncrementAppLaunchCounterUseCase
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.tap.network.auth
|
||||
|
||||
import com.tangem.lib.auth.ExpressAuthProvider
|
||||
import com.tangem.datasource.api.auth.ExpressAuthProvider
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.tangem.tap.network.auth
|
|||
|
||||
import com.tangem.datasource.local.config.environment.EnvironmentConfig
|
||||
import com.tangem.domain.staking.model.ethpool.P2PEthPoolStakingConfig
|
||||
import com.tangem.lib.auth.P2PEthPoolAuthProvider
|
||||
import com.tangem.datasource.api.auth.P2PEthPoolAuthProvider
|
||||
|
||||
internal class DefaultP2PEthPoolAuthProvider(
|
||||
private val environmentConfig: EnvironmentConfig,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.tap.network.auth
|
||||
|
||||
import com.tangem.datasource.local.config.environment.EnvironmentConfig
|
||||
import com.tangem.lib.auth.StakeKitAuthProvider
|
||||
import com.tangem.datasource.api.auth.StakeKitAuthProvider
|
||||
|
||||
internal class DefaultStakeKitAuthProvider(
|
||||
private val environmentConfig: EnvironmentConfig,
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ package com.tangem.tap.network.auth.di
|
|||
import com.tangem.datasource.api.common.AuthProvider
|
||||
import com.tangem.datasource.local.config.environment.EnvironmentConfig
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.lib.auth.ExpressAuthProvider
|
||||
import com.tangem.lib.auth.P2PEthPoolAuthProvider
|
||||
import com.tangem.lib.auth.StakeKitAuthProvider
|
||||
import com.tangem.datasource.api.auth.ExpressAuthProvider
|
||||
import com.tangem.datasource.api.auth.P2PEthPoolAuthProvider
|
||||
import com.tangem.datasource.api.auth.StakeKitAuthProvider
|
||||
import com.tangem.tap.network.auth.*
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
|
|||
|
|
@ -163,6 +163,7 @@ internal val Blockchain.moonPaySupportedCurrency: MoonPaySupportedCurrency?
|
|||
Linea, LineaTestnet -> null
|
||||
ArbitrumNova -> null
|
||||
Plasma, PlasmaTestnet -> null
|
||||
Adi, AdiTestnet -> null
|
||||
SeiEvm, SeiEvmTestnet -> null
|
||||
Monad, MonadTestnet -> null
|
||||
}
|
||||
|
|
@ -28,6 +28,7 @@ import com.arkivanov.decompose.value.Value
|
|||
import com.arkivanov.essenty.backhandler.BackHandler
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.ui.UiDependencies
|
||||
import com.tangem.core.ui.components.haze.ProvideHaze
|
||||
import com.tangem.core.ui.components.snackbar.TangemSnackbarHost
|
||||
import com.tangem.core.ui.components.snackbar.TangemTopSnackbarHost
|
||||
import com.tangem.core.ui.message.EventMessageEffect
|
||||
|
|
@ -72,7 +73,9 @@ internal fun RootContent(
|
|||
when (val instance = child.instance) {
|
||||
is RoutingComponent.Child.Initial -> Unit
|
||||
is RoutingComponent.Child.ComposableComponent -> {
|
||||
instance.component.Content(Modifier.fillMaxSize())
|
||||
ProvideHaze {
|
||||
instance.component.Content(Modifier.fillMaxSize())
|
||||
}
|
||||
}
|
||||
is RoutingComponent.Child.LegacyIntent -> {
|
||||
// TODO: Remove and use it's own router: [REDACTED_JIRA]
|
||||
|
|
|
|||
|
|
@ -14,9 +14,13 @@ import com.tangem.common.routing.entity.InitScreenLaunchMode
|
|||
import com.tangem.common.ui.notifications.NotificationId
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
|
||||
import com.tangem.core.analytics.models.event.OnboardingAnalyticsEvent
|
||||
import com.tangem.core.analytics.utils.TrackingContextProxy
|
||||
import com.tangem.core.configtoggle.FeatureToggles
|
||||
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.context.child
|
||||
import com.tangem.core.decompose.context.childByContext
|
||||
|
|
@ -27,10 +31,11 @@ import com.tangem.core.ui.extensions.resourceReference
|
|||
import com.tangem.core.ui.message.DialogMessage
|
||||
import com.tangem.core.ui.message.EventMessageAction
|
||||
import com.tangem.core.ui.message.SnackbarMessage
|
||||
import com.tangem.datasource.local.appsflyer.AppsFlyerDeeplinkSource
|
||||
import com.tangem.domain.card.repository.CardRepository
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.isBackedUpForAnalytics
|
||||
import com.tangem.domain.models.wallet.isImported
|
||||
import com.tangem.domain.models.wallet.isLocked
|
||||
import com.tangem.domain.notifications.repository.NotificationsRepository
|
||||
|
|
@ -38,6 +43,7 @@ import com.tangem.domain.onboarding.repository.OnboardingRepository
|
|||
import com.tangem.domain.settings.NeverRequestPermissionUseCase
|
||||
import com.tangem.domain.settings.NeverToInitiallyAskPermissionUseCase
|
||||
import com.tangem.domain.settings.ShouldInitiallyAskPermissionUseCase
|
||||
import com.tangem.feature.referral.domain.ShouldShowMobileWalletPromoUseCase
|
||||
import com.tangem.features.hotwallet.HotAccessCodeRequestComponent
|
||||
import com.tangem.features.hotwallet.accesscoderequest.proxy.HotWalletPasswordRequesterProxy
|
||||
import com.tangem.features.onboarding.v2.common.analytics.OnboardingEvent
|
||||
|
|
@ -47,7 +53,7 @@ import com.tangem.hot.sdk.TangemHotSdk
|
|||
import com.tangem.hot.sdk.android.create
|
||||
import com.tangem.sdk.api.BackupServiceHolder
|
||||
import com.tangem.tap.common.SnackbarHandler
|
||||
import com.tangem.tap.common.analytics.events.Onboarding
|
||||
import com.tangem.tap.common.analytics.appsflyer.AppsFlyerReferralParamsHandler
|
||||
import com.tangem.tap.features.hot.TangemHotSDKProxy
|
||||
import com.tangem.tap.features.root.RootDetectedWarningComponent
|
||||
import com.tangem.tap.features.scanfails.ScanFailsComponent
|
||||
|
|
@ -64,8 +70,10 @@ import dagger.assisted.Assisted
|
|||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Suppress("LongParameterList", "LargeClass")
|
||||
internal class DefaultRoutingComponent @AssistedInject constructor(
|
||||
@Assisted context: AppComponentContext,
|
||||
@Assisted val initialStack: List<AppRoute>?,
|
||||
|
|
@ -82,6 +90,7 @@ internal class DefaultRoutingComponent @AssistedInject constructor(
|
|||
private val userWalletsListRepository: UserWalletsListRepository,
|
||||
private val cardRepository: CardRepository,
|
||||
private val onboardingRepository: OnboardingRepository,
|
||||
private val appsFlyerReferralParamsHandler: AppsFlyerReferralParamsHandler,
|
||||
private val trackingContextProxy: TrackingContextProxy,
|
||||
private val scanFailsComponentFactory: ScanFailsComponent.Factory,
|
||||
private val scanFailsRequesterProxy: ScanFailsRequesterProxy,
|
||||
|
|
@ -92,6 +101,8 @@ internal class DefaultRoutingComponent @AssistedInject constructor(
|
|||
private val neverToInitiallyAskPermissionUseCase: NeverToInitiallyAskPermissionUseCase,
|
||||
private val shouldInitiallyAskPermissionUseCase: ShouldInitiallyAskPermissionUseCase,
|
||||
private val neverRequestPermissionUseCase: NeverRequestPermissionUseCase,
|
||||
private val featureTogglesManager: FeatureTogglesManager,
|
||||
private val shouldShowMobileWalletPromoUseCase: ShouldShowMobileWalletPromoUseCase,
|
||||
) : RoutingComponent,
|
||||
AppComponentContext by context,
|
||||
SnackbarHandler {
|
||||
|
|
@ -199,18 +210,54 @@ internal class DefaultRoutingComponent @AssistedInject constructor(
|
|||
}
|
||||
|
||||
private suspend fun navigateForEmptyWallets(): AppRoute {
|
||||
val isHotWalletOnboardingEnabled = featureTogglesManager.isFeatureEnabled(
|
||||
FeatureToggles.AND_15101_TANGEM_PAY_HOT_WALLET_ONBOARDING,
|
||||
)
|
||||
TangemLogger.i("[TangemPay][HWO] Feature toggle enabled=$isHotWalletOnboardingEnabled")
|
||||
if (isHotWalletOnboardingEnabled) {
|
||||
val tangemPayHotWalletOnboardingDeepLink = withTimeoutOrNull(2.seconds) {
|
||||
appsFlyerReferralParamsHandler.waitForDeeplink(AppsFlyerDeeplinkSource.TangemPayHotWalletOnboarding)
|
||||
}
|
||||
TangemLogger.i("[TangemPay][HWO] Deep link present=${tangemPayHotWalletOnboardingDeepLink != null}")
|
||||
if (tangemPayHotWalletOnboardingDeepLink != null) {
|
||||
val hotWalletRoute = AppRoute.TangemPayHotWalletOnboarding
|
||||
val shouldShowTos = !cardRepository.isTangemTOSAccepted()
|
||||
val route = if (shouldShowTos) "Disclaimer" else "HotWalletOnboarding"
|
||||
TangemLogger.i("[TangemPay][HWO] TOS accepted=${!shouldShowTos}, navigating to $route")
|
||||
return if (shouldShowTos) {
|
||||
AppRoute.Disclaimer(isTosAccepted = false, nextRoute = hotWalletRoute)
|
||||
} else {
|
||||
hotWalletRoute
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val isHideStoriesForReferralEnabled = featureTogglesManager.isFeatureEnabled(
|
||||
FeatureToggles.TWI_1512_HIDE_STORIES_FOR_REFERRAL_ENABLED,
|
||||
)
|
||||
// Referral users skip the Home stories screen and land directly on the
|
||||
// mobile wallet creation flow.
|
||||
val afterEmptyRoute: AppRoute = if (isHideStoriesForReferralEnabled && shouldShowMobileWalletPromoUseCase()) {
|
||||
AppRoute.CreateWalletStart(mode = AppRoute.CreateWalletStart.Mode.HotWallet)
|
||||
} else {
|
||||
AppRoute.Home(launchMode = launchMode)
|
||||
}
|
||||
|
||||
val shouldAskPushPermission = shouldInitiallyAskPermissionUseCase(PUSH_PERMISSION).getOrNull()
|
||||
?: return AppRoute.Home(launchMode = launchMode)
|
||||
?: return afterEmptyRoute
|
||||
return if (shouldAskPushPermission) {
|
||||
notificationsRepository.setShouldShowNotifications(
|
||||
key = NotificationId.EnablePushesReminderNotification.key,
|
||||
value = false,
|
||||
)
|
||||
AppRoute.PushNotification(AppRoute.PushNotification.Source.Stories)
|
||||
AppRoute.PushNotification(
|
||||
source = AppRoute.PushNotification.Source.Stories,
|
||||
nextRoute = afterEmptyRoute,
|
||||
)
|
||||
} else {
|
||||
neverToInitiallyAskPermissionUseCase(PUSH_PERMISSION)
|
||||
neverRequestPermissionUseCase(PUSH_PERMISSION)
|
||||
AppRoute.Home(launchMode = launchMode)
|
||||
afterEmptyRoute
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -352,7 +399,7 @@ internal class DefaultRoutingComponent @AssistedInject constructor(
|
|||
val unfinishedBackup = onboardingRepository.getUnfinishedFinalizeOnboarding() ?: return@launch
|
||||
cardRepository.finishCardActivation(unfinishedBackup.card.cardId)
|
||||
onboardingRepository.clearUnfinishedFinalizeOnboarding()
|
||||
analyticsEventHandler.send(Onboarding.Finished())
|
||||
analyticsEventHandler.send(OnboardingAnalyticsEvent.Onboarding.Finished())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -360,16 +407,12 @@ internal class DefaultRoutingComponent @AssistedInject constructor(
|
|||
val userWallets = userWalletsListRepository.userWalletsSync()
|
||||
val selectedWallet = userWalletsListRepository.selectedUserWalletSync() ?: return
|
||||
trackingContextProxy.addContext(selectedWallet)
|
||||
val isBackedUp = when (selectedWallet) {
|
||||
is UserWallet.Cold -> selectedWallet.scanResponse.card.backupStatus?.isActive == true
|
||||
is UserWallet.Hot -> selectedWallet.backedUp
|
||||
}
|
||||
analyticsEventHandler.send(
|
||||
event = Basic.SignedIn(
|
||||
signInType = Basic.SignedIn.SignInType.NoSecurity,
|
||||
signInType = AnalyticsParam.SignInType.NoSecurity,
|
||||
walletsCount = userWallets.size,
|
||||
isImported = selectedWallet.isImported(),
|
||||
hasBackup = isBackedUp,
|
||||
isBackedUp = selectedWallet.isBackedUpForAnalytics(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,12 @@ import androidx.compose.animation.core.CubicBezierEasing
|
|||
import androidx.compose.animation.core.FiniteAnimationSpec
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.CompositingStrategy
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.layout.layout
|
||||
import com.arkivanov.decompose.extensions.compose.stack.animation.*
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import kotlin.math.abs
|
||||
|
||||
object RoutingTransitionAnimationFactory {
|
||||
|
||||
|
|
@ -18,13 +20,15 @@ object RoutingTransitionAnimationFactory {
|
|||
is AppRoute.Home,
|
||||
-> fade(tween(400)).plus(scale(tween(400)))
|
||||
is AppRoute.Wallet,
|
||||
-> slideAndFade(directions = setOf(Direction.ENTER_BACK, Direction.EXIT_BACK))
|
||||
.plus(
|
||||
scaleWithDirection(
|
||||
directions = setOf(Direction.ENTER_FRONT, Direction.EXIT_FRONT),
|
||||
animationSpec = tween(400),
|
||||
),
|
||||
)
|
||||
-> slideAndFade(
|
||||
slideDirections = setOf(Direction.ENTER_BACK, Direction.EXIT_BACK),
|
||||
fadeDirections = emptySet(),
|
||||
).plus(
|
||||
scaleWithDirection(
|
||||
directions = setOf(Direction.ENTER_FRONT, Direction.EXIT_FRONT),
|
||||
animationSpec = tween(400),
|
||||
),
|
||||
)
|
||||
else -> slideAndFade()
|
||||
}
|
||||
}
|
||||
|
|
@ -52,28 +56,68 @@ object RoutingTransitionAnimationFactory {
|
|||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param slideDirections directions in which the horizontal slide is applied.
|
||||
* `null` (default) means slide in all directions.
|
||||
* @param fadeDirections directions in which the alpha fade is applied.
|
||||
* `null` (default) means fade in all directions. Pass `emptySet()` to disable the fade
|
||||
* entirely — useful for screens that own a `hazeEffect` (e.g. WalletTopBar's progressive
|
||||
* blur), where wrapping the screen in an animated `graphicsLayer { alpha = ... }` causes
|
||||
* a visible blink over the blurred region.
|
||||
*/
|
||||
@Suppress("MagicNumber")
|
||||
private fun slideAndFade(directions: Set<Direction>? = null): StackAnimator {
|
||||
private fun slideAndFade(
|
||||
slideDirections: Set<Direction>? = null,
|
||||
fadeDirections: Set<Direction>? = null,
|
||||
): StackAnimator {
|
||||
val easing = CubicBezierEasing(a = 0.55f, b = 0.0f, c = 0.0f, d = 1f)
|
||||
|
||||
return stackAnimator(
|
||||
val slide = stackAnimator(
|
||||
animationSpec = tween(durationMillis = 400, easing = easing),
|
||||
) { factor, direction, content ->
|
||||
content(
|
||||
if (directions == null || directions.contains(direction)) {
|
||||
if (slideDirections == null || slideDirections.contains(direction)) {
|
||||
Modifier.offsetXFactor(factor)
|
||||
} else {
|
||||
Modifier
|
||||
},
|
||||
)
|
||||
}.plus(
|
||||
fade(
|
||||
animationSpec = tween(
|
||||
delayMillis = 50,
|
||||
durationMillis = 300,
|
||||
easing = easing,
|
||||
),
|
||||
}
|
||||
|
||||
val fade = directionalFade(
|
||||
animationSpec = tween(
|
||||
delayMillis = 50,
|
||||
durationMillis = 300,
|
||||
easing = easing,
|
||||
),
|
||||
directions = fadeDirections,
|
||||
)
|
||||
|
||||
return slide.plus(fade)
|
||||
}
|
||||
|
||||
/**
|
||||
* Like `decompose.fade(...)` but only applies the alpha `graphicsLayer` when `direction`
|
||||
* is in [directions]. `null` directions = always fade (matches stock `fade()` behavior).
|
||||
* `emptySet()` directions = never fade (modifier passes through untouched).
|
||||
*
|
||||
* Uses [CompositingStrategy.ModulateAlpha] (not `Offscreen` and not the default `Auto`)
|
||||
* because screens that own a `hazeEffect` (e.g. `WalletTopBar`'s progressive blur) render
|
||||
* through a `RenderEffect`, which always allocates its own offscreen buffer.
|
||||
*/
|
||||
private fun directionalFade(
|
||||
animationSpec: FiniteAnimationSpec<Float>,
|
||||
directions: Set<Direction>?,
|
||||
): StackAnimator = stackAnimator(animationSpec) { factor, direction, content ->
|
||||
content(
|
||||
if (directions == null || directions.contains(direction)) {
|
||||
Modifier.graphicsLayer {
|
||||
alpha = 1f - abs(factor)
|
||||
compositingStrategy = CompositingStrategy.ModulateAlpha
|
||||
}
|
||||
} else {
|
||||
Modifier
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import com.tangem.feature.stories.api.StoriesComponent
|
|||
import com.tangem.feature.usedesk.api.UsedeskComponent
|
||||
import com.tangem.feature.walletsettings.component.WalletSettingsComponent
|
||||
import com.tangem.features.account.AccountCreateEditComponent
|
||||
import com.tangem.features.commonfeatures.api.addfunds.AddFundsComponent
|
||||
import com.tangem.features.account.AccountDetailsComponent
|
||||
import com.tangem.features.account.ArchivedAccountListComponent
|
||||
import com.tangem.features.createwalletselection.CreateWalletSelectionComponent
|
||||
|
|
@ -35,6 +36,7 @@ import com.tangem.features.send.v2.api.SendComponent
|
|||
import com.tangem.features.send.v2.api.SendEntryPointComponent
|
||||
import com.tangem.features.staking.api.StakingComponent
|
||||
import com.tangem.features.swap.SwapComponent
|
||||
import com.tangem.features.tangempay.components.TangemPayHotWalletOnboardingComponent
|
||||
import com.tangem.features.tangempay.components.TangemPayDetailsContainerComponent
|
||||
import com.tangem.features.tangempay.components.TangemPayOnboardingComponent
|
||||
import com.tangem.features.tangempay.components.TangemPayOnboardingComponent.Params.*
|
||||
|
|
@ -108,9 +110,11 @@ internal class ChildFactory @Inject constructor(
|
|||
private val sendEntryPointComponentFactory: SendEntryPointComponent.Factory,
|
||||
private val tangemPayDetailsContainerComponentFactory: TangemPayDetailsContainerComponent.Factory,
|
||||
private val tangemPayOnboardingComponentFactory: TangemPayOnboardingComponent.Factory,
|
||||
private val tangemPayWalletOnboardingComponentFactory: TangemPayHotWalletOnboardingComponent.Factory,
|
||||
private val kycComponentFactory: KycComponent.Factory,
|
||||
private val yieldSupplyEntryComponentFactory: YieldSupplyEntryComponent.Factory,
|
||||
private val feedEntryComponentFactory: FeedEntryComponent.Factory,
|
||||
private val addFundsComponentFactory: AddFundsComponent.Factory,
|
||||
) {
|
||||
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
|
|
@ -129,7 +133,10 @@ internal class ChildFactory @Inject constructor(
|
|||
is AppRoute.Disclaimer -> {
|
||||
createComponentChild(
|
||||
context = context,
|
||||
params = DisclaimerComponent.Params(route.isTosAccepted),
|
||||
params = DisclaimerComponent.Params(
|
||||
isTosAccepted = route.isTosAccepted,
|
||||
nextRoute = route.nextRoute,
|
||||
),
|
||||
componentFactory = disclaimerComponentFactory,
|
||||
)
|
||||
}
|
||||
|
|
@ -209,7 +216,6 @@ internal class ChildFactory @Inject constructor(
|
|||
userWalletId = route.userWalletId,
|
||||
cryptoCurrency = route.currency,
|
||||
source = route.source,
|
||||
shouldLaunchSepa = route.shouldLaunchSepa,
|
||||
),
|
||||
componentFactory = onrampComponentFactory,
|
||||
)
|
||||
|
|
@ -228,6 +234,13 @@ internal class ChildFactory @Inject constructor(
|
|||
componentFactory = buyCryptoComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.AddFunds -> {
|
||||
createComponentChild(
|
||||
context = context,
|
||||
params = AddFundsComponent.Params(userWalletId = route.userWalletId),
|
||||
componentFactory = addFundsComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.SellCrypto -> {
|
||||
createComponentChild(
|
||||
context = context,
|
||||
|
|
@ -277,6 +290,7 @@ internal class ChildFactory @Inject constructor(
|
|||
storyId = route.storyId,
|
||||
nextScreen = route.nextScreen,
|
||||
screenSource = route.screenSource,
|
||||
shouldMarkAsSeenOnClose = route.shouldMarkAsSeenOnClose,
|
||||
),
|
||||
componentFactory = storiesComponentFactory,
|
||||
)
|
||||
|
|
@ -320,7 +334,6 @@ internal class ChildFactory @Inject constructor(
|
|||
cryptoAmount = tangemPayInput.cryptoAmount,
|
||||
fiatAmount = tangemPayInput.fiatAmount,
|
||||
depositAddress = tangemPayInput.depositAddress,
|
||||
isWithdrawal = tangemPayInput.isWithdrawal,
|
||||
)
|
||||
},
|
||||
),
|
||||
|
|
@ -434,7 +447,7 @@ internal class ChildFactory @Inject constructor(
|
|||
params = PushNotificationsParams(
|
||||
modelCallbacks = PushNotificationsModelCallbacksStub(),
|
||||
source = route.source,
|
||||
nextRoute = AppRoute.Home(),
|
||||
nextRoute = route.nextRoute ?: AppRoute.Home(),
|
||||
),
|
||||
componentFactory = pushNotificationsComponentFactory,
|
||||
)
|
||||
|
|
@ -565,9 +578,10 @@ internal class ChildFactory @Inject constructor(
|
|||
params = CreateWalletBackupComponent.Params(
|
||||
userWalletId = route.userWalletId,
|
||||
isUpgradeFlow = route.isUpgradeFlow,
|
||||
shouldSetAccessCode = route.shouldSetAccessCode,
|
||||
analyticsSource = route.analyticsSource,
|
||||
analyticsAction = route.analyticsAction,
|
||||
nextScreen = route.nextScreen,
|
||||
shouldShowBackButton = route.shouldShowBackButton,
|
||||
),
|
||||
componentFactory = createWalletBackupComponentFactory,
|
||||
)
|
||||
|
|
@ -578,6 +592,8 @@ internal class ChildFactory @Inject constructor(
|
|||
params = UpdateAccessCodeComponent.Params(
|
||||
userWalletId = route.userWalletId,
|
||||
source = route.source,
|
||||
nextScreen = route.nextScreen,
|
||||
shouldShowBackButton = route.shouldShowBackButton,
|
||||
),
|
||||
componentFactory = updateAccessCodeComponentFactory,
|
||||
)
|
||||
|
|
@ -649,10 +665,7 @@ internal class ChildFactory @Inject constructor(
|
|||
is AppRoute.TangemPayDetails -> {
|
||||
createComponentChild(
|
||||
context = context,
|
||||
params = TangemPayDetailsContainerComponent.Params(
|
||||
userWalletId = route.userWalletId,
|
||||
config = route.config,
|
||||
),
|
||||
params = TangemPayDetailsContainerComponent.Params(initialStatus = route.status),
|
||||
componentFactory = tangemPayDetailsContainerComponentFactory,
|
||||
)
|
||||
}
|
||||
|
|
@ -663,6 +676,9 @@ internal class ChildFactory @Inject constructor(
|
|||
is AppRoute.TangemPayOnboarding.Mode.ContinueOnboarding -> ContinueOnboarding(
|
||||
userWalletId = mode.userWalletId,
|
||||
)
|
||||
is AppRoute.TangemPayOnboarding.Mode.FirstSetup -> HotWalletOnboarding(
|
||||
userWalletId = mode.userWalletId,
|
||||
)
|
||||
is AppRoute.TangemPayOnboarding.Mode.Deeplink -> Deeplink(
|
||||
deeplink = mode.deeplink,
|
||||
)
|
||||
|
|
@ -672,6 +688,13 @@ internal class ChildFactory @Inject constructor(
|
|||
componentFactory = tangemPayOnboardingComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.TangemPayHotWalletOnboarding -> {
|
||||
createComponentChild(
|
||||
context = context,
|
||||
params = Unit,
|
||||
componentFactory = tangemPayWalletOnboardingComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.Kyc -> {
|
||||
createComponentChild(
|
||||
context = context,
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import com.tangem.features.onramp.deeplink.SwapDeepLinkHandler
|
|||
import com.tangem.features.send.v2.api.deeplink.SellRedirectDeepLinkHandler
|
||||
import com.tangem.features.staking.api.deeplink.StakingDeepLinkHandler
|
||||
import com.tangem.features.tangempay.deeplink.OnboardVisaDeepLinkHandler
|
||||
import com.tangem.features.tangempay.deeplink.TangemPayMainDeepLinkHandler
|
||||
import com.tangem.features.tokendetails.deeplink.TokenDetailsDeepLinkHandler
|
||||
import com.tangem.features.wallet.deeplink.PromoDeeplinkHandler
|
||||
import com.tangem.features.wallet.deeplink.WalletDeepLinkHandler
|
||||
|
|
@ -56,6 +57,7 @@ internal class DeepLinkFactory @Inject constructor(
|
|||
private val promoDeepLink: PromoDeeplinkHandler.Factory,
|
||||
private val onboardVisaDeepLink: OnboardVisaDeepLinkHandler.Factory,
|
||||
private val marketsTokenExchangesDeepLink: MarketsTokenExchangesDeepLinkHandler.Factory,
|
||||
private val tangemPayMainDeepLink: TangemPayMainDeepLinkHandler.Factory,
|
||||
private val newsDetailsDeepLink: NewsDetailsDeepLinkHandler.Factory,
|
||||
private val newsDeepLink: NewsDeepLinkHandler.Factory,
|
||||
private val earnDeepLink: EarnDeepLinkHandler.Factory,
|
||||
|
|
@ -129,6 +131,10 @@ internal class DeepLinkFactory @Inject constructor(
|
|||
private fun handleHttpDeepLinks(deeplinkUri: Uri, coroutineScope: CoroutineScope) {
|
||||
if (deeplinkUri.host == DeepLinkRoute.PayApp.host) {
|
||||
when {
|
||||
deeplinkUri.path?.startsWith("/pay-app-main") == true -> {
|
||||
tangemPayMainDeepLink.create(coroutineScope, getQueryParams(deeplinkUri))
|
||||
return
|
||||
}
|
||||
deeplinkUri.path?.startsWith("/pay-app") == true -> {
|
||||
onboardVisaDeepLink.create(deeplinkUri)
|
||||
return
|
||||
|
|
@ -168,6 +174,7 @@ internal class DeepLinkFactory @Inject constructor(
|
|||
DeepLinkRoute.News.host -> newsDeepLink.create(queryParams)
|
||||
DeepLinkRoute.Earn.host -> earnDeepLink.create(queryParams)
|
||||
DeepLinkRoute.Yield.host -> yieldDeepLink.create(coroutineScope, queryParams)
|
||||
DeepLinkRoute.PayAppMain.host -> tangemPayMainDeepLink.create(coroutineScope, queryParams)
|
||||
else -> {
|
||||
TangemLogger.i(
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -3,5 +3,10 @@
|
|||
|
||||
<string name="tangem_app_name" translatable="false">Tangem</string>
|
||||
<string name="mock_card_picker_title" translatable="false">Select Mock Card</string>
|
||||
<string name="mock_cobrand_dialog_title" translatable="false">Cobrand parameters</string>
|
||||
<string name="mock_cobrand_batch_hint" translatable="false">Batch ID (e.g. AC05 or AF990090)</string>
|
||||
<string name="mock_cobrand_card_count_hint" translatable="false">Card count (2–3)</string>
|
||||
<string name="mock_cobrand_batch_error" translatable="false">Must be 4 or 8 hex characters (0–9, A–F)</string>
|
||||
<string name="mock_cobrand_card_count_error" translatable="false">Must be 2 or 3</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -29,15 +29,19 @@ class AppsFlyerDeepLinkListenerTest {
|
|||
@ProvideTestModels
|
||||
fun onDeepLinking(model: OnDeepLinkingModel) = runTest {
|
||||
if (model.shouldHandle) {
|
||||
every { referralParamsHandler.handle(deepLink = model.deepLinkResult.deepLink) } just Runs
|
||||
every { referralParamsHandler.handleDeeplink(deepLink = model.deepLinkResult.deepLink) } just Runs
|
||||
} else {
|
||||
every { referralParamsHandler.handleNoDeeplink() } just Runs
|
||||
}
|
||||
|
||||
listener.onDeepLinking(p0 = model.deepLinkResult)
|
||||
|
||||
if (model.shouldHandle) {
|
||||
coVerify { referralParamsHandler.handle(deepLink = model.deepLinkResult.deepLink) }
|
||||
coVerify { referralParamsHandler.handleDeeplink(deepLink = model.deepLinkResult.deepLink) }
|
||||
verify(inverse = true) { referralParamsHandler.handleNoDeeplink() }
|
||||
} else {
|
||||
coVerify(inverse = true) { referralParamsHandler.handle(deepLink = any()) }
|
||||
coVerify(inverse = true) { referralParamsHandler.handleDeeplink(deepLink = any()) }
|
||||
verify { referralParamsHandler.handleNoDeeplink() }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.tap.common.analytics.appsflyer
|
||||
|
||||
import com.appsflyer.deeplink.DeepLink
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.datasource.local.appsflyer.AppsFlyerDeeplinkSource
|
||||
import com.tangem.datasource.local.appsflyer.AppsFlyerStore
|
||||
import com.tangem.domain.wallets.models.AppsFlyerConversionData
|
||||
import com.tangem.feature.referral.domain.SetShouldShowMobileWalletPromoUseCase
|
||||
|
|
@ -15,6 +17,7 @@ import io.mockk.mockk
|
|||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import org.junit.jupiter.params.ParameterizedTest
|
||||
|
||||
|
|
@ -46,7 +49,7 @@ class AppsFlyerReferralParamsHandlerTest {
|
|||
@ParameterizedTest
|
||||
@ProvideTestModels
|
||||
fun handle(model: HandleDeepLinkModel) = runTest {
|
||||
handler.handle(deepLink = model.deepLink)
|
||||
handler.handleDeeplink(deepLink = model.deepLink)
|
||||
|
||||
if (model.shouldStore) {
|
||||
val value = AppsFlyerConversionData(refcode = SUCCESS_REFCODE, campaign = SUCCESS_CAMPAIGN)
|
||||
|
|
@ -165,6 +168,78 @@ class AppsFlyerReferralParamsHandlerTest {
|
|||
|
||||
data class HandleParamsModel(val params: Map<String?, Any?>, val shouldStore: Boolean)
|
||||
|
||||
@Nested
|
||||
inner class WaitForDeeplink {
|
||||
|
||||
private val localStore: AppsFlyerStore = mockk(relaxUnitFun = true)
|
||||
private val localHandler = AppsFlyerReferralParamsHandler(
|
||||
appsFlyerStore = localStore,
|
||||
coroutineScope = TestAppCoroutineScope(),
|
||||
setShouldShowMobileWalletPromoUseCase = mockk { coEvery { this@mockk.invoke(true) } returns Unit.right() },
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `GIVEN cached deeplink WHEN waitForDeeplink THEN returns cached value`() = runTest {
|
||||
// GIVEN
|
||||
coEvery {
|
||||
localStore.getDeeplink(AppsFlyerDeeplinkSource.TangemPayHotWalletOnboarding)
|
||||
} returns "tpay_mobileonboard"
|
||||
|
||||
// WHEN
|
||||
val result = localHandler.waitForDeeplink(AppsFlyerDeeplinkSource.TangemPayHotWalletOnboarding)
|
||||
|
||||
// THEN
|
||||
assertThat(result).isEqualTo("tpay_mobileonboard")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN no cache and matching deeplink WHEN handleDeeplink then waitForDeeplink THEN returns deeplink value`() = runTest {
|
||||
// GIVEN
|
||||
coEvery { localStore.getDeeplink(AppsFlyerDeeplinkSource.TangemPayHotWalletOnboarding) } returns null
|
||||
val deepLink = mockk<DeepLink> {
|
||||
every { deepLinkValue } returns "tpay_mobileonboard"
|
||||
every { getStringValue(any()) } returns null
|
||||
}
|
||||
|
||||
// WHEN
|
||||
localHandler.handleDeeplink(deepLink)
|
||||
val result = localHandler.waitForDeeplink(AppsFlyerDeeplinkSource.TangemPayHotWalletOnboarding)
|
||||
|
||||
// THEN
|
||||
assertThat(result).isEqualTo("tpay_mobileonboard")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN no cache and non-matching deeplink WHEN handleDeeplink then waitForDeeplink THEN returns null`() = runTest {
|
||||
// GIVEN
|
||||
coEvery { localStore.getDeeplink(AppsFlyerDeeplinkSource.TangemPayHotWalletOnboarding) } returns null
|
||||
val deepLink = mockk<DeepLink> {
|
||||
every { deepLinkValue } returns "referral"
|
||||
every { getStringValue(any()) } returns null
|
||||
}
|
||||
|
||||
// WHEN
|
||||
localHandler.handleDeeplink(deepLink)
|
||||
val result = localHandler.waitForDeeplink(AppsFlyerDeeplinkSource.TangemPayHotWalletOnboarding)
|
||||
|
||||
// THEN
|
||||
assertThat(result).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN no cache WHEN handleNoDeeplink then waitForDeeplink THEN returns null`() = runTest {
|
||||
// GIVEN
|
||||
coEvery { localStore.getDeeplink(AppsFlyerDeeplinkSource.TangemPayHotWalletOnboarding) } returns null
|
||||
|
||||
// WHEN
|
||||
localHandler.handleNoDeeplink()
|
||||
val result = localHandler.waitForDeeplink(AppsFlyerDeeplinkSource.TangemPayHotWalletOnboarding)
|
||||
|
||||
// THEN
|
||||
assertThat(result).isNull()
|
||||
}
|
||||
}
|
||||
|
||||
private companion object Companion {
|
||||
const val SUCCESS_REFCODE = "valid_refcode"
|
||||
const val SUCCESS_CAMPAIGN = "valid_campaign"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,163 @@
|
|||
package com.tangem.tap.common.libs.blockchainsdk
|
||||
|
||||
import arrow.core.right
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.common.test.TestAppCoroutineScope
|
||||
import com.tangem.common.test.domain.wallet.MockUserWalletFactory
|
||||
import com.tangem.core.analytics.models.Basic.TransactionSent.WalletForm
|
||||
import com.tangem.core.analytics.store.LastSignedWalletFormStore
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.tap.domain.TangemSignerResponse
|
||||
import io.mockk.clearMocks
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.slot
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
internal class DefaultTransactionSignerFactoryTest {
|
||||
|
||||
private val lastSignedWalletFormStore = mockk<LastSignedWalletFormStore>(relaxed = true)
|
||||
private val userWalletsListRepository = mockk<UserWalletsListRepository>()
|
||||
|
||||
private val factory = DefaultTransactionSignerFactory(
|
||||
lastSignedWalletFormStore = lastSignedWalletFormStore,
|
||||
userWalletsListRepository = userWalletsListRepository,
|
||||
coroutineScope = TestAppCoroutineScope(),
|
||||
)
|
||||
|
||||
private val baseWallet = MockUserWalletFactory.create()
|
||||
|
||||
/** Wallet that will be the target of the signing operation. */
|
||||
private val walletA = baseWallet.scanResponse.card.wallets.first().copy(
|
||||
publicKey = PUBLIC_KEY_A,
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = 100,
|
||||
)
|
||||
|
||||
/** Another wallet that must stay untouched after signing with [walletA]'s key. */
|
||||
private val walletB = baseWallet.scanResponse.card.wallets.first().copy(
|
||||
publicKey = PUBLIC_KEY_B,
|
||||
totalSignedHashes = 7,
|
||||
remainingSignatures = 50,
|
||||
)
|
||||
|
||||
private val userWallet = baseWallet.copy(
|
||||
scanResponse = baseWallet.scanResponse.copy(
|
||||
card = baseWallet.scanResponse.card.copy(wallets = listOf(walletA, walletB)),
|
||||
),
|
||||
)
|
||||
|
||||
private val savedWalletSlot = slot<UserWallet>()
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
clearMocks(lastSignedWalletFormStore, userWalletsListRepository)
|
||||
|
||||
every { userWalletsListRepository.userWallets } returns MutableStateFlow(listOf(userWallet))
|
||||
coEvery { userWalletsListRepository.saveWithoutLock(capture(savedWalletSlot), any()) } answers {
|
||||
savedWalletSlot.captured.right()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `updates signed hashes only for the wallet matching the signed public key`() {
|
||||
factory.onSignerResponse(
|
||||
userWalletId = userWallet.walletId,
|
||||
signResponse = signerResponse(
|
||||
signedWalletPublicKey = PUBLIC_KEY_A,
|
||||
totalSignedHashes = 5,
|
||||
remainingSignatures = 95,
|
||||
),
|
||||
)
|
||||
|
||||
val savedWallets = (savedWalletSlot.captured as UserWallet.Cold).scanResponse.card.wallets
|
||||
val savedA = savedWallets.first { it.publicKey.contentEquals(PUBLIC_KEY_A) }
|
||||
val savedB = savedWallets.first { it.publicKey.contentEquals(PUBLIC_KEY_B) }
|
||||
|
||||
assertThat(savedA.totalSignedHashes).isEqualTo(5)
|
||||
assertThat(savedA.remainingSignatures).isEqualTo(95)
|
||||
// The non-signed wallet must keep its original values.
|
||||
assertThat(savedB.totalSignedHashes).isEqualTo(7)
|
||||
assertThat(savedB.remainingSignatures).isEqualTo(50)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `keeps previously known counters when the signer response has null values`() {
|
||||
factory.onSignerResponse(
|
||||
userWalletId = userWallet.walletId,
|
||||
signResponse = signerResponse(
|
||||
signedWalletPublicKey = PUBLIC_KEY_A,
|
||||
totalSignedHashes = null,
|
||||
remainingSignatures = null,
|
||||
),
|
||||
)
|
||||
|
||||
val savedA = (savedWalletSlot.captured as UserWallet.Cold).scanResponse.card.wallets
|
||||
.first { it.publicKey.contentEquals(PUBLIC_KEY_A) }
|
||||
|
||||
// Null response values must not overwrite the known counters.
|
||||
assertThat(savedA.totalSignedHashes).isEqualTo(0)
|
||||
assertThat(savedA.remainingSignatures).isEqualTo(100)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `leaves all wallets untouched when no public key matches`() {
|
||||
factory.onSignerResponse(
|
||||
userWalletId = userWallet.walletId,
|
||||
signResponse = signerResponse(
|
||||
signedWalletPublicKey = UNKNOWN_PUBLIC_KEY,
|
||||
totalSignedHashes = 5,
|
||||
remainingSignatures = 95,
|
||||
),
|
||||
)
|
||||
|
||||
val savedWallets = (savedWalletSlot.captured as UserWallet.Cold).scanResponse.card.wallets
|
||||
assertThat(savedWallets.first { it.publicKey.contentEquals(PUBLIC_KEY_A) }.totalSignedHashes).isEqualTo(0)
|
||||
assertThat(savedWallets.first { it.publicKey.contentEquals(PUBLIC_KEY_B) }.totalSignedHashes).isEqualTo(7)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `updates last signed wallet form with Card for a non-ring response`() {
|
||||
factory.onSignerResponse(
|
||||
userWalletId = userWallet.walletId,
|
||||
signResponse = signerResponse(signedWalletPublicKey = PUBLIC_KEY_A, isRing = false),
|
||||
)
|
||||
|
||||
verify(exactly = 1) { lastSignedWalletFormStore.update(WalletForm.Card) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `updates last signed wallet form with Ring for a ring response`() {
|
||||
factory.onSignerResponse(
|
||||
userWalletId = userWallet.walletId,
|
||||
signResponse = signerResponse(signedWalletPublicKey = PUBLIC_KEY_A, isRing = true),
|
||||
)
|
||||
|
||||
verify(exactly = 1) { lastSignedWalletFormStore.update(WalletForm.Ring) }
|
||||
}
|
||||
|
||||
private fun signerResponse(
|
||||
signedWalletPublicKey: ByteArray,
|
||||
totalSignedHashes: Int? = 1,
|
||||
remainingSignatures: Int? = 1,
|
||||
isRing: Boolean = false,
|
||||
) = TangemSignerResponse(
|
||||
totalSignedHashes = totalSignedHashes,
|
||||
remainingSignatures = remainingSignatures,
|
||||
isRing = isRing,
|
||||
signedWalletPublicKey = signedWalletPublicKey,
|
||||
)
|
||||
|
||||
private companion object {
|
||||
val PUBLIC_KEY_A = byteArrayOf(1, 2, 3)
|
||||
val PUBLIC_KEY_B = byteArrayOf(4, 5, 6)
|
||||
val UNKNOWN_PUBLIC_KEY = byteArrayOf(9, 9, 9)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
package com.tangem.tap.common.pushes
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.common.routing.deeplink.DeeplinkConst.DERIVATION_PATH_KEY
|
||||
import com.tangem.common.routing.deeplink.DeeplinkConst.NETWORK_ID_KEY
|
||||
import com.tangem.common.routing.deeplink.DeeplinkConst.TOKEN_ID_KEY
|
||||
import com.tangem.common.routing.deeplink.DeeplinkConst.WALLET_ID_KEY
|
||||
import com.tangem.domain.account.fetcher.SingleAccountListFetcher
|
||||
import com.tangem.domain.account.models.AccountList
|
||||
import com.tangem.domain.account.supplier.SingleAccountListSupplier
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.models.wallet.isLocked
|
||||
import com.tangem.domain.models.wallet.isMultiCurrency
|
||||
import com.tangem.domain.wallets.models.GetUserWalletError
|
||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.utils.coroutines.AppCoroutineScope
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
import io.mockk.*
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class TokenDetailsPushHandlerTest {
|
||||
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase = mockk()
|
||||
private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase = mockk()
|
||||
private val singleAccountListSupplier: SingleAccountListSupplier = mockk()
|
||||
private val singleAccountListFetcher: SingleAccountListFetcher = mockk()
|
||||
|
||||
private val handler = TokenDetailsPushHandler(
|
||||
appCoroutineScope = mockk<AppCoroutineScope>(),
|
||||
getUserWalletUseCase = getUserWalletUseCase,
|
||||
getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase,
|
||||
singleAccountListSupplier = singleAccountListSupplier,
|
||||
singleAccountListFetcher = singleAccountListFetcher,
|
||||
)
|
||||
|
||||
private val userWalletId = UserWalletId("011")
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
mockkObject(TangemLogger)
|
||||
coEvery { singleAccountListFetcher.invoke(any()) } returns Either.Right(Unit)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN token absent in portfolio WHEN handle push THEN refresh accounts`() = runTest {
|
||||
every { getUserWalletUseCase.invoke(userWalletId) } returns Either.Right(multiCurrencyWallet())
|
||||
coEvery { singleAccountListSupplier.getSyncOrNull(userWalletId) } returns accountList(currencies = emptyList())
|
||||
|
||||
handler.refreshPortfolioIfTokenMissing(defaultData())
|
||||
|
||||
coVerify { singleAccountListFetcher.invoke(SingleAccountListFetcher.Params(userWalletId)) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN token present in portfolio WHEN handle push THEN do not refresh`() = runTest {
|
||||
every { getUserWalletUseCase.invoke(userWalletId) } returns Either.Right(multiCurrencyWallet())
|
||||
coEvery {
|
||||
singleAccountListSupplier.getSyncOrNull(userWalletId)
|
||||
} returns accountList(currencies = listOf(mockCryptoCurrency()))
|
||||
|
||||
handler.refreshPortfolioIfTokenMissing(defaultData())
|
||||
|
||||
coVerify(exactly = 0) { singleAccountListFetcher.invoke(any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN no wallet id in payload WHEN handle push THEN refresh selected wallet`() = runTest {
|
||||
every { getSelectedWalletSyncUseCase.invoke() } returns Either.Right(multiCurrencyWallet())
|
||||
coEvery { singleAccountListSupplier.getSyncOrNull(userWalletId) } returns accountList(currencies = emptyList())
|
||||
|
||||
handler.refreshPortfolioIfTokenMissing(defaultData() - WALLET_ID_KEY)
|
||||
|
||||
coVerify { singleAccountListFetcher.invoke(SingleAccountListFetcher.Params(userWalletId)) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN no wallet id and no selected wallet WHEN handle push THEN do not refresh`() = runTest {
|
||||
every { getSelectedWalletSyncUseCase.invoke() } returns Either.Left(GetUserWalletError.UserWalletNotFound)
|
||||
|
||||
handler.refreshPortfolioIfTokenMissing(defaultData() - WALLET_ID_KEY)
|
||||
|
||||
coVerify(exactly = 0) { singleAccountListFetcher.invoke(any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN locked wallet WHEN handle push THEN do not refresh`() = runTest {
|
||||
every { getUserWalletUseCase.invoke(userWalletId) } returns Either.Right(
|
||||
value = mockk { every { isLocked } returns true },
|
||||
)
|
||||
|
||||
handler.refreshPortfolioIfTokenMissing(defaultData())
|
||||
|
||||
coVerify(exactly = 0) { singleAccountListFetcher.invoke(any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN single currency wallet WHEN handle push THEN do not refresh`() = runTest {
|
||||
every { getUserWalletUseCase.invoke(userWalletId) } returns Either.Right(
|
||||
value = mockk {
|
||||
every { isLocked } returns false
|
||||
every { isMultiCurrency } returns false
|
||||
},
|
||||
)
|
||||
|
||||
handler.refreshPortfolioIfTokenMissing(defaultData())
|
||||
|
||||
coVerify(exactly = 0) { singleAccountListFetcher.invoke(any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN wallet not found WHEN handle push THEN do not refresh`() = runTest {
|
||||
every { getUserWalletUseCase.invoke(userWalletId) } returns Either.Left(
|
||||
value = GetUserWalletError.UserWalletNotFound,
|
||||
)
|
||||
|
||||
handler.refreshPortfolioIfTokenMissing(defaultData())
|
||||
|
||||
coVerify(exactly = 0) { singleAccountListFetcher.invoke(any()) }
|
||||
}
|
||||
|
||||
private fun defaultData() = mapOf(
|
||||
WALLET_ID_KEY to "011",
|
||||
NETWORK_ID_KEY to "123",
|
||||
TOKEN_ID_KEY to "321",
|
||||
DERIVATION_PATH_KEY to "777",
|
||||
)
|
||||
|
||||
private fun multiCurrencyWallet(): UserWallet = mockk {
|
||||
every { isLocked } returns false
|
||||
every { isMultiCurrency } returns true
|
||||
every { walletId } returns userWalletId
|
||||
}
|
||||
|
||||
private fun accountList(currencies: List<CryptoCurrency>): AccountList = AccountList.empty(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = currencies,
|
||||
)
|
||||
|
||||
private fun mockCryptoCurrency() = mockk<CryptoCurrency> {
|
||||
every { network } returns mockk {
|
||||
every { rawId } returns "123"
|
||||
every { derivationPath } returns Network.DerivationPath.Card(value = "777")
|
||||
}
|
||||
every { id } returns CryptoCurrency.ID(
|
||||
prefix = CryptoCurrency.ID.Prefix.TOKEN_PREFIX,
|
||||
body = CryptoCurrency.ID.Body.NetworkIdWithDerivationPath(rawId = "321", derivationPath = "777"),
|
||||
suffix = CryptoCurrency.ID.Suffix.RawID("321"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ import com.tangem.features.onramp.deeplink.SwapDeepLinkHandler
|
|||
import com.tangem.features.send.v2.api.deeplink.SellRedirectDeepLinkHandler
|
||||
import com.tangem.features.staking.api.deeplink.StakingDeepLinkHandler
|
||||
import com.tangem.features.tangempay.deeplink.OnboardVisaDeepLinkHandler
|
||||
import com.tangem.features.tangempay.deeplink.TangemPayMainDeepLinkHandler
|
||||
import com.tangem.features.tokendetails.deeplink.TokenDetailsDeepLinkHandler
|
||||
import com.tangem.features.wallet.deeplink.PromoDeeplinkHandler
|
||||
import com.tangem.features.wallet.deeplink.WalletDeepLinkHandler
|
||||
|
|
@ -82,6 +83,10 @@ class DeepLinkFactoryTest {
|
|||
every { create(any()) } returns mockk()
|
||||
}
|
||||
|
||||
private val tangemPayMainDeepLink = mockk<TangemPayMainDeepLinkHandler.Factory>(relaxed = true) {
|
||||
every { create(any(), any()) } returns mockk()
|
||||
}
|
||||
|
||||
private val cardSdkProvider = mockk<CardSdkProvider>(relaxed = true) {
|
||||
every { sdk.uiVisibility() } returns MutableStateFlow(false)
|
||||
}
|
||||
|
|
@ -130,6 +135,7 @@ class DeepLinkFactoryTest {
|
|||
swapDeepLink = swapDeepLinkFactory,
|
||||
promoDeepLink = promoDeepLinkFactory,
|
||||
onboardVisaDeepLink = onboardVisaDeepLink,
|
||||
tangemPayMainDeepLink = tangemPayMainDeepLink,
|
||||
newsDetailsDeepLink = newsDeeplink,
|
||||
newsDeepLink = newsDeepLinkFactory,
|
||||
earnDeepLink = earnDeepLinkFactory,
|
||||
|
|
@ -358,6 +364,14 @@ class DeepLinkFactoryTest {
|
|||
deepLinkFactory.handleDeeplink(mockedUri, testScope, isFromOnNewIntent)
|
||||
advanceUntilIdle()
|
||||
verify { promoDeepLinkFactory.create(eq(testScope), eq(emptyMap())) }
|
||||
|
||||
// Test TangemPay
|
||||
every { mockedUri.host } returns "pay-app-main"
|
||||
every { mockedUri.queryParameterNames } returns setOf("param")
|
||||
every { mockedUri.getQueryParameter("param") } returns "value"
|
||||
deepLinkFactory.handleDeeplink(mockedUri, testScope, isFromOnNewIntent)
|
||||
advanceUntilIdle()
|
||||
verify { tangemPayMainDeepLink.create(eq(testScope), any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -381,6 +395,7 @@ class DeepLinkFactoryTest {
|
|||
sellDeepLinkFactory.create()
|
||||
swapDeepLinkFactory.create()
|
||||
promoDeepLinkFactory.create(any(), any())
|
||||
tangemPayMainDeepLink.create(any(), any())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue