Updated on 2026-08-14

This commit is contained in:
Tangem 2026-04-28 14:11:23 +03:00
commit 0470e1f010
1144 changed files with 48463 additions and 11166 deletions

View file

@ -18,11 +18,11 @@ data class TokenReceiveConfig(
@Serializable
data class ReceiveAddressModel(
val nameService: NameService,
val displayType: DisplayType,
val value: String,
) {
enum class NameService {
Default, Legacy, Ens
enum class DisplayType {
Default, Legacy, Ens, Dynamic,
}
}

View file

@ -37,6 +37,8 @@ data class AccountId private constructor(
companion object {
const val PaymentAccountIdPrefix = "payment_"
private val sha256Digest: MessageDigest by lazy { MessageDigest.getInstance("SHA-256") }
private val hexRegex = Regex("^[a-fA-F0-9]{64}$")
@ -73,7 +75,7 @@ data class AccountId private constructor(
}
fun forPaymentAccount(userWalletId: UserWalletId): AccountId {
return AccountId(value = "payment_$userWalletId", userWalletId = userWalletId)
return AccountId(value = "$PaymentAccountIdPrefix$userWalletId", userWalletId = userWalletId)
}
}
}

View file

@ -0,0 +1,36 @@
package com.tangem.domain.models.account
import arrow.core.Either
import arrow.core.raise.either
import arrow.core.raise.ensure
import kotlinx.serialization.Serializable
@Serializable
@ConsistentCopyVisibility
data class CardDisplayName private constructor(val value: String) {
@Serializable
sealed interface Error {
@Serializable
data object Empty : Error
@Serializable
data object ExceedsMaxLength : Error
@Serializable
data object InvalidCharacters : Error
}
companion object {
const val MAX_LENGTH = 20
private val allowedPattern = Regex("^[\\p{L}\\p{N} ]+$")
operator fun invoke(name: String): Either<Error, CardDisplayName> = either {
val trimmed = name.trim()
ensure(trimmed.isNotEmpty()) { Error.Empty }
ensure(trimmed.length <= MAX_LENGTH) { Error.ExceedsMaxLength }
ensure(allowedPattern.matches(trimmed)) { Error.InvalidCharacters }
CardDisplayName(trimmed)
}
}
}

View file

@ -2,9 +2,15 @@ package com.tangem.domain.models.account
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.TotalFiatBalance
import com.tangem.domain.models.account.PaymentAccountStatusValue.Loaded
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.kyc.KycStatus
import com.tangem.domain.models.network.NetworkAddress
import com.tangem.domain.models.pay.TangemPayCard
import com.tangem.domain.models.serialization.SerializedBigDecimal
import kotlinx.serialization.Serializable
import java.math.BigDecimal
/**
* Represents the various states a payment account can have, encapsulating different information based on the state.
@ -25,7 +31,6 @@ sealed class PaymentAccountStatusValue {
is UnderReview,
-> TotalFiatBalance.Loaded(amount = SerializedBigDecimal.ZERO, source = source)
is Loading -> TotalFiatBalance.Loading
is Locked -> TotalFiatBalance.Loaded(amount = fiatBalance.availableBalance, source = source)
is Loaded -> TotalFiatBalance.Loaded(amount = fiatBalance.availableBalance, source = source)
}
@ -38,7 +43,6 @@ sealed class PaymentAccountStatusValue {
return when (this) {
is IssuingCard -> copy(source = source)
is Loaded -> copy(source = source)
is Locked -> copy(source = source)
is UnderReview -> copy(source = source)
is Loading,
is Empty,
@ -88,57 +92,49 @@ sealed class PaymentAccountStatusValue {
@Serializable
data class IssuingCard(override val source: StatusSource) : PaymentAccountStatusValue()
/**
* Represents a state where the payment account is locked.
*
* @property source The source of the status information.
* @property customerId The unique identifier of the customer.
* @property cardId The unique identifier of the card.
* @property lastFourDigits The last four digits of the card number.
* @property currencyCode The code of the currency.
* @property depositAddress The address for deposits, if available.
* @property isPinSet Indicates if the PIN is set for the card.
* @property fiatBalance The fiat balance details.
* @property cryptoBalance The crypto balance details.
*/
@Serializable
data class Locked(
override val source: StatusSource,
val customerId: String,
val cardId: String,
val lastFourDigits: String,
val currencyCode: String,
val depositAddress: String?,
val isPinSet: Boolean,
val fiatBalance: FiatBalance,
val cryptoBalance: CryptoBalance,
) : PaymentAccountStatusValue()
/**
* Represents a state where the payment account is successfully loaded with complete information.
*
* @property source The source of the status information.
* @property customerId The unique identifier of the customer.
* @property cardId The unique identifier of the card.
* @property lastFourDigits The last four digits of the card number.
* @property currencyCode The code of the currency.
* @property depositAddress The address for deposits, if available.
* @property isPinSet Indicates if the PIN is set for the card.
* @property fiatBalance The fiat balance details.
* @property cryptoBalance The crypto balance details.
* @property cards The list of user's cards.
*/
@Serializable
data class Loaded(
override val source: StatusSource,
val customerId: String,
val cardId: String,
val lastFourDigits: String,
val currencyCode: String,
val depositAddress: String?,
val isPinSet: Boolean,
val fiatBalance: FiatBalance,
val cryptoBalance: CryptoBalance,
) : PaymentAccountStatusValue()
val cryptoCurrency: CryptoCurrency.Token,
val cards: List<TangemPayCard>,
) : PaymentAccountStatusValue() {
val cryptoCurrencyStatus: CryptoCurrencyStatus = CryptoCurrencyStatus(
currency = cryptoCurrency,
value = CryptoCurrencyStatus.Loaded(
amount = cryptoBalance.balance,
fiatAmount = fiatBalance.availableBalance,
fiatRate = BigDecimal.ONE,
priceChange = BigDecimal.ZERO,
networkAddress = NetworkAddress.Single(
defaultAddress = NetworkAddress.Address(
type = NetworkAddress.Address.Type.Primary,
value = cryptoBalance.depositAddress,
),
),
sources = CryptoCurrencyStatus.Sources(),
pendingTransactions = emptySet(),
stakingBalance = null,
yieldSupplyStatus = null,
hasCurrentNetworkTransactions = false,
),
)
}
/** Represents an error state for the payment account status. */
@Serializable
@ -198,4 +194,10 @@ sealed class PaymentAccountStatusValue {
val tokenContractAddress: String,
val balance: SerializedBigDecimal,
)
}
}
fun Loaded.hasCardWithId(cardId: String): Boolean = cards.any { it.id == cardId }
fun Loaded.findCardWithId(cardId: String): TangemPayCard? = cards.firstOrNull { it.id == cardId }
fun Loaded.requireCardWithId(cardId: String): TangemPayCard = requireNotNull(findCardWithId(cardId))

View file

@ -3,7 +3,7 @@ package com.tangem.domain.models.currency
import java.math.BigDecimal
fun CryptoCurrency.Token.yieldSupplyKey(): String {
return "${network.backendId}_$contractAddress"
return "${network.rawId}_$contractAddress"
}
fun CryptoCurrencyStatus.hasNotSuppliedAmount(): Boolean {

View file

@ -10,7 +10,6 @@ import kotlinx.serialization.Serializable
* (e.g., ERC20, BEP20).
*
* @property id the unique identifier of the network
* @property backendId the name of this network in the Tangem backend
* @property name the human-readable name of the network, such as "Ethereum" or "Bitcoin"
* @property currencySymbol the symbol of the currency associated with the network
* @property derivationPath the path used to derive keys for this network
@ -25,7 +24,6 @@ import kotlinx.serialization.Serializable
@Serializable
data class Network(
val id: ID,
val backendId: String,
val name: String,
val currencySymbol: String,
val derivationPath: DerivationPath,
@ -49,7 +47,7 @@ data class Network(
/**
* Represents a unique identifier for a blockchain network
*
* @property rawId raw network ID
* @property rawId raw network ID (backend id)
* @property derivationPath derivation path
*/
@Serializable

View file

@ -0,0 +1,25 @@
package com.tangem.domain.models.pay
import com.tangem.domain.models.account.CardDisplayName
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
/**
* Represents a Tangem Pay card linked to a payment account.
*
* @property id unique card identifier assigned by the backend.
* @property hasPinCode whether the card has a PIN code set.
* @property displayName optional human-readable name assigned to the card; `null` if not set.
* @property limit spending limit configuration for the card; `null` if not configured or not yet loaded.
* @property isFrozen whether the card is currently frozen (blocked for payments).
* @property lastDigits The last four digits of the card number.
*/
@Serializable
data class TangemPayCard(
@SerialName("id") val id: String,
@SerialName("has_pin_code") val hasPinCode: Boolean,
@SerialName("display_name") val displayName: CardDisplayName?,
@SerialName("limit") val limit: TangemPayCardLimitData?,
@SerialName("is_frozen") val isFrozen: Boolean,
@SerialName("last_digits") val lastDigits: String,
)

View file

@ -0,0 +1,49 @@
package com.tangem.domain.models.pay
import com.tangem.domain.models.serialization.SerializedBigDecimal
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import java.util.Locale
@Serializable
data class TangemPayCardLimit(
@SerialName("amount") val amount: SerializedBigDecimal,
@SerialName("period") val period: TangemPayCardLimitPeriod,
)
@Serializable
enum class TangemPayCardLimitPeriod {
@SerialName("DAY")
DAY,
@SerialName("WEEK")
WEEK,
@SerialName("MONTH")
MONTH,
@SerialName("YEAR")
YEAR,
@SerialName("ALL_TIME")
ALL_TIME,
@SerialName("AUTHORIZATION")
AUTHORIZATION,
@SerialName("UNKNOWN")
UNKNOWN,
;
companion object {
fun fromString(value: String) = when (value.uppercase(Locale.US)) {
"DAY" -> DAY
"WEEK" -> WEEK
"MONTH" -> MONTH
"YEAR" -> YEAR
"ALL_TIME" -> ALL_TIME
"AUTHORIZATION" -> AUTHORIZATION
else -> UNKNOWN
}
}
}

View file

@ -0,0 +1,10 @@
package com.tangem.domain.models.pay
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class TangemPayCardLimitData(
@SerialName("actual_card_limit") val actualCardLimit: TangemPayCardLimit?,
@SerialName("admin_card_limit") val adminCardLimit: TangemPayCardLimit?,
)

View file

@ -1,4 +1,4 @@
package com.tangem.domain.models
package com.tangem.domain.models.pay
enum class TangemPayEligibilityType {

View file

@ -0,0 +1,8 @@
package com.tangem.domain.models.pay
import java.math.BigDecimal
data class TangemPayReissueCardFee(
val amount: BigDecimal,
val currencyCode: String,
)

View file

@ -0,0 +1,16 @@
package com.tangem.domain.models.portfolio
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.account.AccountName
import com.tangem.domain.models.account.CryptoPortfolioIcon
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWalletId
data class UserAssetEntry(
val userWalletId: UserWalletId,
val userWalletName: String,
val accountId: AccountId,
val accountName: AccountName,
val accountIcon: CryptoPortfolioIcon,
val currencyStatus: CryptoCurrencyStatus,
)