Updated on 2026-08-14

This commit is contained in:
Tangem 2024-05-21 12:02:55 +02:00
parent 78996521c6
commit ab5fac9196
41 changed files with 366 additions and 73 deletions

View file

@ -9,17 +9,16 @@ android {
}
dependencies {
implementation(project(":core:datasource"))
implementation(project(":core:utils"))
implementation(project(":common"))
implementation(project(":libs:auth"))
implementation(projects.core.datasource)
implementation(projects.core.utils)
implementation(projects.common)
implementation(projects.libs.auth)
implementation(projects.libs.blockchainSdk)
implementation(projects.domain.demo)
implementation(projects.domain.models)
implementation(projects.domain.tokens.models)
implementation(projects.domain.txhistory.models)
implementation(projects.domain.wallets.models)
/** Tangem libraries */
implementation(deps.tangem.blockchain) {
exclude(module = "joda-time")

View file

@ -1,14 +0,0 @@
package com.tangem.domain.userwallets
data class Artwork(val artworkId: String) {
companion object {
const val DEFAULT_IMG_URL = "https://app.tangem.com/cards/card_default.png"
const val SERGIO_CARD_URL = "https://app.tangem.com/cards/card_tg059.png"
const val MARTA_CARD_URL = "https://app.tangem.com/cards/card_tg083.png"
const val SERGIO_CARD_ID = "BC01"
const val MARTA_CARD_ID = "BC02"
const val TWIN_CARD_1 = "https://app.tangem.com/cards/card_tg085.png"
const val TWIN_CARD_2 = "https://app.tangem.com/cards/card_tg086.png"
}
}

View file

@ -1,56 +0,0 @@
package com.tangem.domain.userwallets
import com.tangem.common.extensions.toHexString
import com.tangem.common.services.Result
import com.tangem.domain.common.TwinCardNumber
import com.tangem.domain.common.TwinsHelper
import com.tangem.operations.attestation.OnlineCardVerifier
import com.tangem.operations.attestation.TangemApi
/**
* Use case for getting card image url
*
* @property verifier REST API
*
[REDACTED_AUTHOR]
*/
class GetCardImageUseCase(private val verifier: OnlineCardVerifier = OnlineCardVerifier()) {
/**
* Get card image url
*
* @param cardId card id
* @param cardPublicKey card public key
*/
suspend operator fun invoke(cardId: String, cardPublicKey: ByteArray): String {
return when (val result = verifier.getCardInfo(cardId, cardPublicKey)) {
is Result.Success -> {
val artworkId = result.data.artwork?.id
if (artworkId.isNullOrEmpty()) {
getFallbackArtworkUrl(cardId)
} else {
getUrlForArtwork(cardId, cardPublicKey.toHexString(), artworkId)
}
}
is Result.Failure -> getFallbackArtworkUrl(cardId)
}
}
private fun getFallbackArtworkUrl(cardId: String): String {
return when {
cardId.startsWith(Artwork.SERGIO_CARD_ID) -> Artwork.SERGIO_CARD_URL
cardId.startsWith(Artwork.MARTA_CARD_ID) -> Artwork.MARTA_CARD_URL
else -> when (TwinsHelper.getTwinCardNumber(cardId)) {
TwinCardNumber.First -> Artwork.TWIN_CARD_1
TwinCardNumber.Second -> Artwork.TWIN_CARD_2
else -> Artwork.DEFAULT_IMG_URL
}
}
}
private fun getUrlForArtwork(cardId: String, cardPublicKeyHex: String, artworkId: String): String {
return TangemApi.Companion.BaseUrl.VERIFY.url + TangemApi.ARTWORK +
"?artworkId=$artworkId&CID=$cardId&publicKey=$cardPublicKeyHex"
}
}

View file

@ -1,69 +0,0 @@
package com.tangem.domain.userwallets
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ProductType
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.wallets.models.UserWallet
class UserWalletBuilder(
private val scanResponse: ScanResponse,
private val getCardImageUseCase: GetCardImageUseCase = GetCardImageUseCase(),
) {
private var backupCardsIds: Set<String> = emptySet()
private var hasBackupError: Boolean = false
private val CardDTO.isBackupNotAllowed: Boolean
get() = !this.settings.isBackupAllowed
private val ScanResponse.userWalletName: String
get() = when (productType) {
ProductType.Note -> "Note"
ProductType.Twins -> "Twin"
ProductType.Start2Coin -> "Start2Coin"
ProductType.Visa -> "Tangem Visa"
ProductType.Wallet,
ProductType.Wallet2,
ProductType.Ring,
-> when {
card.isBackupNotAllowed -> "Tangem card"
cardTypesResolver.isStart2Coin() -> "Start2Coin"
else -> "Wallet"
}
}
/**
* DANGEROUS!!!
* [backupCardsIds] will be non-empty list if card is backed up on current device.
*/
fun backupCardsIds(backupCardsIds: Set<String>?) = this.apply {
if (backupCardsIds != null) {
this.backupCardsIds = backupCardsIds
}
}
/**
* Sets if UserWallet has any backup errors (wrong curves etc). Use in onboarding
*/
fun hasBackupError(hasBackupError: Boolean) = this.apply {
this.hasBackupError = hasBackupError
}
suspend fun build(): UserWallet? {
return with(scanResponse) {
UserWalletIdBuilder.scanResponse(scanResponse)
.build()
?.let {
UserWallet(
walletId = it,
name = userWalletName,
artworkUrl = getCardImageUseCase.invoke(card.cardId, card.cardPublicKey),
cardsInWallet = backupCardsIds.plus(card.cardId),
scanResponse = this,
isMultiCurrency = cardTypesResolver.isMultiwalletAllowed(),
hasBackupError = hasBackupError,
)
}
}
}
}

View file

@ -1,83 +0,0 @@
package com.tangem.domain.userwallets
import com.tangem.common.extensions.calculateSha256
import com.tangem.common.extensions.hexToBytes
import com.tangem.crypto.Secp256k1
import com.tangem.domain.common.TapWorkarounds.isTangemTwins
import com.tangem.domain.common.extensions.calculateHmacSha256
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ProductType
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.wallets.models.UserWalletId
class UserWalletIdBuilder private constructor(
private val publicKey: ByteArray?,
private val pairTwinPublicKey: ByteArray? = null,
) {
fun build(): UserWalletId? {
val seed = if (publicKey != null) {
if (pairTwinPublicKey != null) {
Secp256k1.sum(publicKey, pairTwinPublicKey)
} else {
publicKey
}
} else {
null
}
return seed?.let {
UserWalletId(value = calculateUserWalletId(it))
}
}
private fun calculateUserWalletId(seed: ByteArray?): ByteArray? {
val message = MESSAGE_FOR_WALLET_ID.toByteArray()
val keyHash = seed?.calculateSha256()
return if (keyHash != null) {
message.calculateHmacSha256(keyHash)
} else {
null
}
}
companion object {
private const val MESSAGE_FOR_WALLET_ID = "UserWalletID"
@Throws(IllegalArgumentException::class)
fun card(card: CardDTO): UserWalletIdBuilder {
require(!card.isTangemTwins) {
"For twin cards use scanResponse to ID calculation"
}
return UserWalletIdBuilder(findPublicKey(card.wallets))
}
fun scanResponse(scanResponse: ScanResponse): UserWalletIdBuilder {
return UserWalletIdBuilder(
publicKey = findPublicKey(scanResponse.card.wallets),
pairTwinPublicKey = when (scanResponse.productType) {
ProductType.Twins -> scanResponse.secondTwinPublicKey?.hexToBytes()
ProductType.Note,
ProductType.Wallet,
ProductType.Wallet2,
ProductType.Ring,
ProductType.Start2Coin,
ProductType.Visa,
-> null
},
)
}
fun walletPublicKey(publicKey: ByteArray): UserWalletId {
return UserWalletIdBuilder(
publicKey = publicKey,
).build()!!
}
private fun findPublicKey(wallets: List<CardDTO.Wallet>): ByteArray? {
return wallets.firstOrNull()
?.publicKey
}
}
}