Updated on 2026-08-14
This commit is contained in:
parent
85803abc7f
commit
095c755362
194 changed files with 830 additions and 473 deletions
|
|
@ -1,40 +0,0 @@
|
|||
package com.tangem.domain.common
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.common.extensions.calculateHashCode
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
@Deprecated("Use Network model")
|
||||
data class BlockchainNetwork(
|
||||
@Json(name = "blockchain")
|
||||
val blockchain: Blockchain,
|
||||
@Json(name = "derivationPath")
|
||||
val derivationPath: String?,
|
||||
@Json(name = "tokens")
|
||||
val tokens: List<Token>,
|
||||
) {
|
||||
|
||||
constructor(blockchain: Blockchain, derivationStyleProvider: DerivationStyleProvider) : this(
|
||||
blockchain = blockchain,
|
||||
derivationPath = blockchain.derivationPath(derivationStyleProvider.getDerivationStyle())?.rawPath,
|
||||
tokens = emptyList(),
|
||||
)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as BlockchainNetwork
|
||||
|
||||
if (blockchain != other.blockchain) return false
|
||||
return derivationPath == other.derivationPath
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = calculateHashCode(
|
||||
blockchain.hashCode(),
|
||||
derivationPath?.hashCode() ?: 0,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
package com.tangem.domain.common
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.Token
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
interface CardTypesResolver {
|
||||
|
||||
fun isTangemNote(): Boolean
|
||||
|
||||
fun isTangemWallet(): Boolean
|
||||
|
||||
fun isShibaWallet(): Boolean
|
||||
|
||||
fun isWhiteWallet(): Boolean
|
||||
|
||||
fun isWallet2(): Boolean
|
||||
|
||||
fun isVisaWallet(): Boolean
|
||||
|
||||
fun isRing(): Boolean
|
||||
|
||||
fun isTangemTwins(): Boolean
|
||||
|
||||
fun isStart2Coin(): Boolean
|
||||
|
||||
fun isDevKit(): Boolean
|
||||
|
||||
fun isSingleWallet(): Boolean
|
||||
|
||||
fun isSingleWalletWithToken(): Boolean
|
||||
|
||||
fun isMultiwalletAllowed(): Boolean
|
||||
|
||||
fun getBlockchain(): Blockchain
|
||||
|
||||
fun getPrimaryToken(): Token?
|
||||
|
||||
fun isReleaseFirmwareType(): Boolean
|
||||
|
||||
fun getRemainingSignatures(): Int?
|
||||
|
||||
fun getCardId(): String
|
||||
|
||||
fun isTestCard(): Boolean
|
||||
|
||||
fun isAttestationFailed(): Boolean
|
||||
|
||||
fun hasWalletSignedHashes(): Boolean
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
package com.tangem.domain.common
|
||||
|
||||
import com.tangem.blockchain.common.derivation.DerivationStyle
|
||||
import com.tangem.domain.common.TapWorkarounds.isWallet2
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
|
||||
interface DerivationStyleProvider {
|
||||
fun getDerivationStyle(): DerivationStyle?
|
||||
}
|
||||
|
||||
internal class TangemDerivationStyleProvider(
|
||||
private val card: CardDTO,
|
||||
) : DerivationStyleProvider {
|
||||
override fun getDerivationStyle(): DerivationStyle? {
|
||||
return when {
|
||||
!card.settings.isHDWalletAllowed -> null
|
||||
firstBatchesOfWallet1(card) -> DerivationStyle.V1
|
||||
card.isWallet2 -> DerivationStyle.V3
|
||||
else -> DerivationStyle.V2
|
||||
}
|
||||
}
|
||||
|
||||
private fun firstBatchesOfWallet1(card: CardDTO): Boolean {
|
||||
return card.batchId == "AC01" || card.batchId == "AC02" || card.batchId == "CB95"
|
||||
}
|
||||
}
|
||||
|
||||
internal class TangemHotDerivationStyleProvider : DerivationStyleProvider {
|
||||
override fun getDerivationStyle(): DerivationStyle? = DerivationStyle.V3
|
||||
}
|
||||
|
|
@ -1,138 +0,0 @@
|
|||
package com.tangem.domain.common
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.card.FirmwareVersion
|
||||
import com.tangem.common.card.WalletData
|
||||
import com.tangem.domain.common.TapWorkarounds.getTangemNoteBlockchain
|
||||
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
|
||||
import com.tangem.domain.common.TapWorkarounds.isTestCard
|
||||
import com.tangem.domain.common.TapWorkarounds.isWallet2
|
||||
import com.tangem.domain.common.visa.VisaUtilities
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ProductType
|
||||
import com.tangem.operations.attestation.Attestation
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
internal class TangemCardTypesResolver(
|
||||
private val card: CardDTO,
|
||||
private val productType: ProductType,
|
||||
private val walletData: WalletData?,
|
||||
) : CardTypesResolver {
|
||||
|
||||
override fun isTangemNote(): Boolean = productType == ProductType.Note
|
||||
|
||||
override fun isTangemWallet(): Boolean {
|
||||
return card.settings.isBackupAllowed && card.settings.isHDWalletAllowed &&
|
||||
card.firmwareVersion >= FirmwareVersion.MultiWalletAvailable
|
||||
}
|
||||
|
||||
override fun isShibaWallet(): Boolean {
|
||||
return card.firmwareVersion.compareTo(FirmwareVersion.KeysImportAvailable) == 0
|
||||
}
|
||||
|
||||
override fun isWhiteWallet(): Boolean {
|
||||
return walletData == null && card.firmwareVersion <= FirmwareVersion.HDWalletAvailable
|
||||
}
|
||||
|
||||
override fun isWallet2(): Boolean = card.isWallet2
|
||||
|
||||
override fun isVisaWallet(): Boolean = productType == ProductType.Visa
|
||||
|
||||
override fun isRing(): Boolean {
|
||||
return productType == ProductType.Ring
|
||||
}
|
||||
|
||||
override fun isTangemTwins(): Boolean = productType == ProductType.Twins
|
||||
|
||||
override fun isStart2Coin(): Boolean = card.isStart2Coin
|
||||
|
||||
override fun isDevKit(): Boolean = card.batchId == DEV_KIT_CARD_BATCH_ID
|
||||
|
||||
override fun isSingleWallet(): Boolean = !isMultiwalletAllowed() && !isSingleWalletWithToken() && !isVisaWallet()
|
||||
|
||||
override fun isSingleWalletWithToken(): Boolean = walletData?.token != null && !isMultiwalletAllowed()
|
||||
|
||||
override fun isMultiwalletAllowed(): Boolean {
|
||||
return !isTangemTwins() &&
|
||||
!card.isStart2Coin &&
|
||||
!isTangemNote() &&
|
||||
!isVisaWallet() &&
|
||||
(multiWalletAvailable() || card.wallets.firstOrNull()?.curve == EllipticCurve.Secp256k1)
|
||||
}
|
||||
|
||||
private fun multiWalletAvailable() = card.firmwareVersion >= FirmwareVersion.MultiWalletAvailable
|
||||
|
||||
override fun getBlockchain(): Blockchain {
|
||||
return when (productType) {
|
||||
ProductType.Start2Coin -> if (card.isTestCard) Blockchain.BitcoinTestnet else Blockchain.Bitcoin
|
||||
ProductType.Visa -> VisaUtilities.visaBlockchain
|
||||
else -> {
|
||||
val blockchainName: String = walletData?.blockchain
|
||||
?: if (productType == ProductType.Note) {
|
||||
return card.getTangemNoteBlockchain() ?: Blockchain.Unknown
|
||||
} else {
|
||||
return Blockchain.Unknown
|
||||
}
|
||||
Blockchain.fromBlockchainName(blockchainName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getPrimaryToken(): Token? {
|
||||
val cardToken = walletData?.token ?: return null
|
||||
return Token(
|
||||
cardToken.name,
|
||||
cardToken.symbol,
|
||||
cardToken.contractAddress,
|
||||
cardToken.decimals,
|
||||
)
|
||||
}
|
||||
|
||||
override fun isReleaseFirmwareType(): Boolean = card.firmwareVersion.type == FirmwareVersion.FirmwareType.Release
|
||||
|
||||
override fun getRemainingSignatures(): Int? = card.wallets.firstOrNull()?.remainingSignatures
|
||||
|
||||
override fun getCardId(): String {
|
||||
return if (isTangemTwins()) {
|
||||
card.getTwinCardIdForUser()
|
||||
} else {
|
||||
card.cardId
|
||||
}
|
||||
}
|
||||
|
||||
override fun isTestCard(): Boolean = card.isTestCard
|
||||
|
||||
override fun isAttestationFailed(): Boolean = card.attestation.status == Attestation.Status.Failed
|
||||
|
||||
override fun hasWalletSignedHashes(): Boolean {
|
||||
return card.wallets.any {
|
||||
val totalSignedHashes = it.totalSignedHashes ?: 0
|
||||
totalSignedHashes > 0
|
||||
}
|
||||
}
|
||||
|
||||
private fun Blockchain.Companion.fromBlockchainName(blockchainName: String): Blockchain {
|
||||
// workaround for BSC (BNB) notes cards
|
||||
return when (blockchainName) {
|
||||
"BINANCE" -> {
|
||||
Blockchain.BSC
|
||||
}
|
||||
"BINANCE/test" -> {
|
||||
Blockchain.BSCTestnet
|
||||
}
|
||||
"CARDANO" -> {
|
||||
Blockchain.Cardano
|
||||
}
|
||||
else -> {
|
||||
Blockchain.fromId(blockchainName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
||||
const val DEV_KIT_CARD_BATCH_ID = "CB83"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
package com.tangem.domain.common
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.card.FirmwareVersion
|
||||
import com.tangem.domain.common.visa.VisaUtilities
|
||||
import com.tangem.domain.demo.DemoConfig
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
object TapWorkarounds {
|
||||
private const val START_2_COIN_ISSUER = "start2coin"
|
||||
private const val TEST_CARD_BATCH = "99FF"
|
||||
private const val TEST_CARD_ID_STARTS_WITH = "FF99"
|
||||
private val backupRequiredFirmwareVersion = FirmwareVersion(major = 6, minor = 21)
|
||||
private val demoConfig = DemoConfig()
|
||||
|
||||
val CardDTO.isTangemTwins: Boolean
|
||||
get() = TwinsHelper.getTwinCardNumber(cardId) != null
|
||||
|
||||
val CardDTO.isStart2Coin: Boolean
|
||||
get() = isStart2CoinIssuer(issuer.name)
|
||||
|
||||
val CardDTO.isWallet2: Boolean
|
||||
get() = firmwareVersion >= FirmwareVersion.Ed25519Slip0010Available && settings.isKeysImportAllowed
|
||||
|
||||
val CardDTO.isVisa: Boolean
|
||||
get() = VisaUtilities.isVisaCard(this)
|
||||
|
||||
val CardDTO.isTestCard: Boolean
|
||||
get() = batchId == TEST_CARD_BATCH && cardId.startsWith(TEST_CARD_ID_STARTS_WITH)
|
||||
|
||||
// for cards 6.21 and higher backup is not skippable
|
||||
val CardDTO.canSkipBackup: Boolean
|
||||
get() = this.firmwareVersion < backupRequiredFirmwareVersion || demoConfig.isDemoCardId(cardId)
|
||||
|
||||
val CardDTO.useOldStyleDerivation: Boolean
|
||||
get() = batchId == "AC01" || batchId == "AC02" || batchId == "CB95"
|
||||
|
||||
val CardDTO.isExcluded: Boolean
|
||||
get() {
|
||||
val excludedBatch = excludedBatches.contains(batchId)
|
||||
val excludedIssuerName = excludedIssuers.contains(issuer.name.uppercase(Locale.ROOT))
|
||||
return excludedBatch || excludedIssuerName
|
||||
}
|
||||
|
||||
val CardDTO.isNotSupportedInThatRelease: Boolean
|
||||
get() = false
|
||||
|
||||
private val tangemNoteBatches = mapOf(
|
||||
"AB01" to Blockchain.Bitcoin,
|
||||
"AB02" to Blockchain.Ethereum,
|
||||
"AB03" to Blockchain.Cardano,
|
||||
"AB04" to Blockchain.Dogecoin,
|
||||
"AB05" to Blockchain.BSC,
|
||||
"AB06" to Blockchain.XRP,
|
||||
"AB07" to Blockchain.Bitcoin,
|
||||
"AB08" to Blockchain.Ethereum,
|
||||
"AB09" to Blockchain.Bitcoin, // new batches for 3.34
|
||||
"AB10" to Blockchain.Ethereum,
|
||||
"AB11" to Blockchain.Bitcoin,
|
||||
"AB12" to Blockchain.Ethereum,
|
||||
)
|
||||
|
||||
// TODO try remove if it's possible because we can configure on init sdk com.tangem.common.core.Config
|
||||
private val excludedBatches = listOf("0027", "0030", "0031", "0035", "DA88", "AF56")
|
||||
|
||||
private val excludedIssuers = listOf("TTM BANK")
|
||||
|
||||
@Deprecated(
|
||||
"Now blockchain is read form files (CardTypesResolver.getBlockchain), " +
|
||||
"but for previously saved cards this method is still used",
|
||||
)
|
||||
fun CardDTO.getTangemNoteBlockchain(): Blockchain? = tangemNoteBatches[batchId]
|
||||
|
||||
fun isStart2CoinIssuer(cardIssuer: String?): Boolean {
|
||||
return cardIssuer?.lowercase(Locale.US) == START_2_COIN_ISSUER
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
package com.tangem.domain.common.configs
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.card.FirmwareVersion
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
|
||||
sealed interface CardConfig {
|
||||
|
||||
val mandatoryCurves: List<EllipticCurve>
|
||||
|
||||
fun primaryCurve(blockchain: Blockchain): EllipticCurve?
|
||||
|
||||
companion object {
|
||||
|
||||
fun createConfig(cardDTO: CardDTO): CardConfig {
|
||||
if (cardDTO.firmwareVersion >= FirmwareVersion.Ed25519Slip0010Available) {
|
||||
return Wallet2CardConfig
|
||||
}
|
||||
if (cardDTO.settings.isBackupAllowed && cardDTO.settings.isHDWalletAllowed &&
|
||||
cardDTO.firmwareVersion >= FirmwareVersion.MultiWalletAvailable
|
||||
) {
|
||||
return MultiWalletCardConfig
|
||||
}
|
||||
if (cardDTO.supportedCurves.size == 1 &&
|
||||
cardDTO.supportedCurves.contains(EllipticCurve.Ed25519)
|
||||
) {
|
||||
return EdSingleCurrencyCardConfig
|
||||
}
|
||||
return GenericCardConfig(cardDTO.settings.maxWalletsCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
package com.tangem.domain.common.configs
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import timber.log.Timber
|
||||
|
||||
object EdSingleCurrencyCardConfig : CardConfig {
|
||||
|
||||
override val mandatoryCurves: List<EllipticCurve> = listOf(EllipticCurve.Ed25519)
|
||||
|
||||
override fun primaryCurve(blockchain: Blockchain): EllipticCurve? {
|
||||
return when {
|
||||
blockchain.getSupportedCurves().contains(EllipticCurve.Ed25519) -> {
|
||||
EllipticCurve.Ed25519
|
||||
}
|
||||
else -> {
|
||||
Timber.e("Unsupported blockchain, curve not found")
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
package com.tangem.domain.common.configs
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import timber.log.Timber
|
||||
|
||||
class GenericCardConfig(maxWalletCount: Int) : CardConfig {
|
||||
|
||||
override val mandatoryCurves: List<EllipticCurve> = buildList {
|
||||
add(EllipticCurve.Secp256k1)
|
||||
if (maxWalletCount > 1) {
|
||||
add(EllipticCurve.Ed25519)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Old logic to determine primary curve for blockchain
|
||||
*/
|
||||
override fun primaryCurve(blockchain: Blockchain): EllipticCurve? {
|
||||
return when {
|
||||
blockchain.getSupportedCurves().contains(EllipticCurve.Secp256k1) -> {
|
||||
EllipticCurve.Secp256k1
|
||||
}
|
||||
blockchain.getSupportedCurves().contains(EllipticCurve.Ed25519) -> {
|
||||
EllipticCurve.Ed25519
|
||||
}
|
||||
else -> {
|
||||
Timber.e("Unsupported blockchain, curve not found")
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
package com.tangem.domain.common.configs
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import timber.log.Timber
|
||||
|
||||
object MultiWalletCardConfig : CardConfig {
|
||||
override val mandatoryCurves: List<EllipticCurve>
|
||||
get() = listOf(
|
||||
EllipticCurve.Secp256k1,
|
||||
EllipticCurve.Ed25519,
|
||||
EllipticCurve.Bls12381G2Aug,
|
||||
)
|
||||
|
||||
/**
|
||||
* Old logic to determine primary curve for blockchain in TangemWallet
|
||||
*/
|
||||
override fun primaryCurve(blockchain: Blockchain): EllipticCurve? {
|
||||
return when {
|
||||
blockchain.getSupportedCurves().contains(EllipticCurve.Secp256k1) -> {
|
||||
EllipticCurve.Secp256k1
|
||||
}
|
||||
blockchain.getSupportedCurves().contains(EllipticCurve.Ed25519) -> {
|
||||
EllipticCurve.Ed25519
|
||||
}
|
||||
blockchain.getSupportedCurves().contains(EllipticCurve.Bls12381G2Aug) -> {
|
||||
EllipticCurve.Bls12381G2Aug
|
||||
}
|
||||
else -> {
|
||||
Timber.e("Unsupported blockchain, curve not found")
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,212 +0,0 @@
|
|||
package com.tangem.domain.common.configs
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import timber.log.Timber
|
||||
|
||||
data object Wallet2CardConfig : CardConfig {
|
||||
override val mandatoryCurves: List<EllipticCurve>
|
||||
get() = listOf(
|
||||
EllipticCurve.Secp256k1,
|
||||
EllipticCurve.Ed25519,
|
||||
EllipticCurve.Bls12381G2Aug,
|
||||
EllipticCurve.Bip0340,
|
||||
EllipticCurve.Ed25519Slip0010,
|
||||
)
|
||||
|
||||
/**
|
||||
* Logic to determine primary curve for blockchain in TangemWallet 2.0
|
||||
* Order is important here
|
||||
*/
|
||||
override fun primaryCurve(blockchain: Blockchain): EllipticCurve? {
|
||||
// order is important, new curve is preferred for wallet 2
|
||||
// TODO Comment old logic without direct mapping until tests and release
|
||||
// return when {
|
||||
// blockchain.getSupportedCurves().contains(EllipticCurve.Ed25519Slip0010) -> {
|
||||
// EllipticCurve.Ed25519Slip0010
|
||||
// }
|
||||
// blockchain.getSupportedCurves().contains(EllipticCurve.Secp256k1) -> {
|
||||
// EllipticCurve.Secp256k1
|
||||
// }
|
||||
// blockchain.getSupportedCurves().contains(EllipticCurve.Bls12381G2Aug) -> {
|
||||
// EllipticCurve.Bls12381G2Aug
|
||||
// }
|
||||
// // only for support cardano on Wallet2
|
||||
// blockchain.getSupportedCurves().contains(EllipticCurve.Ed25519) -> {
|
||||
// EllipticCurve.Ed25519
|
||||
// }
|
||||
// else -> {
|
||||
// Timber.e("Unsupported blockchain, curve not found")
|
||||
// null
|
||||
// }
|
||||
// }
|
||||
val curve = getPrimaryCurveForBlockchain(blockchain)
|
||||
// check curve supports
|
||||
if (!blockchain.getSupportedCurves().contains(curve)) {
|
||||
Timber.e("Unsupported curve $curve for blockchain $blockchain")
|
||||
return null
|
||||
}
|
||||
return curve
|
||||
}
|
||||
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
private fun getPrimaryCurveForBlockchain(blockchain: Blockchain): EllipticCurve? {
|
||||
return when (blockchain) {
|
||||
Blockchain.Unknown -> null
|
||||
Blockchain.Arbitrum -> EllipticCurve.Secp256k1
|
||||
Blockchain.ArbitrumTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Avalanche -> EllipticCurve.Secp256k1
|
||||
Blockchain.AvalancheTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Binance -> EllipticCurve.Secp256k1
|
||||
Blockchain.BinanceTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.BSC -> EllipticCurve.Secp256k1
|
||||
Blockchain.BSCTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Bitcoin -> EllipticCurve.Secp256k1
|
||||
Blockchain.BitcoinTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.BitcoinCash -> EllipticCurve.Secp256k1
|
||||
Blockchain.BitcoinCashTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Cardano -> EllipticCurve.Ed25519
|
||||
Blockchain.Cosmos -> EllipticCurve.Secp256k1
|
||||
Blockchain.CosmosTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Dogecoin -> EllipticCurve.Secp256k1
|
||||
Blockchain.Ducatus -> EllipticCurve.Secp256k1
|
||||
Blockchain.Ethereum -> EllipticCurve.Secp256k1
|
||||
Blockchain.EthereumTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.EthereumClassic -> EllipticCurve.Secp256k1
|
||||
Blockchain.EthereumClassicTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Fantom -> EllipticCurve.Secp256k1
|
||||
Blockchain.FantomTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Litecoin -> EllipticCurve.Secp256k1
|
||||
Blockchain.Near -> EllipticCurve.Ed25519Slip0010
|
||||
Blockchain.NearTestnet -> EllipticCurve.Ed25519Slip0010
|
||||
Blockchain.Polkadot -> EllipticCurve.Ed25519Slip0010
|
||||
Blockchain.PolkadotTestnet -> EllipticCurve.Ed25519Slip0010
|
||||
Blockchain.Kava -> EllipticCurve.Secp256k1
|
||||
Blockchain.KavaTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Kusama -> EllipticCurve.Ed25519Slip0010
|
||||
Blockchain.Polygon -> EllipticCurve.Secp256k1
|
||||
Blockchain.PolygonTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.RSK -> EllipticCurve.Secp256k1
|
||||
Blockchain.Sei -> EllipticCurve.Secp256k1
|
||||
Blockchain.SeiTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Stellar -> EllipticCurve.Ed25519Slip0010
|
||||
Blockchain.StellarTestnet -> EllipticCurve.Ed25519Slip0010
|
||||
Blockchain.Solana -> EllipticCurve.Ed25519Slip0010
|
||||
Blockchain.SolanaTestnet -> EllipticCurve.Ed25519Slip0010
|
||||
Blockchain.Tezos -> EllipticCurve.Ed25519Slip0010
|
||||
Blockchain.Tron -> EllipticCurve.Secp256k1
|
||||
Blockchain.TronTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.XRP -> EllipticCurve.Secp256k1
|
||||
Blockchain.Gnosis -> EllipticCurve.Secp256k1
|
||||
Blockchain.Dash -> EllipticCurve.Secp256k1
|
||||
Blockchain.Optimism -> EllipticCurve.Secp256k1
|
||||
Blockchain.OptimismTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Dischain -> EllipticCurve.Secp256k1
|
||||
Blockchain.EthereumPow -> EllipticCurve.Secp256k1
|
||||
Blockchain.EthereumPowTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Kaspa -> EllipticCurve.Secp256k1
|
||||
Blockchain.Telos -> EllipticCurve.Secp256k1
|
||||
Blockchain.TelosTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.TON -> EllipticCurve.Ed25519Slip0010
|
||||
Blockchain.TONTestnet -> EllipticCurve.Ed25519Slip0010
|
||||
Blockchain.Ravencoin -> EllipticCurve.Secp256k1
|
||||
Blockchain.RavencoinTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.TerraV1 -> EllipticCurve.Secp256k1
|
||||
Blockchain.TerraV2 -> EllipticCurve.Secp256k1
|
||||
Blockchain.Cronos -> EllipticCurve.Secp256k1
|
||||
Blockchain.AlephZero -> EllipticCurve.Ed25519Slip0010
|
||||
Blockchain.AlephZeroTestnet -> EllipticCurve.Ed25519Slip0010
|
||||
Blockchain.OctaSpace -> EllipticCurve.Secp256k1
|
||||
Blockchain.OctaSpaceTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Chia -> EllipticCurve.Bls12381G2Aug
|
||||
Blockchain.ChiaTestnet -> EllipticCurve.Bls12381G2Aug
|
||||
Blockchain.Decimal -> EllipticCurve.Secp256k1
|
||||
Blockchain.DecimalTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.XDC -> EllipticCurve.Secp256k1
|
||||
Blockchain.XDCTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.VeChain -> EllipticCurve.Secp256k1
|
||||
Blockchain.VeChainTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Aptos -> EllipticCurve.Ed25519Slip0010
|
||||
Blockchain.AptosTestnet -> EllipticCurve.Ed25519Slip0010
|
||||
Blockchain.Playa3ull -> EllipticCurve.Secp256k1
|
||||
Blockchain.Shibarium -> EllipticCurve.Secp256k1
|
||||
Blockchain.ShibariumTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Algorand -> EllipticCurve.Ed25519Slip0010
|
||||
Blockchain.AlgorandTestnet -> EllipticCurve.Ed25519Slip0010
|
||||
Blockchain.Hedera -> EllipticCurve.Ed25519Slip0010
|
||||
Blockchain.HederaTestnet -> EllipticCurve.Ed25519Slip0010
|
||||
Blockchain.Aurora -> EllipticCurve.Secp256k1
|
||||
Blockchain.AuroraTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Areon -> EllipticCurve.Secp256k1
|
||||
Blockchain.AreonTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.PulseChain -> EllipticCurve.Secp256k1
|
||||
Blockchain.PulseChainTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.ZkSyncEra -> EllipticCurve.Secp256k1
|
||||
Blockchain.ZkSyncEraTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Nexa -> EllipticCurve.Secp256k1
|
||||
Blockchain.NexaTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Moonbeam -> EllipticCurve.Secp256k1
|
||||
Blockchain.MoonbeamTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Manta -> EllipticCurve.Secp256k1
|
||||
Blockchain.MantaTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.PolygonZkEVM -> EllipticCurve.Secp256k1
|
||||
Blockchain.PolygonZkEVMTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Radiant -> EllipticCurve.Secp256k1
|
||||
Blockchain.Base -> EllipticCurve.Secp256k1
|
||||
Blockchain.BaseTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Moonriver -> EllipticCurve.Secp256k1
|
||||
Blockchain.MoonriverTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Mantle -> EllipticCurve.Secp256k1
|
||||
Blockchain.MantleTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Fact0rn -> EllipticCurve.Secp256k1
|
||||
Blockchain.Flare -> EllipticCurve.Secp256k1
|
||||
Blockchain.FlareTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Taraxa -> EllipticCurve.Secp256k1
|
||||
Blockchain.TaraxaTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Koinos -> EllipticCurve.Secp256k1
|
||||
Blockchain.KoinosTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Joystream -> EllipticCurve.Ed25519Slip0010
|
||||
Blockchain.Bittensor -> EllipticCurve.Ed25519Slip0010
|
||||
Blockchain.Filecoin -> EllipticCurve.Secp256k1
|
||||
Blockchain.Blast -> EllipticCurve.Secp256k1
|
||||
Blockchain.BlastTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Cyber -> EllipticCurve.Secp256k1
|
||||
Blockchain.CyberTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.InternetComputer -> EllipticCurve.Secp256k1
|
||||
Blockchain.Sui -> EllipticCurve.Ed25519Slip0010
|
||||
Blockchain.SuiTestnet -> EllipticCurve.Ed25519Slip0010
|
||||
Blockchain.EnergyWebChain -> EllipticCurve.Secp256k1
|
||||
Blockchain.EnergyWebChainTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.EnergyWebX -> EllipticCurve.Ed25519Slip0010
|
||||
Blockchain.EnergyWebXTestnet -> EllipticCurve.Ed25519Slip0010
|
||||
Blockchain.Casper -> EllipticCurve.Secp256k1
|
||||
Blockchain.CasperTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Core -> EllipticCurve.Secp256k1
|
||||
Blockchain.CoreTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Xodex -> EllipticCurve.Secp256k1
|
||||
Blockchain.Canxium -> EllipticCurve.Secp256k1
|
||||
Blockchain.Chiliz -> EllipticCurve.Secp256k1
|
||||
Blockchain.ChilizTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Clore -> EllipticCurve.Secp256k1
|
||||
Blockchain.VanarChain -> EllipticCurve.Secp256k1
|
||||
Blockchain.VanarChainTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.OdysseyChain -> EllipticCurve.Secp256k1
|
||||
Blockchain.OdysseyChainTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Bitrock -> EllipticCurve.Secp256k1
|
||||
Blockchain.BitrockTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Sonic -> EllipticCurve.Secp256k1
|
||||
Blockchain.SonicTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.ApeChain -> EllipticCurve.Secp256k1
|
||||
Blockchain.ApeChainTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.KaspaTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Alephium -> EllipticCurve.Secp256k1
|
||||
Blockchain.AlephiumTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Scroll -> EllipticCurve.Secp256k1
|
||||
Blockchain.ScrollTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.ZkLinkNova -> EllipticCurve.Secp256k1
|
||||
Blockchain.ZkLinkNovaTestnet -> EllipticCurve.Secp256k1
|
||||
Blockchain.Pepecoin -> EllipticCurve.Secp256k1
|
||||
Blockchain.PepecoinTestnet -> EllipticCurve.Secp256k1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
package com.tangem.domain.common.extensions
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.card.FirmwareVersion
|
||||
import com.tangem.domain.common.CardTypesResolver
|
||||
import com.tangem.domain.common.TapWorkarounds.isTestCard
|
||||
import com.tangem.domain.common.configs.CardConfig
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
|
||||
// TODO: refactor [REDACTED_JIRA]
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
val FirmwareVersion.Companion.SolanaTokensAvailable
|
||||
get() = FirmwareVersion(4, 52)
|
||||
|
||||
fun UserWallet.supportedBlockchains(excludedBlockchains: ExcludedBlockchains): List<Blockchain> {
|
||||
return when (this) {
|
||||
is UserWallet.Cold -> {
|
||||
scanResponse.card.supportedBlockchains(
|
||||
excludedBlockchains = excludedBlockchains,
|
||||
cardTypesResolver = this.cardTypesResolver,
|
||||
)
|
||||
}
|
||||
is UserWallet.Hot -> {
|
||||
Blockchain.entries.filter { it !in excludedBlockchains }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun CardDTO.supportedBlockchains(
|
||||
cardTypesResolver: CardTypesResolver,
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
): List<Blockchain> {
|
||||
val supportedBlockchains = if (firmwareVersion < FirmwareVersion.MultiWalletAvailable) {
|
||||
Blockchain.fromCurve(EllipticCurve.Secp256k1).toMutableList()
|
||||
} else if (!cardTypesResolver.isWallet2() && !cardTypesResolver.isTangemWallet()) {
|
||||
// need for old multiwallet that supports only secp256k1
|
||||
wallets.flatMap { Blockchain.fromCurve(it.curve) }.distinct().toMutableList()
|
||||
} else {
|
||||
// multiwallet supports all blockchains, move this logic to config
|
||||
Blockchain.entries.toMutableList()
|
||||
}
|
||||
return supportedBlockchains
|
||||
.filter { isTestCard == it.isTestnet() }
|
||||
.filter { it !in excludedBlockchains }
|
||||
}
|
||||
|
||||
fun CardDTO.supportedTokens(
|
||||
cardTypesResolver: CardTypesResolver,
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
): List<Blockchain> {
|
||||
val tokensSupportedByBlockchain = supportedBlockchains(cardTypesResolver, excludedBlockchains)
|
||||
.filter { it.canHandleTokens() }
|
||||
.toMutableList()
|
||||
val tokensSupportedByCard = when {
|
||||
firmwareVersion >= FirmwareVersion.SolanaTokensAvailable -> tokensSupportedByBlockchain
|
||||
else -> {
|
||||
tokensSupportedByBlockchain.apply {
|
||||
remove(Blockchain.Solana)
|
||||
remove(Blockchain.SolanaTestnet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tokensSupportedByCard.filter { isTestCard == it.isTestnet() }
|
||||
}
|
||||
|
||||
fun UserWallet.canHandleToken(supportedTokens: List<Blockchain>, blockchain: Blockchain): Boolean {
|
||||
return when (this) {
|
||||
is UserWallet.Cold -> {
|
||||
scanResponse.card.canHandleToken(
|
||||
supportedTokens = supportedTokens,
|
||||
blockchain = blockchain,
|
||||
cardTypesResolver = scanResponse.cardTypesResolver,
|
||||
)
|
||||
}
|
||||
is UserWallet.Hot -> blockchain in supportedTokens
|
||||
}
|
||||
}
|
||||
|
||||
fun UserWallet.canHandleToken(blockchain: Blockchain, excludedBlockchains: ExcludedBlockchains): Boolean {
|
||||
return when (this) {
|
||||
is UserWallet.Cold -> {
|
||||
scanResponse.card.canHandleToken(
|
||||
blockchain = blockchain,
|
||||
excludedBlockchains = excludedBlockchains,
|
||||
cardTypesResolver = scanResponse.cardTypesResolver,
|
||||
)
|
||||
}
|
||||
|
||||
is UserWallet.Hot -> blockchain !in excludedBlockchains
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The same as [CardDTO.supportedTokens] but with supportedTokens input, if previously calculated
|
||||
*/
|
||||
fun CardDTO.canHandleToken(
|
||||
supportedTokens: List<Blockchain>,
|
||||
blockchain: Blockchain,
|
||||
cardTypesResolver: CardTypesResolver,
|
||||
): Boolean {
|
||||
val cardConfig = CardConfig.createConfig(this)
|
||||
val primaryCurveForBlockchain = cardConfig.primaryCurve(blockchain)
|
||||
val isContainsBlockchain = supportedTokens.contains(blockchain)
|
||||
val isWalletForCurveExists = wallets.any { it.curve == primaryCurveForBlockchain }
|
||||
// fixme: check for first wallets with 1 curve and remove condition
|
||||
return if (cardTypesResolver.isTangemWallet() || cardTypesResolver.isWallet2()) {
|
||||
// if there's no wallet on card for blockchain with given curve
|
||||
isContainsBlockchain && isWalletForCurveExists
|
||||
} else {
|
||||
isContainsBlockchain
|
||||
}
|
||||
}
|
||||
|
||||
fun CardDTO.canHandleToken(
|
||||
blockchain: Blockchain,
|
||||
cardTypesResolver: CardTypesResolver,
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
): Boolean {
|
||||
val cardConfig = CardConfig.createConfig(this)
|
||||
val primaryCurveForBlockchain = cardConfig.primaryCurve(blockchain)
|
||||
val isContainsBlockchain = blockchain in supportedTokens(cardTypesResolver, excludedBlockchains)
|
||||
val isWalletForCurveExists = wallets.any { it.curve == primaryCurveForBlockchain }
|
||||
// fixme: check for first wallets with 1 curve and remove condition
|
||||
return if (cardTypesResolver.isTangemWallet() || cardTypesResolver.isWallet2()) {
|
||||
// if there's no wallet on card for blockchain with given curve
|
||||
isContainsBlockchain && isWalletForCurveExists
|
||||
} else {
|
||||
isContainsBlockchain
|
||||
}
|
||||
}
|
||||
|
||||
fun CardDTO.canHandleBlockchain(
|
||||
blockchain: Blockchain,
|
||||
cardTypesResolver: CardTypesResolver,
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
): Boolean {
|
||||
val cardConfig = CardConfig.createConfig(this)
|
||||
val primaryCurveForBlockchain = cardConfig.primaryCurve(blockchain)
|
||||
val isContainsBlockchain =
|
||||
blockchain in supportedBlockchains(cardTypesResolver, excludedBlockchains)
|
||||
val isWalletForCurveExists = wallets.any { it.curve == primaryCurveForBlockchain }
|
||||
// fixme: check for first wallets with 1 curve and remove condition
|
||||
return if (cardTypesResolver.isTangemWallet() || cardTypesResolver.isWallet2()) {
|
||||
// if there's no wallet on card for blockchain with given curve
|
||||
isContainsBlockchain && isWalletForCurveExists
|
||||
} else {
|
||||
isContainsBlockchain
|
||||
}
|
||||
}
|
||||
|
|
@ -1,151 +0,0 @@
|
|||
package com.tangem.domain.common.extensions
|
||||
|
||||
import com.tangem.blockchain.blockchains.cardano.CardanoUtils
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.common.derivation.DerivationStyle
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.extensions.toMapKey
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.domain.common.TapWorkarounds.isTestCard
|
||||
import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation
|
||||
import com.tangem.domain.common.configs.CardConfig
|
||||
import com.tangem.domain.common.configs.Wallet2CardConfig
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
|
||||
fun WalletManagerFactory.makeWalletManagerForApp(
|
||||
scanResponse: ScanResponse,
|
||||
blockchain: Blockchain,
|
||||
derivationParams: DerivationParams?,
|
||||
): WalletManager? {
|
||||
val card = scanResponse.card
|
||||
val cardConfig = CardConfig.createConfig(card)
|
||||
if (card.isTestCard && blockchain.getTestnetVersion() == null) return null
|
||||
val supportedCurves = blockchain.getSupportedCurves()
|
||||
|
||||
val wallets = card.wallets.filter { wallet -> supportedCurves.contains(wallet.curve) }
|
||||
val wallet = selectWallet(
|
||||
wallets = wallets,
|
||||
cardConfig = cardConfig,
|
||||
blockchain = blockchain,
|
||||
) ?: return null
|
||||
|
||||
val environmentBlockchain =
|
||||
if (card.isTestCard) blockchain.getTestnetVersion()!! else blockchain
|
||||
|
||||
val seedKey = wallet.extendedPublicKey
|
||||
return when {
|
||||
scanResponse.cardTypesResolver.isTangemTwins() && scanResponse.secondTwinPublicKey != null -> {
|
||||
createTwinWalletManager(
|
||||
walletPublicKey = wallet.publicKey,
|
||||
pairPublicKey = scanResponse.secondTwinPublicKey!!.hexToBytes(),
|
||||
blockchain = environmentBlockchain,
|
||||
curve = wallet.curve,
|
||||
)
|
||||
}
|
||||
scanResponse.card.settings.isHDWalletAllowed && seedKey != null && derivationParams != null -> {
|
||||
val derivedKeys = scanResponse.derivedKeys[wallet.publicKey.toMapKey()]
|
||||
val derivationPath = derivationParams.getPath(blockchain)
|
||||
|
||||
val publicKey = makePublicKey(
|
||||
seedKey = wallet.publicKey,
|
||||
blockchain = blockchain,
|
||||
derivationPath = derivationPath ?: return null,
|
||||
derivedWalletKeys = derivedKeys ?: return null,
|
||||
isWallet2 = scanResponse.cardTypesResolver.isWallet2(),
|
||||
) ?: return null
|
||||
|
||||
createWalletManager(
|
||||
blockchain = environmentBlockchain,
|
||||
publicKey = publicKey,
|
||||
curve = wallet.curve,
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
createLegacyWalletManager(
|
||||
blockchain = environmentBlockchain,
|
||||
walletPublicKey = wallet.publicKey,
|
||||
curve = wallet.curve,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun makePublicKey(
|
||||
seedKey: ByteArray,
|
||||
blockchain: Blockchain,
|
||||
derivationPath: DerivationPath,
|
||||
derivedWalletKeys: Map<DerivationPath, ExtendedPublicKey>,
|
||||
isWallet2: Boolean,
|
||||
): Wallet.PublicKey? {
|
||||
val derivedKey = derivedWalletKeys[derivationPath] ?: return null
|
||||
|
||||
val derivationKey = Wallet.HDKey(
|
||||
path = derivationPath,
|
||||
extendedPublicKey = derivedKey,
|
||||
)
|
||||
|
||||
// we should generate second key for cardano
|
||||
// because cardano address generation for wallet2 requires keys from 2 derivations
|
||||
// https://developers.cardano.org/docs/get-started/cardano-serialization-lib/generating-keys/
|
||||
if (blockchain == Blockchain.Cardano && isWallet2) {
|
||||
val extendedDerivationPath = CardanoUtils.extendedDerivationPath(derivationPath)
|
||||
val secondDerivedKey = derivedWalletKeys[extendedDerivationPath] ?: error("No derivation found")
|
||||
|
||||
val secondDerivationKey = Wallet.HDKey(secondDerivedKey, extendedDerivationPath)
|
||||
|
||||
return Wallet.PublicKey(
|
||||
seedKey = seedKey,
|
||||
derivationType = Wallet.PublicKey.DerivationType.Double(derivationKey, secondDerivationKey),
|
||||
)
|
||||
}
|
||||
|
||||
return Wallet.PublicKey(
|
||||
seedKey = seedKey,
|
||||
derivationType = Wallet.PublicKey.DerivationType.Plain(derivationKey),
|
||||
)
|
||||
}
|
||||
|
||||
private fun getDerivationParams(card: CardDTO): DerivationParams? {
|
||||
return if (!card.settings.isHDWalletAllowed) {
|
||||
null
|
||||
} else if (card.useOldStyleDerivation) {
|
||||
DerivationParams.Default(DerivationStyle.LEGACY)
|
||||
} else {
|
||||
DerivationParams.Default(DerivationStyle.NEW)
|
||||
}
|
||||
}
|
||||
|
||||
fun WalletManagerFactory.makePrimaryWalletManager(scanResponse: ScanResponse): WalletManager? {
|
||||
val blockchain = if (scanResponse.card.isTestCard) {
|
||||
scanResponse.cardTypesResolver.getBlockchain().getTestnetVersion() ?: return null
|
||||
} else {
|
||||
scanResponse.cardTypesResolver.getBlockchain()
|
||||
}
|
||||
val derivationParams = getDerivationParams(scanResponse.card)
|
||||
return makeWalletManagerForApp(
|
||||
scanResponse = scanResponse,
|
||||
blockchain = blockchain,
|
||||
derivationParams = derivationParams,
|
||||
)
|
||||
}
|
||||
|
||||
private fun selectWallet(
|
||||
wallets: List<CardDTO.Wallet>,
|
||||
cardConfig: CardConfig,
|
||||
blockchain: Blockchain,
|
||||
): CardDTO.Wallet? {
|
||||
return if (cardConfig is Wallet2CardConfig) {
|
||||
val primaryCurve = cardConfig.primaryCurve(blockchain)
|
||||
wallets.firstOrNull { it.curve == primaryCurve }
|
||||
} else {
|
||||
when (wallets.size) {
|
||||
0 -> null
|
||||
1 -> wallets[0]
|
||||
else -> wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 } ?: wallets[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
package com.tangem.domain.common.util
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.extensions.toMapKey
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.domain.common.*
|
||||
import com.tangem.domain.common.TapWorkarounds.isTangemTwins
|
||||
import com.tangem.domain.common.TapWorkarounds.isTestCard
|
||||
import com.tangem.domain.common.configs.CardConfig
|
||||
import com.tangem.domain.common.configs.Wallet2CardConfig
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
|
||||
val ScanResponse.cardTypesResolver: CardTypesResolver
|
||||
get() = TangemCardTypesResolver(
|
||||
card = card,
|
||||
productType = productType,
|
||||
walletData = walletData,
|
||||
)
|
||||
|
||||
val UserWallet.derivationStyleProvider: DerivationStyleProvider
|
||||
get() = when (this) {
|
||||
is UserWallet.Cold -> this.scanResponse.derivationStyleProvider
|
||||
is UserWallet.Hot -> TangemHotDerivationStyleProvider()
|
||||
}
|
||||
|
||||
val ScanResponse.derivationStyleProvider: DerivationStyleProvider
|
||||
get() = card.derivationStyleProvider
|
||||
|
||||
val CardDTO.derivationStyleProvider: DerivationStyleProvider
|
||||
get() = TangemDerivationStyleProvider(this)
|
||||
|
||||
val UserWallet.Cold.cardTypesResolver: CardTypesResolver
|
||||
get() = scanResponse.cardTypesResolver
|
||||
|
||||
fun ScanResponse.twinsIsTwinned(): Boolean = card.isTangemTwins && walletData != null && secondTwinPublicKey != null
|
||||
|
||||
fun ScanResponse.hasDerivation(blockchain: Blockchain, rawDerivationPath: String): Boolean {
|
||||
return hasDerivation(blockchain, DerivationPath(rawDerivationPath))
|
||||
}
|
||||
|
||||
private fun ScanResponse.hasDerivation(blockchain: Blockchain, derivationPath: DerivationPath): Boolean {
|
||||
val isTestnet = card.isTestCard || blockchain.isTestnet()
|
||||
val config = CardConfig.createConfig(card)
|
||||
return if (config is Wallet2CardConfig) {
|
||||
// new logic for wallet2
|
||||
val primaryCurve = config.primaryCurve(blockchain)
|
||||
primaryCurve?.let { hasDerivation(it, derivationPath) } == true
|
||||
} else {
|
||||
// leave logic for legacy wallets
|
||||
when {
|
||||
Blockchain.secp256k1Blockchains(isTestnet).contains(blockchain) -> {
|
||||
hasDerivation(EllipticCurve.Secp256k1, derivationPath)
|
||||
}
|
||||
Blockchain.ed25519Blockchains(isTestnet).contains(blockchain) -> {
|
||||
hasDerivation(EllipticCurve.Ed25519, derivationPath)
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ScanResponse.hasDerivation(curve: EllipticCurve, derivationPath: DerivationPath): Boolean {
|
||||
val foundWallet = card.wallets.firstOrNull { it.curve == curve }
|
||||
?: return false
|
||||
val extendedPublicKeysMap = derivedKeys[foundWallet.publicKey.toMapKey()] ?: return false
|
||||
val extendedPublicKey = extendedPublicKeysMap[derivationPath]
|
||||
return extendedPublicKey != null
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total cards count in wallets set for this [ScanResponse] card
|
||||
*
|
||||
* @return null if wallet is not multi-currency or total cards count
|
||||
*/
|
||||
fun ScanResponse.getCardsCount(): Int? {
|
||||
if (cardTypesResolver.isTangemTwins()) return 2
|
||||
if (!cardTypesResolver.isMultiwalletAllowed()) return null
|
||||
|
||||
return when (val status = card.backupStatus) {
|
||||
is CardDTO.BackupStatus.Active -> status.cardCount + 1
|
||||
is CardDTO.BackupStatus.NoBackup,
|
||||
is CardDTO.BackupStatus.CardLinked,
|
||||
null, // Multi-currency wallet without backup function. Example, 4.12
|
||||
-> 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get backup cards count for this [ScanResponse] card
|
||||
*
|
||||
* @return null if wallet is not multi-currency or total cards count
|
||||
*/
|
||||
fun ScanResponse.getBackupCardsCount(): Int? {
|
||||
return if (cardTypesResolver.isMultiwalletAllowed()) {
|
||||
when (val status = card.backupStatus) {
|
||||
is CardDTO.BackupStatus.Active -> status.cardCount
|
||||
else -> 0
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package com.tangem.domain.common.util
|
||||
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
|
||||
/**
|
||||
* Get total cards count in wallets set for a card that was saved in [UserWallet]
|
||||
*
|
||||
* @return null if wallet is not multi-currency or total cards count
|
||||
*/
|
||||
fun UserWallet.Cold.getCardsCount(): Int? = scanResponse.getCardsCount()
|
||||
|
||||
/**
|
||||
* Get backup cards count for a card that was saved in [UserWallet]
|
||||
*
|
||||
* @return null if wallet is not multi-currency or total cards count
|
||||
*/
|
||||
fun UserWallet.Cold.getBackupCardsCount(): Int? = scanResponse.getBackupCardsCount()
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
package com.tangem.domain.common.visa
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.derivation.DerivationStyle
|
||||
import com.tangem.common.card.FirmwareVersion
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
|
||||
private const val VISA_BATCH_START = "AE"
|
||||
private const val VISA_BATCH_START_2 = "FFFC"
|
||||
|
||||
object VisaUtilities {
|
||||
|
||||
const val tokenId = "tether"
|
||||
|
||||
val visaBlockchain = Blockchain.Polygon
|
||||
|
||||
val visaDefaultDerivationPath
|
||||
get() = visaBlockchain.derivationPath(DerivationStyle.V3)
|
||||
|
||||
fun visaDefaultDerivationPath(style: DerivationStyle) = visaBlockchain.derivationPath(style)
|
||||
|
||||
fun isVisaCard(card: CardDTO): Boolean {
|
||||
return isVisaCard(card.firmwareVersion.doubleValue, card.batchId)
|
||||
}
|
||||
|
||||
fun isVisaCard(firmwareVersion: Double, batchId: String): Boolean {
|
||||
return firmwareVersion in FirmwareVersion.visaRange &&
|
||||
(batchId.startsWith(VISA_BATCH_START) || batchId.startsWith(VISA_BATCH_START_2))
|
||||
}
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
package com.tangem.domain.common.visa
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.raise.catch
|
||||
import arrow.core.raise.either
|
||||
import com.tangem.blockchain.common.address.Address
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.visa.error.VisaActivationError
|
||||
|
||||
object VisaWalletPublicKeyUtility {
|
||||
|
||||
fun validateExtendedPublicKey(
|
||||
targetAddress: String,
|
||||
extendedPublicKey: ExtendedPublicKey,
|
||||
): Either<VisaActivationError, Unit> = either {
|
||||
validatePublicKey(
|
||||
targetAddress = targetAddress,
|
||||
publicKey = extendedPublicKey.publicKey,
|
||||
).bind()
|
||||
}
|
||||
|
||||
fun findKeyWithoutDerivation(targetAddress: String, card: CardDTO): Either<VisaActivationError, ByteArray> =
|
||||
either {
|
||||
val wallet = findWalletOnSecp256k1(card).bind()
|
||||
|
||||
validatePublicKey(
|
||||
targetAddress = targetAddress,
|
||||
publicKey = wallet.publicKey,
|
||||
).bind()
|
||||
|
||||
wallet.publicKey
|
||||
}
|
||||
|
||||
fun generateAddressOnSecp256k1(walletPublicKey: ByteArray): Either<VisaActivationError, Address> = either {
|
||||
val addresses = catch(
|
||||
block = {
|
||||
VisaUtilities.visaBlockchain.makeAddresses(
|
||||
walletPublicKey = walletPublicKey,
|
||||
pairPublicKey = null,
|
||||
curve = EllipticCurve.Secp256k1,
|
||||
)
|
||||
},
|
||||
catch = { raise(VisaActivationError.FailedToCreateAddress) },
|
||||
)
|
||||
|
||||
addresses.firstOrNull { it.type == AddressType.Default } ?: raise(VisaActivationError.FailedToCreateAddress)
|
||||
}
|
||||
|
||||
private fun findWalletOnSecp256k1(card: CardDTO): Either<VisaActivationError, CardDTO.Wallet> = either {
|
||||
card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 } ?: raise(VisaActivationError.MissingWallet)
|
||||
}
|
||||
|
||||
private fun validatePublicKey(targetAddress: String, publicKey: ByteArray): Either<VisaActivationError, Unit> =
|
||||
either {
|
||||
val address = generateAddressOnSecp256k1(publicKey).bind()
|
||||
|
||||
if (address.value.lowercase() != targetAddress.lowercase()) {
|
||||
raise(VisaActivationError.AddressNotMatched)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,748 +0,0 @@
|
|||
package com.tangem.domain.walletmanager
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.raise.either
|
||||
import arrow.core.raise.ensureNotNull
|
||||
import arrow.core.right
|
||||
import com.tangem.blockchain.blockchains.solana.RentProvider
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.common.address.Address
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.blockchain.common.address.EstimationFeeAddressFactory
|
||||
import com.tangem.blockchain.common.pagination.Page
|
||||
import com.tangem.blockchain.common.smartcontract.SmartContractCallDataProviderFactory
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.blockchain.common.trustlines.AssetRequirementsManager
|
||||
import com.tangem.blockchain.extensions.Result
|
||||
import com.tangem.blockchain.extensions.SimpleResult
|
||||
import com.tangem.blockchain.nft.models.NFTAsset
|
||||
import com.tangem.blockchain.nft.models.NFTCollection
|
||||
import com.tangem.blockchain.transactionhistory.models.TransactionHistoryRequest
|
||||
import com.tangem.blockchainsdk.BlockchainSDKFactory
|
||||
import com.tangem.blockchainsdk.models.UpdateWalletManagerResult
|
||||
import com.tangem.blockchainsdk.utils.toBlockchain
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.datasource.asset.loader.AssetLoader
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.datasource.local.walletmanager.WalletManagersStore
|
||||
import com.tangem.domain.common.util.hasDerivation
|
||||
import com.tangem.domain.demo.DemoConfig
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.network.TxInfo
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.transaction.models.AssetRequirementsCondition
|
||||
import com.tangem.domain.txhistory.models.PaginationWrapper
|
||||
import com.tangem.domain.txhistory.models.TxHistoryState
|
||||
import com.tangem.domain.walletmanager.model.RentData
|
||||
import com.tangem.domain.walletmanager.model.SmartContractMethod
|
||||
import com.tangem.domain.walletmanager.model.TokenInfo
|
||||
import com.tangem.domain.walletmanager.utils.*
|
||||
import com.tangem.domain.walletmanager.utils.WalletManagerFactory
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
import java.util.EnumSet
|
||||
|
||||
@Suppress("LargeClass", "TooManyFunctions")
|
||||
// FIXME: Move to its own module and make internal
|
||||
@Deprecated("Inject the WalletManagerFacade interface using DI instead")
|
||||
class DefaultWalletManagersFacade(
|
||||
private val walletManagersStore: WalletManagersStore,
|
||||
private val userWalletsStore: UserWalletsStore,
|
||||
private val assetLoader: AssetLoader,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
blockchainSDKFactory: BlockchainSDKFactory,
|
||||
) : WalletManagersFacade {
|
||||
|
||||
private val demoConfig by lazy { DemoConfig() }
|
||||
private val resultFactory by lazy { UpdateWalletManagerResultFactory() }
|
||||
private val walletManagerFactory by lazy { WalletManagerFactory(blockchainSDKFactory) }
|
||||
private val sdkTokenConverter by lazy { SdkTokenConverter() }
|
||||
private val txHistoryStateConverter by lazy { SdkTransactionHistoryStateConverter() }
|
||||
private val sdkPageConverter by lazy { SdkPageConverter() }
|
||||
private val cryptoCurrencyTypeConverter by lazy { CryptoCurrencyTypeConverter() }
|
||||
private val requirementsConditionConverter by lazy { SdkRequirementsConditionConverter() }
|
||||
private val estimationFeeAddressFactory by lazy { EstimationFeeAddressFactory() }
|
||||
|
||||
private val initMutex = Mutex()
|
||||
|
||||
override suspend fun update(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
extraTokens: Set<CryptoCurrency.Token>,
|
||||
): UpdateWalletManagerResult {
|
||||
val userWallet = getUserWallet(userWalletId)
|
||||
val blockchain = network.toBlockchain()
|
||||
val derivationPath = network.derivationPath.value
|
||||
|
||||
return getAndUpdateWalletManager(userWallet, blockchain, derivationPath, extraTokens)
|
||||
}
|
||||
|
||||
override suspend fun remove(userWalletId: UserWalletId, networks: Set<Network>) {
|
||||
if (networks.isEmpty()) return
|
||||
|
||||
val blockchainsToDerivationPaths = networks.map {
|
||||
it.toBlockchain() to it.derivationPath.value
|
||||
}
|
||||
|
||||
withContext(dispatchers.io) {
|
||||
walletManagersStore.remove(userWalletId) { walletManager ->
|
||||
val wallet = walletManager.wallet
|
||||
val blockchainToDerivationPath = wallet.blockchain to wallet.publicKey.derivationPath?.rawPath
|
||||
|
||||
blockchainToDerivationPath in blockchainsToDerivationPaths
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun removeTokens(userWalletId: UserWalletId, tokens: Set<CryptoCurrency.Token>) {
|
||||
if (tokens.isEmpty()) return
|
||||
|
||||
tokens
|
||||
.groupBy(CryptoCurrency.Token::network)
|
||||
.forEach { (network, networkTokens) ->
|
||||
removeTokens(
|
||||
userWalletId = userWalletId,
|
||||
network = network,
|
||||
networkTokens = sdkTokenConverter.convertList(networkTokens),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun removeTokensByTokenInfo(userWalletId: UserWalletId, tokenInfos: Set<TokenInfo>) {
|
||||
if (tokenInfos.isEmpty()) return
|
||||
|
||||
tokenInfos
|
||||
.groupBy { it.network }
|
||||
.forEach { (network, tokenInfoList) ->
|
||||
removeTokens(
|
||||
userWalletId = userWalletId,
|
||||
network = network,
|
||||
networkTokens = tokenInfoList.map {
|
||||
Token(
|
||||
name = it.name,
|
||||
symbol = it.symbol,
|
||||
contractAddress = it.contractAddress,
|
||||
decimals = it.decimals,
|
||||
id = it.id,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun removeTokens(userWalletId: UserWalletId, network: Network, networkTokens: List<Token>) {
|
||||
withContext(dispatchers.io) {
|
||||
val walletManager = walletManagersStore.getSyncOrNull(
|
||||
userWalletId = userWalletId,
|
||||
blockchain = network.toBlockchain(),
|
||||
derivationPath = network.derivationPath.value,
|
||||
) ?: return@withContext
|
||||
|
||||
networkTokens.forEach { token ->
|
||||
walletManager.removeToken(token)
|
||||
}
|
||||
|
||||
walletManagersStore.store(userWalletId, walletManager)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun updatePendingTransactions(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
): UpdateWalletManagerResult {
|
||||
val userWallet = getUserWallet(userWalletId)
|
||||
val blockchain = network.toBlockchain()
|
||||
val derivationPath = network.derivationPath.value
|
||||
|
||||
if (derivationPath != null &&
|
||||
userWallet is UserWallet.Cold &&
|
||||
!userWallet.scanResponse.hasDerivation(blockchain, derivationPath)
|
||||
) {
|
||||
Timber.w("Derivation missed for: $blockchain")
|
||||
return UpdateWalletManagerResult.MissedDerivation
|
||||
}
|
||||
|
||||
val walletManager = getOrCreateWalletManager(userWalletId, blockchain, derivationPath)
|
||||
if (walletManager == null || blockchain == Blockchain.Unknown) {
|
||||
Timber.w("Unable to get a wallet manager for blockchain: $blockchain")
|
||||
return UpdateWalletManagerResult.Unreachable()
|
||||
}
|
||||
|
||||
return getLastWalletManagerResult(walletManager)
|
||||
}
|
||||
|
||||
override suspend fun getExploreUrl(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
addressType: AddressType,
|
||||
contractAddress: String?,
|
||||
): String {
|
||||
val blockchain = network.toBlockchain()
|
||||
val walletManager = getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
network = network,
|
||||
)
|
||||
|
||||
requireNotNull(walletManager) {
|
||||
"Unable to get a wallet manager for blockchain: $blockchain"
|
||||
}
|
||||
|
||||
val address = walletManager
|
||||
.wallet
|
||||
.addresses
|
||||
.find { it.type == addressType }
|
||||
?.value ?: walletManager.wallet.address
|
||||
return blockchain.getExploreUrl(address, contractAddress)
|
||||
}
|
||||
|
||||
override suspend fun getTxHistoryState(userWalletId: UserWalletId, currency: CryptoCurrency): TxHistoryState {
|
||||
val walletManager = getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
network = currency.network,
|
||||
)
|
||||
|
||||
requireNotNull(walletManager) {
|
||||
"Unable to get a wallet manager for blockchain: ${currency.network}"
|
||||
}
|
||||
|
||||
return walletManager
|
||||
.getTransactionHistoryState(
|
||||
address = walletManager.wallet.address,
|
||||
filterType = when (currency) {
|
||||
is CryptoCurrency.Coin -> TransactionHistoryRequest.FilterType.Coin
|
||||
is CryptoCurrency.Token -> {
|
||||
val blockchainToken = Token(
|
||||
name = currency.name,
|
||||
symbol = currency.symbol,
|
||||
contractAddress = currency.contractAddress,
|
||||
decimals = currency.decimals,
|
||||
id = currency.id.rawCurrencyId?.value,
|
||||
)
|
||||
TransactionHistoryRequest.FilterType.Contract(blockchainToken)
|
||||
}
|
||||
},
|
||||
)
|
||||
.let(txHistoryStateConverter::convert)
|
||||
}
|
||||
|
||||
override suspend fun getTxHistoryItems(
|
||||
userWalletId: UserWalletId,
|
||||
currency: CryptoCurrency,
|
||||
page: Page,
|
||||
pageSize: Int,
|
||||
): PaginationWrapper<TxInfo> {
|
||||
val walletManager = getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
network = currency.network,
|
||||
)
|
||||
|
||||
requireNotNull(walletManager) {
|
||||
"Unable to get a wallet manager for blockchain: ${currency.network}"
|
||||
}
|
||||
|
||||
val itemsResult = walletManager.getTransactionsHistory(
|
||||
request = TransactionHistoryRequest(
|
||||
address = walletManager.wallet.address,
|
||||
decimals = currency.decimals,
|
||||
page = page,
|
||||
pageSize = pageSize,
|
||||
filterType = when (currency) {
|
||||
is CryptoCurrency.Coin -> TransactionHistoryRequest.FilterType.Coin
|
||||
is CryptoCurrency.Token -> {
|
||||
val blockchainToken = Token(
|
||||
name = currency.name,
|
||||
symbol = currency.symbol,
|
||||
contractAddress = currency.contractAddress,
|
||||
decimals = currency.decimals,
|
||||
id = currency.id.rawCurrencyId?.value,
|
||||
)
|
||||
TransactionHistoryRequest.FilterType.Contract(blockchainToken)
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
return when (itemsResult) {
|
||||
is Result.Success -> PaginationWrapper(
|
||||
currentPage = sdkPageConverter.convert(page),
|
||||
nextPage = sdkPageConverter.convert(itemsResult.data.nextPage),
|
||||
items = SdkTransactionHistoryItemConverter(smartContractMethods = readSmartContractMethods())
|
||||
.convertList(itemsResult.data.items),
|
||||
)
|
||||
is Result.Failure -> error(itemsResult.error.message ?: itemsResult.error.customMessage)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getUserWallet(userWalletId: UserWalletId) = userWalletsStore.getSyncStrict(userWalletId)
|
||||
|
||||
private suspend fun getAndUpdateWalletManager(
|
||||
userWallet: UserWallet,
|
||||
blockchain: Blockchain,
|
||||
derivationPath: String?,
|
||||
extraTokens: Set<CryptoCurrency.Token>,
|
||||
): UpdateWalletManagerResult {
|
||||
if (derivationPath != null &&
|
||||
userWallet is UserWallet.Cold &&
|
||||
!userWallet.scanResponse.hasDerivation(blockchain, derivationPath)
|
||||
) {
|
||||
Timber.w("Derivation missed for: $blockchain")
|
||||
return UpdateWalletManagerResult.MissedDerivation
|
||||
}
|
||||
|
||||
val walletManager = getOrCreateWalletManager(
|
||||
userWalletId = userWallet.walletId,
|
||||
blockchain = blockchain,
|
||||
derivationPath = derivationPath,
|
||||
)
|
||||
if (walletManager == null || blockchain == Blockchain.Unknown) {
|
||||
Timber.w("Unable to create or find a wallet manager for blockchain: $blockchain")
|
||||
return UpdateWalletManagerResult.Unreachable()
|
||||
}
|
||||
|
||||
updateWalletManagerTokensIfNeeded(walletManager, extraTokens)
|
||||
|
||||
return try {
|
||||
if (userWallet is UserWallet.Cold && demoConfig.isDemoCardId(userWallet.scanResponse.card.cardId)) {
|
||||
updateDemoWalletManager(walletManager)
|
||||
} else {
|
||||
updateWalletManager(walletManager)
|
||||
}
|
||||
} finally {
|
||||
walletManagersStore.store(userWallet.walletId, walletManager)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateDemoWalletManager(walletManager: WalletManager): UpdateWalletManagerResult {
|
||||
val amount = demoConfig.getBalance(walletManager.wallet.blockchain)
|
||||
walletManager.wallet.setAmount(amount)
|
||||
|
||||
return resultFactory.getDemoResult(walletManager, amount)
|
||||
}
|
||||
|
||||
private suspend fun updateWalletManager(walletManager: WalletManager): UpdateWalletManagerResult {
|
||||
return try {
|
||||
walletManager.update()
|
||||
|
||||
resultFactory.getResult(walletManager)
|
||||
} catch (e: BlockchainSdkError.AccountNotFound) {
|
||||
resultFactory.getNoAccountResult(
|
||||
walletManager = walletManager,
|
||||
customMessage = e.customMessage,
|
||||
amountToCreateAccount = e.amountToCreateAccount,
|
||||
)
|
||||
} catch (e: Throwable) {
|
||||
Timber.w(e, "Unable to update a wallet manager for: ${walletManager.wallet.blockchain}")
|
||||
|
||||
resultFactory.getUnreachableResult(walletManager)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getLastWalletManagerResult(walletManager: WalletManager): UpdateWalletManagerResult {
|
||||
return try {
|
||||
resultFactory.getResult(walletManager)
|
||||
} catch (e: BlockchainSdkError.AccountNotFound) {
|
||||
resultFactory.getNoAccountResult(
|
||||
walletManager = walletManager,
|
||||
customMessage = e.customMessage,
|
||||
amountToCreateAccount = e.amountToCreateAccount,
|
||||
)
|
||||
} catch (e: Throwable) {
|
||||
Timber.w(e, "Unable to update a wallet manager for: ${walletManager.wallet.blockchain}")
|
||||
|
||||
resultFactory.getUnreachableResult(walletManager)
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("Will be removed in future")
|
||||
override suspend fun getOrCreateWalletManager(
|
||||
userWalletId: UserWalletId,
|
||||
blockchain: Blockchain,
|
||||
derivationPath: String?,
|
||||
): WalletManager? {
|
||||
initMutex.withLock {
|
||||
val userWallet = getUserWallet(userWalletId)
|
||||
|
||||
var walletManager = walletManagersStore.getSyncOrNull(
|
||||
userWalletId = userWalletId,
|
||||
blockchain = blockchain,
|
||||
derivationPath = derivationPath,
|
||||
)
|
||||
val path = derivationPath?.let { DerivationPath(rawPath = it) }
|
||||
|
||||
if (walletManager == null) {
|
||||
when (userWallet) {
|
||||
is UserWallet.Hot -> {
|
||||
walletManager = walletManagerFactory.createWalletManagerForHot(
|
||||
hotWallet = userWallet,
|
||||
blockchain = blockchain,
|
||||
derivationPath = path,
|
||||
)
|
||||
}
|
||||
is UserWallet.Cold -> {
|
||||
walletManager = walletManagerFactory.createWalletManager(
|
||||
scanResponse = userWallet.scanResponse,
|
||||
blockchain = blockchain,
|
||||
derivationPath = path,
|
||||
)
|
||||
}
|
||||
}
|
||||
walletManager ?: return null
|
||||
walletManagersStore.store(userWalletId, walletManager)
|
||||
}
|
||||
return walletManager
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("Will be removed in future")
|
||||
override suspend fun getOrCreateWalletManager(userWalletId: UserWalletId, network: Network): WalletManager? {
|
||||
val blockchain = network.toBlockchain()
|
||||
return getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
blockchain = blockchain,
|
||||
derivationPath = network.derivationPath.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Deprecated("Will be removed in future")
|
||||
override suspend fun getStoredWalletManagers(userWalletId: UserWalletId): List<WalletManager> {
|
||||
return walletManagersStore.getAllSync(userWalletId)
|
||||
}
|
||||
|
||||
override suspend fun getDefaultAddress(userWalletId: UserWalletId, network: Network): String? {
|
||||
return getAddresses(userWalletId, network)
|
||||
.firstOrNull { it.type == AddressType.Default }
|
||||
?.value
|
||||
}
|
||||
|
||||
override suspend fun getAddresses(userWalletId: UserWalletId, network: Network): Set<Address> {
|
||||
val manager = getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
network = network,
|
||||
)
|
||||
|
||||
return manager?.wallet?.addresses.orEmpty()
|
||||
}
|
||||
|
||||
override suspend fun getRentInfo(userWalletId: UserWalletId, network: Network): RentData? {
|
||||
val manager = getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
network = network,
|
||||
)
|
||||
if (manager !is RentProvider) return null
|
||||
|
||||
return when (val result = manager.minimalBalanceForRentExemption()) {
|
||||
is Result.Success -> {
|
||||
RentData(manager.rentAmount(), result.data)
|
||||
}
|
||||
is Result.Failure -> null
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("Will be removed in future")
|
||||
override fun getAll(userWalletId: UserWalletId): Flow<List<WalletManager>> {
|
||||
return walletManagersStore.getAll(userWalletId)
|
||||
}
|
||||
|
||||
override suspend fun validateSignatureCount(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
signedHashes: Int,
|
||||
): Either<Throwable, Unit> {
|
||||
return either {
|
||||
val walletManager = getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
network = network,
|
||||
)
|
||||
|
||||
val validator = ensureNotNull(walletManager as? SignatureCountValidator) {
|
||||
raise(IllegalStateException("Wallet manager is not a SignatureCountValidator"))
|
||||
}
|
||||
|
||||
when (val result = validator.validateSignatureCount(signedHashes)) {
|
||||
is SimpleResult.Failure -> raise(result.error)
|
||||
is SimpleResult.Success -> Unit.right()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("Will be removed in future")
|
||||
override suspend fun getFee(
|
||||
amount: Amount,
|
||||
destination: String,
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
): Result<TransactionFee>? = withContext(dispatchers.io) {
|
||||
val walletManager = getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
network = network,
|
||||
)
|
||||
(walletManager as? TransactionSender)?.getFee(
|
||||
amount = amount,
|
||||
destination = destination,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun estimateFee(
|
||||
amount: Amount,
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
): Result<TransactionFee>? = withContext(dispatchers.io) {
|
||||
val blockchain = network.toBlockchain()
|
||||
val walletManager = getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
blockchain = blockchain,
|
||||
derivationPath = network.derivationPath.value,
|
||||
)
|
||||
|
||||
val destination = estimationFeeAddressFactory.makeAddress(blockchain)
|
||||
|
||||
val callData = if (amount.type is AmountType.Token) {
|
||||
SmartContractCallDataProviderFactory.getTokenTransferCallData(
|
||||
destinationAddress = destination,
|
||||
amount = amount,
|
||||
blockchain = blockchain,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
(walletManager as? TransactionSender)?.estimateFee(
|
||||
amount = amount,
|
||||
destination = destination,
|
||||
callData = callData,
|
||||
)
|
||||
}
|
||||
|
||||
@Deprecated("Will be removed in future")
|
||||
override suspend fun validateTransaction(
|
||||
amount: Amount,
|
||||
fee: Amount?,
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
): EnumSet<TransactionError>? {
|
||||
val walletManager = getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
network = network,
|
||||
)
|
||||
return walletManager?.validateTransaction(amount, fee)
|
||||
}
|
||||
|
||||
@Deprecated("Will be removed in future")
|
||||
override suspend fun createTransaction(
|
||||
amount: Amount,
|
||||
fee: Fee,
|
||||
memo: String?,
|
||||
destination: String,
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
): TransactionData? {
|
||||
val walletManager = getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
network = network,
|
||||
)
|
||||
|
||||
return walletManager?.createTransaction(amount, fee, destination)
|
||||
}
|
||||
|
||||
override suspend fun getRecentTransactions(userWalletId: UserWalletId, currency: CryptoCurrency): List<TxInfo> {
|
||||
val walletManager = getOrCreateWalletManager(userWalletId = userWalletId, network = currency.network)
|
||||
|
||||
if (walletManager == null) {
|
||||
Timber.e("Unable to get a wallet manager for blockchain: ${currency.network.id}")
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val transactionDataConverter = TransactionDataToTxHistoryItemConverter(
|
||||
walletAddresses = SdkAddressToAddressConverter.convertList(walletManager.wallet.addresses).toSet(),
|
||||
feePaidCurrency = walletManager.wallet.blockchain.feePaidCurrency(),
|
||||
)
|
||||
|
||||
return walletManager.wallet.recentTransactions
|
||||
.filter { transaction ->
|
||||
when (currency) {
|
||||
is CryptoCurrency.Coin -> transaction.amount.type is AmountType.Coin
|
||||
is CryptoCurrency.Token -> transaction.contractAddress == currency.contractAddress
|
||||
}
|
||||
}
|
||||
.mapNotNull(transactionDataConverter::convert)
|
||||
}
|
||||
|
||||
override suspend fun tokenBalance(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
name: String,
|
||||
symbol: String,
|
||||
contractAddress: String,
|
||||
decimals: Int,
|
||||
id: String?,
|
||||
): BigDecimal {
|
||||
val blockchain = network.toBlockchain()
|
||||
val walletManager = getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
blockchain = blockchain,
|
||||
derivationPath = network.derivationPath.value,
|
||||
)
|
||||
requireNotNull(walletManager) { "Unable to get a wallet manager for blockchain: $blockchain" }
|
||||
return walletManager.wallet.fundsAvailable(
|
||||
AmountType.Token(
|
||||
token = Token(
|
||||
name = name,
|
||||
symbol = symbol,
|
||||
contractAddress = contractAddress,
|
||||
decimals = decimals,
|
||||
id = id,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getAssetRequirements(
|
||||
userWalletId: UserWalletId,
|
||||
currency: CryptoCurrency,
|
||||
): AssetRequirementsCondition? {
|
||||
return withContext(dispatchers.io) {
|
||||
val walletManager = getOrCreateWalletManager(userWalletId = userWalletId, network = currency.network)
|
||||
val currencyType = cryptoCurrencyTypeConverter.convert(currency)
|
||||
if (walletManager !is AssetRequirementsManager) {
|
||||
return@withContext null
|
||||
}
|
||||
|
||||
val condition = walletManager.requirementsCondition(currencyType) ?: return@withContext null
|
||||
requirementsConditionConverter.convert(condition)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun fulfillRequirements(
|
||||
userWalletId: UserWalletId,
|
||||
currency: CryptoCurrency,
|
||||
signer: TransactionSigner,
|
||||
): SimpleResult {
|
||||
return withContext(dispatchers.io) {
|
||||
val walletManager = getOrCreateWalletManager(userWalletId = userWalletId, network = currency.network)
|
||||
val currencyType = cryptoCurrencyTypeConverter.convert(currency)
|
||||
|
||||
if (walletManager !is AssetRequirementsManager) {
|
||||
return@withContext SimpleResult.Failure(
|
||||
BlockchainSdkError.CustomError("WalletManager is not implemented AssetRequirementsManager"),
|
||||
)
|
||||
}
|
||||
|
||||
walletManager.fulfillRequirements(currencyType, signer)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun discardRequirements(userWalletId: UserWalletId, currency: CryptoCurrency): SimpleResult {
|
||||
return withContext(dispatchers.io) {
|
||||
val walletManager = getOrCreateWalletManager(userWalletId = userWalletId, network = currency.network)
|
||||
val currencyType = cryptoCurrencyTypeConverter.convert(currency)
|
||||
|
||||
if (walletManager !is AssetRequirementsManager) {
|
||||
return@withContext SimpleResult.Failure(
|
||||
BlockchainSdkError.CustomError("WalletManager is not implemented AssetRequirementsManager"),
|
||||
)
|
||||
}
|
||||
|
||||
walletManager.discardRequirements(currencyType)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun checkUtxoConsolidationAvailability(userWalletId: UserWalletId, network: Network): Boolean {
|
||||
val blockchain = network.toBlockchain()
|
||||
val walletManager = getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
blockchain = blockchain,
|
||||
derivationPath = network.derivationPath.value,
|
||||
) ?: return false
|
||||
|
||||
return (walletManager as? UtxoBlockchainManager)?.allowConsolidation == true
|
||||
}
|
||||
|
||||
override suspend fun getNFTCollections(userWalletId: UserWalletId, network: Network): List<NFTCollection> {
|
||||
val blockchain = network.toBlockchain()
|
||||
val walletManager = getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
blockchain = blockchain,
|
||||
derivationPath = network.derivationPath.value,
|
||||
) ?: return emptyList()
|
||||
val address = walletManager.wallet.address
|
||||
return walletManager.getCollections(address)
|
||||
}
|
||||
|
||||
override suspend fun getNFTAssets(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
collectionIdentifier: NFTCollection.Identifier,
|
||||
): List<NFTAsset> {
|
||||
val blockchain = network.toBlockchain()
|
||||
val walletManager = getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
blockchain = blockchain,
|
||||
derivationPath = network.derivationPath.value,
|
||||
) ?: return emptyList()
|
||||
val address = walletManager.wallet.address
|
||||
return walletManager.getAssets(address, collectionIdentifier)
|
||||
}
|
||||
|
||||
override suspend fun getNFTAsset(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
collectionIdentifier: NFTCollection.Identifier,
|
||||
assetIdentifier: NFTAsset.Identifier,
|
||||
): NFTAsset? {
|
||||
val blockchain = network.toBlockchain()
|
||||
val walletManager = getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
blockchain = blockchain,
|
||||
derivationPath = network.derivationPath.value,
|
||||
) ?: return null
|
||||
return walletManager.getAsset(collectionIdentifier, assetIdentifier)
|
||||
}
|
||||
|
||||
override suspend fun getNFTSalePrice(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
collectionIdentifier: NFTCollection.Identifier,
|
||||
assetIdentifier: NFTAsset.Identifier,
|
||||
): NFTAsset.SalePrice? {
|
||||
val blockchain = network.toBlockchain()
|
||||
val walletManager = getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
blockchain = blockchain,
|
||||
derivationPath = network.derivationPath.value,
|
||||
) ?: return null
|
||||
return walletManager.getSalePrice(collectionIdentifier, assetIdentifier)
|
||||
}
|
||||
|
||||
override suspend fun getNFTExploreUrl(network: Network, assetIdentifier: NFTAsset.Identifier): String? {
|
||||
val blockchain = network.toBlockchain()
|
||||
return blockchain.getNFTExploreUrl(assetIdentifier)
|
||||
}
|
||||
|
||||
override suspend fun isAccountInitialized(userWalletId: UserWalletId, network: Network): Boolean {
|
||||
val walletManager = getOrCreateWalletManager(userWalletId = userWalletId, network = network)
|
||||
val initializableAccountWalletManger = walletManager as? InitializableAccount ?: return true
|
||||
return initializableAccountWalletManger.accountInitializationState == InitializableAccount.State.INITIALIZED
|
||||
}
|
||||
|
||||
private fun updateWalletManagerTokensIfNeeded(walletManager: WalletManager, tokens: Set<CryptoCurrency.Token>) {
|
||||
if (tokens.isEmpty()) return
|
||||
|
||||
val tokensToAdd = sdkTokenConverter
|
||||
.convertList(tokens)
|
||||
.filter { it !in walletManager.cardTokens }
|
||||
|
||||
walletManager.addTokens(tokensToAdd)
|
||||
}
|
||||
|
||||
private suspend fun readSmartContractMethods(): Map<String, SmartContractMethod> {
|
||||
return assetLoader.loadMap<SmartContractMethod>(fileName = "contract_methods")
|
||||
}
|
||||
}
|
||||
|
|
@ -1,283 +0,0 @@
|
|||
package com.tangem.domain.walletmanager
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.blockchain.blockchains.solana.RentProvider
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.common.address.Address
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.blockchain.common.pagination.Page
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.blockchain.extensions.Result
|
||||
import com.tangem.blockchain.extensions.SimpleResult
|
||||
import com.tangem.blockchain.nft.models.NFTAsset
|
||||
import com.tangem.blockchain.nft.models.NFTCollection
|
||||
import com.tangem.blockchainsdk.models.UpdateWalletManagerResult
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.network.TxInfo
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.transaction.models.AssetRequirementsCondition
|
||||
import com.tangem.domain.txhistory.models.PaginationWrapper
|
||||
import com.tangem.domain.txhistory.models.TxHistoryState
|
||||
import com.tangem.domain.walletmanager.model.RentData
|
||||
import com.tangem.domain.walletmanager.model.TokenInfo
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import java.math.BigDecimal
|
||||
import java.util.EnumSet
|
||||
|
||||
// TODO: Move to its own module
|
||||
/**
|
||||
* A facade for managing wallets.
|
||||
*/
|
||||
@Suppress("TooManyFunctions")
|
||||
interface WalletManagersFacade {
|
||||
|
||||
/**
|
||||
* Updates the wallet manager associated with a user's wallet and network.
|
||||
*
|
||||
* @param userWalletId The ID of the user's wallet.
|
||||
* @param network The network.
|
||||
* @param extraTokens Additional tokens.
|
||||
* @return The result of updating the wallet manager.
|
||||
*/
|
||||
suspend fun update(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
extraTokens: Set<CryptoCurrency.Token>,
|
||||
): UpdateWalletManagerResult
|
||||
|
||||
/**
|
||||
* Removes the wallet managers associated with a user's wallet and networks.
|
||||
*
|
||||
* @param userWalletId The ID of the user's wallet.
|
||||
* @param networks Set of networks
|
||||
* */
|
||||
suspend fun remove(userWalletId: UserWalletId, networks: Set<Network>)
|
||||
|
||||
suspend fun removeTokens(userWalletId: UserWalletId, tokens: Set<CryptoCurrency.Token>)
|
||||
|
||||
suspend fun removeTokensByTokenInfo(userWalletId: UserWalletId, tokenInfos: Set<TokenInfo>)
|
||||
|
||||
/**
|
||||
* Returns [UpdateWalletManagerResult] with last pending transactions
|
||||
*
|
||||
* @param userWalletId The ID of the user's wallet.
|
||||
* @param network The network.
|
||||
* @return The result of updating the wallet manager.
|
||||
*/
|
||||
suspend fun updatePendingTransactions(userWalletId: UserWalletId, network: Network): UpdateWalletManagerResult
|
||||
|
||||
/**
|
||||
* Returns network explorer URL of the wallet manager associated with a user's wallet and network.
|
||||
*
|
||||
* @param userWalletId The ID of the user's wallet.
|
||||
* @param network The network.
|
||||
* @param addressType Address type.
|
||||
* @param contractAddress Contract address if currency is Token.
|
||||
*
|
||||
* @return The network explorer URL, maybe empty if the wallet manager was not found.
|
||||
* */
|
||||
suspend fun getExploreUrl(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
addressType: AddressType,
|
||||
contractAddress: String?,
|
||||
): String
|
||||
|
||||
/**
|
||||
* Returns transactions count
|
||||
*
|
||||
* @param userWalletId The ID of the user's wallet.
|
||||
* @param currency currency.
|
||||
*/
|
||||
suspend fun getTxHistoryState(userWalletId: UserWalletId, currency: CryptoCurrency): TxHistoryState
|
||||
|
||||
/**
|
||||
* Returns transaction history items wrapped to pagination
|
||||
*
|
||||
* @param userWalletId The ID of the user's wallet.
|
||||
* @param currency currency.
|
||||
* @param page Pagination page.
|
||||
* @param pageSize Pagination size.
|
||||
*/
|
||||
suspend fun getTxHistoryItems(
|
||||
userWalletId: UserWalletId,
|
||||
currency: CryptoCurrency,
|
||||
page: Page,
|
||||
pageSize: Int,
|
||||
): PaginationWrapper<TxInfo>
|
||||
|
||||
@Deprecated("Will be removed in future")
|
||||
suspend fun getOrCreateWalletManager(
|
||||
userWalletId: UserWalletId,
|
||||
blockchain: Blockchain,
|
||||
derivationPath: String?,
|
||||
): WalletManager?
|
||||
|
||||
@Deprecated("Will be removed in future")
|
||||
suspend fun getOrCreateWalletManager(userWalletId: UserWalletId, network: Network): WalletManager?
|
||||
|
||||
@Deprecated("Will be removed in future")
|
||||
suspend fun getStoredWalletManagers(userWalletId: UserWalletId): List<WalletManager>
|
||||
|
||||
/**
|
||||
* Returns default network address for selected wallet in given network
|
||||
*
|
||||
* @param userWalletId selected wallet id
|
||||
* @param network network of currency
|
||||
*/
|
||||
suspend fun getDefaultAddress(userWalletId: UserWalletId, network: Network): String?
|
||||
|
||||
/** Returns list of all addresses for all currencies in selected wallet
|
||||
*
|
||||
* @param userWalletId selected wallet id
|
||||
* @param network required to create wallet manager
|
||||
*/
|
||||
suspend fun getAddresses(userWalletId: UserWalletId, network: Network): Set<Address>
|
||||
|
||||
/**
|
||||
* Returns info about rent if wallet manager implemented [RentProvider], otherwise null
|
||||
*
|
||||
* @param userWalletId selected wallet id
|
||||
* @param network network of currency
|
||||
*/
|
||||
suspend fun getRentInfo(userWalletId: UserWalletId, network: Network): RentData?
|
||||
|
||||
@Deprecated("Will be removed in future")
|
||||
fun getAll(userWalletId: UserWalletId): Flow<List<WalletManager>>
|
||||
|
||||
suspend fun validateSignatureCount(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
signedHashes: Int,
|
||||
): Either<Throwable, Unit>
|
||||
|
||||
/**
|
||||
* Returns fee for transaction
|
||||
*
|
||||
* @param amount of transaction
|
||||
* @param destination address
|
||||
* @param userWalletId selected wallet id
|
||||
* @param network network of currency
|
||||
*/
|
||||
@Deprecated("Will be removed in future")
|
||||
suspend fun getFee(
|
||||
amount: Amount,
|
||||
destination: String,
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
): Result<TransactionFee>?
|
||||
|
||||
/**
|
||||
* Returns estimated fee for transaction
|
||||
*
|
||||
* @param amount of transaction
|
||||
* @param userWalletId selected wallet id
|
||||
* @param network network of currency
|
||||
*/
|
||||
suspend fun estimateFee(amount: Amount, userWalletId: UserWalletId, network: Network): Result<TransactionFee>?
|
||||
|
||||
/**
|
||||
* Validates transaction
|
||||
*
|
||||
* @param amount of transaction
|
||||
* @param fee of transaction
|
||||
* @param userWalletId selected wallet id
|
||||
* @param network network of currency
|
||||
*/
|
||||
@Deprecated("Will be removed in future")
|
||||
suspend fun validateTransaction(
|
||||
amount: Amount,
|
||||
fee: Amount?,
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
): EnumSet<TransactionError>?
|
||||
|
||||
/**
|
||||
* Creates transaction [TransactionData]
|
||||
*
|
||||
* @param amount of transaction
|
||||
* @param fee of transaction
|
||||
* @param memo of transaction optional
|
||||
* @param destination address
|
||||
* @param userWalletId selected wallet id
|
||||
* @param network network of currency
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
@Deprecated("Will be removed in future")
|
||||
suspend fun createTransaction(
|
||||
amount: Amount,
|
||||
fee: Fee,
|
||||
memo: String?,
|
||||
destination: String,
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
): TransactionData?
|
||||
|
||||
/** Get recent transactions of [userWalletId] for [currency] */
|
||||
suspend fun getRecentTransactions(userWalletId: UserWalletId, currency: CryptoCurrency): List<TxInfo>
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
suspend fun tokenBalance(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
name: String,
|
||||
symbol: String,
|
||||
contractAddress: String,
|
||||
decimals: Int,
|
||||
id: String? = null,
|
||||
): BigDecimal
|
||||
|
||||
/**
|
||||
* Get requirements for asset(currency)
|
||||
* @return null if there's no requirement, otherwise [AssetRequirementsCondition].
|
||||
*/
|
||||
suspend fun getAssetRequirements(userWalletId: UserWalletId, currency: CryptoCurrency): AssetRequirementsCondition?
|
||||
|
||||
suspend fun fulfillRequirements(
|
||||
userWalletId: UserWalletId,
|
||||
currency: CryptoCurrency,
|
||||
signer: TransactionSigner,
|
||||
): SimpleResult
|
||||
|
||||
suspend fun discardRequirements(userWalletId: UserWalletId, currency: CryptoCurrency): SimpleResult
|
||||
|
||||
/**
|
||||
* Indicates UTXO consolidation availability
|
||||
*
|
||||
* @param userWalletId selected user wallet
|
||||
* @param network availability for network
|
||||
*/
|
||||
suspend fun checkUtxoConsolidationAvailability(userWalletId: UserWalletId, network: Network): Boolean
|
||||
|
||||
suspend fun getNFTCollections(userWalletId: UserWalletId, network: Network): List<NFTCollection>
|
||||
|
||||
suspend fun getNFTAssets(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
collectionIdentifier: NFTCollection.Identifier,
|
||||
): List<NFTAsset>
|
||||
|
||||
suspend fun getNFTAsset(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
collectionIdentifier: NFTCollection.Identifier,
|
||||
assetIdentifier: NFTAsset.Identifier,
|
||||
): NFTAsset?
|
||||
|
||||
suspend fun getNFTSalePrice(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
collectionIdentifier: NFTCollection.Identifier,
|
||||
assetIdentifier: NFTAsset.Identifier,
|
||||
): NFTAsset.SalePrice?
|
||||
|
||||
suspend fun getNFTExploreUrl(network: Network, assetIdentifier: NFTAsset.Identifier): String?
|
||||
|
||||
/**
|
||||
* If wallet manager implements [InitializableAccount] then returns [InitializableAccount.isAccountInitialized]
|
||||
* value. Otherwise always return true
|
||||
*/
|
||||
suspend fun isAccountInitialized(userWalletId: UserWalletId, network: Network): Boolean
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
package com.tangem.domain.walletmanager.model
|
||||
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* Represents wallet blockchain rent
|
||||
* @param rent Amount that will be charged in overtime if the blockchain does not have an amount greater than
|
||||
* the [exemptionAmount]
|
||||
* @param exemptionAmount Amount that should be on the blockchain balance not to pay rent
|
||||
*/
|
||||
data class RentData(val rent: BigDecimal, val exemptionAmount: BigDecimal)
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
package com.tangem.domain.walletmanager.model
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class SmartContractMethod(
|
||||
@Json(name = "info") val info: String?,
|
||||
@Json(name = "source") val source: String?,
|
||||
@Json(name = "name") val name: String,
|
||||
)
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
package com.tangem.domain.walletmanager.model
|
||||
|
||||
import com.tangem.domain.models.network.Network
|
||||
|
||||
data class TokenInfo(
|
||||
val network: Network,
|
||||
val name: String,
|
||||
val symbol: String,
|
||||
val contractAddress: String,
|
||||
val decimals: Int,
|
||||
val id: String? = null,
|
||||
)
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
package com.tangem.domain.walletmanager.utils
|
||||
|
||||
import com.tangem.blockchain.common.CryptoCurrencyType
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal class CryptoCurrencyTypeConverter : Converter<CryptoCurrency, CryptoCurrencyType> {
|
||||
override fun convert(value: CryptoCurrency): CryptoCurrencyType {
|
||||
return when (value) {
|
||||
is CryptoCurrency.Coin -> CryptoCurrencyType.Coin
|
||||
is CryptoCurrency.Token -> CryptoCurrencyType.Token(
|
||||
info = Token(
|
||||
name = value.name,
|
||||
symbol = value.symbol,
|
||||
contractAddress = value.contractAddress,
|
||||
decimals = value.decimals,
|
||||
id = value.id.rawCurrencyId?.value,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
package com.tangem.domain.walletmanager.utils
|
||||
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.blockchainsdk.models.UpdateWalletManagerResult.Address
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.blockchain.common.address.Address as SdkAddress
|
||||
|
||||
/**
|
||||
* Convert [SdkAddress] to [Address]
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal object SdkAddressToAddressConverter : Converter<SdkAddress, Address> {
|
||||
|
||||
override fun convert(value: SdkAddress): Address {
|
||||
return Address(
|
||||
value = value.value,
|
||||
type = when (value.type) {
|
||||
AddressType.Default -> Address.Type.Primary
|
||||
AddressType.Legacy -> Address.Type.Secondary
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
package com.tangem.domain.walletmanager.utils
|
||||
|
||||
import com.tangem.domain.txhistory.models.Page
|
||||
import com.tangem.utils.converter.TwoWayConverter
|
||||
import com.tangem.blockchain.common.pagination.Page as SdkPage
|
||||
|
||||
class SdkPageConverter : TwoWayConverter<SdkPage, Page> {
|
||||
override fun convert(value: SdkPage): Page {
|
||||
return when (value) {
|
||||
is SdkPage.Initial -> Page.Initial
|
||||
is SdkPage.LastPage -> Page.LastPage
|
||||
is SdkPage.Next -> Page.Next(value = value.value)
|
||||
}
|
||||
}
|
||||
|
||||
override fun convertBack(value: Page): SdkPage {
|
||||
return when (value) {
|
||||
is Page.Initial -> SdkPage.Initial
|
||||
is Page.LastPage -> SdkPage.LastPage
|
||||
is Page.Next -> SdkPage.Next(value = value.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
package com.tangem.domain.walletmanager.utils
|
||||
|
||||
import com.tangem.domain.transaction.models.AssetRequirementsCondition
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.blockchain.common.trustlines.AssetRequirementsCondition as SdkRequirementsCondition
|
||||
|
||||
internal class SdkRequirementsConditionConverter : Converter<SdkRequirementsCondition, AssetRequirementsCondition> {
|
||||
override fun convert(value: SdkRequirementsCondition): AssetRequirementsCondition {
|
||||
return when (value) {
|
||||
is SdkRequirementsCondition.PaidTransaction -> AssetRequirementsCondition.PaidTransaction
|
||||
is SdkRequirementsCondition.RequiredTrustline -> AssetRequirementsCondition.RequiredTrustline(
|
||||
requiredAmount = requireNotNull(value.amount.value),
|
||||
currencySymbol = value.amount.currencySymbol,
|
||||
decimals = value.amount.decimals,
|
||||
)
|
||||
is SdkRequirementsCondition.PaidTransactionWithFee -> AssetRequirementsCondition.PaidTransactionWithFee(
|
||||
feeAmount = requireNotNull(value.feeAmount.value),
|
||||
feeCurrencySymbol = value.feeAmount.currencySymbol,
|
||||
decimals = value.feeAmount.decimals,
|
||||
)
|
||||
is SdkRequirementsCondition.IncompleteTransaction -> AssetRequirementsCondition.IncompleteTransaction(
|
||||
amount = requireNotNull(value.amount.value),
|
||||
currencySymbol = value.amount.currencySymbol,
|
||||
currencyDecimals = value.amount.decimals,
|
||||
feeAmount = requireNotNull(value.feeAmount.value),
|
||||
feeCurrencySymbol = value.feeAmount.currencySymbol,
|
||||
feeCurrencyDecimals = value.feeAmount.decimals,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package com.tangem.domain.walletmanager.utils
|
||||
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.blockchain.common.Token as SdkToken
|
||||
|
||||
internal class SdkTokenConverter : Converter<CryptoCurrency.Token, SdkToken> {
|
||||
|
||||
override fun convert(value: CryptoCurrency.Token): SdkToken {
|
||||
return SdkToken(
|
||||
id = value.id.rawCurrencyId?.value,
|
||||
name = value.name,
|
||||
symbol = value.symbol,
|
||||
contractAddress = value.contractAddress,
|
||||
decimals = value.decimals,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
package com.tangem.domain.walletmanager.utils
|
||||
|
||||
import com.tangem.blockchain.transactionhistory.models.TransactionHistoryItem
|
||||
import com.tangem.domain.models.network.TxInfo
|
||||
import com.tangem.domain.walletmanager.model.SmartContractMethod
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.blockchain.transactionhistory.models.TransactionHistoryItem as SdkTransactionHistoryItem
|
||||
|
||||
internal class SdkTransactionHistoryItemConverter(
|
||||
smartContractMethods: Map<String, SmartContractMethod>,
|
||||
) : Converter<SdkTransactionHistoryItem, TxInfo> {
|
||||
|
||||
private val typeConverter by lazy { SdkTransactionTypeConverter(smartContractMethods) }
|
||||
|
||||
override fun convert(value: SdkTransactionHistoryItem): TxInfo = TxInfo(
|
||||
txHash = value.txHash,
|
||||
timestampInMillis = value.timestamp,
|
||||
isOutgoing = value.isOutgoing,
|
||||
destinationType = value.destinationType.toDomain(),
|
||||
sourceType = value.sourceType.toDomain(),
|
||||
interactionAddressType = value.extractInteractionAddressType(),
|
||||
status = when (value.status) {
|
||||
SdkTransactionHistoryItem.TransactionStatus.Confirmed -> TxInfo.TransactionStatus.Confirmed
|
||||
SdkTransactionHistoryItem.TransactionStatus.Failed -> TxInfo.TransactionStatus.Failed
|
||||
SdkTransactionHistoryItem.TransactionStatus.Unconfirmed -> TxInfo.TransactionStatus.Unconfirmed
|
||||
},
|
||||
type = typeConverter.convert(value.type),
|
||||
amount = requireNotNull(value.amount.value) { "Transaction amount value must not be null" },
|
||||
)
|
||||
|
||||
private fun SdkTransactionHistoryItem.SourceType.toDomain(): TxInfo.SourceType = when (this) {
|
||||
is TransactionHistoryItem.SourceType.Single -> TxInfo.SourceType.Single(address)
|
||||
is TransactionHistoryItem.SourceType.Multiple -> TxInfo.SourceType.Multiple(addresses)
|
||||
}
|
||||
|
||||
private fun SdkTransactionHistoryItem.DestinationType.toDomain(): TxInfo.DestinationType = when (this) {
|
||||
is SdkTransactionHistoryItem.DestinationType.Single -> TxInfo.DestinationType.Single(
|
||||
addressType.toDomain(),
|
||||
)
|
||||
is SdkTransactionHistoryItem.DestinationType.Multiple -> TxInfo.DestinationType.Multiple(
|
||||
addressTypes.map { it.toDomain() },
|
||||
)
|
||||
}
|
||||
|
||||
private fun SdkTransactionHistoryItem.AddressType.toDomain(): TxInfo.AddressType = when (this) {
|
||||
is SdkTransactionHistoryItem.AddressType.Contract -> TxInfo.AddressType.Contract(address)
|
||||
is SdkTransactionHistoryItem.AddressType.User -> TxInfo.AddressType.User(address)
|
||||
is SdkTransactionHistoryItem.AddressType.Validator -> TxInfo.AddressType.Validator(address)
|
||||
}
|
||||
|
||||
private fun SdkTransactionHistoryItem.extractInteractionAddressType(): TxInfo.InteractionAddressType? {
|
||||
return when (val transactionType = type) {
|
||||
SdkTransactionHistoryItem.TransactionType.Transfer -> if (isOutgoing) {
|
||||
mapToInteractionAddressType(destinationType = destinationType)
|
||||
} else {
|
||||
mapToInteractionAddressType(sourceType = sourceType)
|
||||
}
|
||||
|
||||
is SdkTransactionHistoryItem.TransactionType.ContractMethod,
|
||||
is SdkTransactionHistoryItem.TransactionType.ContractMethodName,
|
||||
-> mapToInteractionAddressType(destinationType = destinationType)
|
||||
|
||||
is SdkTransactionHistoryItem.TransactionType.TronStakingTransactionType.VoteWitnessContract -> {
|
||||
TxInfo.InteractionAddressType.Validator(address = transactionType.validatorAddress)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun mapToInteractionAddressType(
|
||||
destinationType: SdkTransactionHistoryItem.DestinationType,
|
||||
): TxInfo.InteractionAddressType {
|
||||
return when (destinationType) {
|
||||
is TransactionHistoryItem.DestinationType.Multiple -> TxInfo.InteractionAddressType.Multiple(
|
||||
destinationType.addressTypes.map { it.address },
|
||||
)
|
||||
is TransactionHistoryItem.DestinationType.Single -> when (destinationType.addressType) {
|
||||
is TransactionHistoryItem.AddressType.Contract -> TxInfo.InteractionAddressType.Contract(
|
||||
destinationType.addressType.address,
|
||||
)
|
||||
is TransactionHistoryItem.AddressType.User -> TxInfo.InteractionAddressType.User(
|
||||
destinationType.addressType.address,
|
||||
)
|
||||
is TransactionHistoryItem.AddressType.Validator -> TxInfo.InteractionAddressType.Validator(
|
||||
destinationType.addressType.address,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun mapToInteractionAddressType(
|
||||
sourceType: SdkTransactionHistoryItem.SourceType,
|
||||
): TxInfo.InteractionAddressType {
|
||||
return when (sourceType) {
|
||||
is TransactionHistoryItem.SourceType.Multiple -> TxInfo.InteractionAddressType.Multiple(
|
||||
sourceType.addresses,
|
||||
)
|
||||
is TransactionHistoryItem.SourceType.Single -> {
|
||||
TxInfo.InteractionAddressType.User(sourceType.address)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
package com.tangem.domain.walletmanager.utils
|
||||
|
||||
import com.tangem.blockchain.transactionhistory.TransactionHistoryState
|
||||
import com.tangem.domain.txhistory.models.TxHistoryState
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.blockchain.transactionhistory.TransactionHistoryState as SdkTransactionHistoryState
|
||||
|
||||
internal class SdkTransactionHistoryStateConverter : Converter<SdkTransactionHistoryState, TxHistoryState> {
|
||||
|
||||
override fun convert(value: TransactionHistoryState): TxHistoryState = when (value) {
|
||||
is TransactionHistoryState.Success.Empty -> TxHistoryState.Success.Empty
|
||||
is TransactionHistoryState.Success.HasTransactions -> TxHistoryState.Success.HasTransactions(value.txCount)
|
||||
is TransactionHistoryState.Failed.FetchError -> TxHistoryState.Failed.FetchError(value.exception)
|
||||
is TransactionHistoryState.NotImplemented -> TxHistoryState.NotImplemented
|
||||
}
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
package com.tangem.domain.walletmanager.utils
|
||||
|
||||
import com.tangem.blockchain.transactionhistory.models.TransactionHistoryItem.TransactionType
|
||||
import com.tangem.domain.models.network.TxInfo
|
||||
import com.tangem.domain.walletmanager.model.SmartContractMethod
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal class SdkTransactionTypeConverter(
|
||||
private val smartContractMethods: Map<String, SmartContractMethod>,
|
||||
) : Converter<TransactionType, TxInfo.TransactionType> {
|
||||
|
||||
override fun convert(value: TransactionType): TxInfo.TransactionType {
|
||||
return when (value) {
|
||||
is TransactionType.ContractMethod -> {
|
||||
getTransactionType(methodName = smartContractMethods[value.id]?.name)
|
||||
}
|
||||
is TransactionType.ContractMethodName -> {
|
||||
getTransactionType(methodName = value.name)
|
||||
}
|
||||
is TransactionType.Transfer -> {
|
||||
TxInfo.TransactionType.Transfer
|
||||
}
|
||||
is TransactionType.TronStakingTransactionType.FreezeBalanceV2Contract -> {
|
||||
TxInfo.TransactionType.Staking.Stake
|
||||
}
|
||||
is TransactionType.TronStakingTransactionType.UnfreezeBalanceV2Contract -> {
|
||||
TxInfo.TransactionType.Staking.Unstake
|
||||
}
|
||||
is TransactionType.TronStakingTransactionType.VoteWitnessContract -> {
|
||||
TxInfo.TransactionType.Staking.Vote(value.validatorAddress)
|
||||
}
|
||||
is TransactionType.TronStakingTransactionType.WithdrawBalanceContract -> {
|
||||
TxInfo.TransactionType.Staking.ClaimRewards
|
||||
}
|
||||
is TransactionType.TronStakingTransactionType.WithdrawExpireUnfreezeContract -> {
|
||||
TxInfo.TransactionType.Staking.Withdraw
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getTransactionType(methodName: String?): TxInfo.TransactionType {
|
||||
return when (methodName) {
|
||||
"transfer" -> TxInfo.TransactionType.Transfer
|
||||
"approve" -> TxInfo.TransactionType.Approve
|
||||
"swap" -> TxInfo.TransactionType.Swap
|
||||
"buyVoucher",
|
||||
"buyVoucherPOL",
|
||||
"delegate",
|
||||
-> TxInfo.TransactionType.Staking.Stake
|
||||
"sellVoucher_new",
|
||||
"sellVoucher_newPOL",
|
||||
"undelegate",
|
||||
-> TxInfo.TransactionType.Staking.Unstake
|
||||
"unstakeClaimTokens_new",
|
||||
"unstakeClaimTokens_newPOL",
|
||||
"claim",
|
||||
-> TxInfo.TransactionType.Staking.Withdraw
|
||||
"withdrawRewards",
|
||||
"withdrawRewardsPOL",
|
||||
-> TxInfo.TransactionType.Staking.ClaimRewards
|
||||
"redelegate" -> TxInfo.TransactionType.Staking.Restake
|
||||
null -> TxInfo.TransactionType.UnknownOperation
|
||||
else -> TxInfo.TransactionType.Operation(name = methodName.replaceFirstChar { it.titlecase() })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
package com.tangem.domain.walletmanager.utils
|
||||
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchainsdk.models.UpdateWalletManagerResult.Address
|
||||
import com.tangem.domain.models.network.TxInfo
|
||||
import com.tangem.utils.converter.Converter
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* Convert [TransactionData] to [TxInfo]
|
||||
*
|
||||
* @property walletAddresses wallet addresses
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class TransactionDataToTxHistoryItemConverter(
|
||||
private val walletAddresses: Set<Address>,
|
||||
private val feePaidCurrency: FeePaidCurrency,
|
||||
) : Converter<TransactionData.Uncompiled, TxInfo?> {
|
||||
|
||||
override fun convert(value: TransactionData.Uncompiled): TxInfo? {
|
||||
val hash = value.hash ?: return null
|
||||
val millis = value.date?.timeInMillis ?: return null
|
||||
val amount = getTransactionAmountValue(value.amount, value.fee?.amount) ?: return null
|
||||
val isOutgoing = value.sourceAddress in walletAddresses.map(Address::value)
|
||||
|
||||
return TxInfo(
|
||||
txHash = hash,
|
||||
timestampInMillis = millis,
|
||||
isOutgoing = isOutgoing,
|
||||
destinationType = TxInfo.DestinationType.Single(
|
||||
addressType = TxInfo.AddressType.User(value.destinationAddress),
|
||||
),
|
||||
sourceType = TxInfo.SourceType.Single(value.sourceAddress),
|
||||
interactionAddressType = TxInfo.InteractionAddressType.User(
|
||||
address = if (isOutgoing) value.destinationAddress else value.sourceAddress,
|
||||
),
|
||||
status = when (value.status) {
|
||||
TransactionStatus.Confirmed -> TxInfo.TransactionStatus.Confirmed
|
||||
TransactionStatus.Unconfirmed -> TxInfo.TransactionStatus.Unconfirmed
|
||||
},
|
||||
type = TxInfo.TransactionType.Transfer,
|
||||
amount = amount,
|
||||
)
|
||||
}
|
||||
|
||||
private fun getTransactionAmountValue(amount: Amount, feeAmount: Amount?): BigDecimal? {
|
||||
val feeValue = feeAmount?.value ?: BigDecimal.ZERO
|
||||
val value = amount.value
|
||||
|
||||
if (value == null) {
|
||||
Timber.w("Transaction amount must not be null: ${amount.currencySymbol}")
|
||||
}
|
||||
|
||||
return when (feePaidCurrency) {
|
||||
FeePaidCurrency.SameCurrency -> value?.plus(feeValue)
|
||||
FeePaidCurrency.Coin -> {
|
||||
if (amount.type is AmountType.Coin) value?.plus(feeValue) else value
|
||||
}
|
||||
is FeePaidCurrency.Token -> {
|
||||
val token = (amount.type as? AmountType.Token)?.token ?: return value
|
||||
if (isSameToken(token, feePaidCurrency.token)) {
|
||||
value?.plus(feeValue)
|
||||
} else {
|
||||
value
|
||||
}
|
||||
}
|
||||
is FeePaidCurrency.FeeResource -> value
|
||||
}
|
||||
}
|
||||
|
||||
private fun isSameToken(amountToken: Token, feeToken: Token): Boolean {
|
||||
return amountToken.contractAddress.equals(feeToken.contractAddress, ignoreCase = true) &&
|
||||
amountToken.symbol.equals(feeToken.symbol, ignoreCase = true)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,185 +0,0 @@
|
|||
package com.tangem.domain.walletmanager.utils
|
||||
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchainsdk.models.UpdateWalletManagerResult
|
||||
import com.tangem.blockchainsdk.models.UpdateWalletManagerResult.*
|
||||
import com.tangem.blockchainsdk.utils.amountToCreateAccount
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
import com.tangem.blockchain.common.address.Address as SdkAddress
|
||||
|
||||
/** Factory for creating [UpdateWalletManagerResult] */
|
||||
internal class UpdateWalletManagerResultFactory {
|
||||
|
||||
/** Get [Verified] result for [walletManager] */
|
||||
fun getResult(walletManager: WalletManager): Verified {
|
||||
val wallet = walletManager.wallet
|
||||
val addresses = getAvailableAddresses(wallet.addresses)
|
||||
val feePaidCurrency = wallet.blockchain.feePaidCurrency()
|
||||
val txHistoryItemConverter = TransactionDataToTxHistoryItemConverter(addresses, feePaidCurrency)
|
||||
|
||||
return Verified(
|
||||
selectedAddress = wallet.address,
|
||||
addresses = addresses,
|
||||
currenciesAmounts = getTokensAmounts(wallet.amounts.values.toSet()),
|
||||
currentTransactions = getCurrentTransactions(txHistoryItemConverter, wallet.recentTransactions.toSet()),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get demo [Verified] result
|
||||
*
|
||||
* @param walletManager wallet manager
|
||||
* @param demoAmount amount that will be used for demo result
|
||||
*/
|
||||
fun getDemoResult(walletManager: WalletManager, demoAmount: Amount): Verified {
|
||||
val wallet = walletManager.wallet
|
||||
val addresses = getAvailableAddresses(wallet.addresses)
|
||||
val feePaidCurrency = wallet.blockchain.feePaidCurrency()
|
||||
val txHistoryItemConverter = TransactionDataToTxHistoryItemConverter(addresses, feePaidCurrency)
|
||||
|
||||
return Verified(
|
||||
selectedAddress = wallet.address,
|
||||
addresses = addresses,
|
||||
currenciesAmounts = getDemoTokensAmounts(demoAmount, walletManager.cardTokens),
|
||||
currentTransactions = getCurrentTransactions(txHistoryItemConverter, wallet.recentTransactions.toSet()),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get [NoAccount] result.
|
||||
* If unable to get required amount for creating account, [Unreachable] result will be returned.
|
||||
*
|
||||
* @param walletManager wallet manager
|
||||
* @param customMessage custom error message
|
||||
* @param amountToCreateAccount amount to create account
|
||||
*/
|
||||
fun getNoAccountResult(
|
||||
walletManager: WalletManager,
|
||||
customMessage: String,
|
||||
amountToCreateAccount: BigDecimal?,
|
||||
): UpdateWalletManagerResult {
|
||||
val wallet = walletManager.wallet
|
||||
val blockchain = wallet.blockchain
|
||||
val firstWalletToken = wallet.getTokens().firstOrNull()
|
||||
val amount = amountToCreateAccount ?: blockchain.amountToCreateAccount(walletManager, firstWalletToken)
|
||||
|
||||
return if (amount == null) {
|
||||
Timber.w("Unable to get required amount to create account for: $blockchain")
|
||||
Unreachable(
|
||||
selectedAddress = wallet.address,
|
||||
addresses = getAvailableAddresses(wallet.addresses),
|
||||
)
|
||||
} else {
|
||||
NoAccount(
|
||||
selectedAddress = wallet.address,
|
||||
addresses = getAvailableAddresses(wallet.addresses),
|
||||
amountToCreateAccount = amount,
|
||||
errorMessage = customMessage,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Get [Unreachable] result for [walletManager] */
|
||||
fun getUnreachableResult(walletManager: WalletManager): Unreachable {
|
||||
val wallet = walletManager.wallet
|
||||
|
||||
return Unreachable(
|
||||
selectedAddress = wallet.address,
|
||||
addresses = getAvailableAddresses(wallet.addresses),
|
||||
)
|
||||
}
|
||||
|
||||
private fun getAvailableAddresses(addresses: Set<SdkAddress>): Set<Address> {
|
||||
return SdkAddressToAddressConverter.convertList(addresses).toSet()
|
||||
}
|
||||
|
||||
private fun getTokensAmounts(amounts: Set<Amount>): Set<CryptoCurrencyAmount> {
|
||||
return amounts.mapNotNullTo(hashSetOf(), ::createCurrencyAmount)
|
||||
}
|
||||
|
||||
private fun createCurrencyAmount(amount: Amount): CryptoCurrencyAmount? {
|
||||
return when (val type = amount.type) {
|
||||
is AmountType.Token -> {
|
||||
val value = getCurrencyAmountValue(amount) ?: return null
|
||||
|
||||
CryptoCurrencyAmount.Token(
|
||||
currencyRawId = type.token.id?.let(CryptoCurrency::RawID),
|
||||
contractAddress = type.token.contractAddress,
|
||||
value = value,
|
||||
)
|
||||
}
|
||||
is AmountType.Coin -> {
|
||||
val value = getCurrencyAmountValue(amount) ?: return null
|
||||
|
||||
CryptoCurrencyAmount.Coin(value = value)
|
||||
}
|
||||
is AmountType.FeeResource,
|
||||
is AmountType.Reserve,
|
||||
-> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCurrencyAmountValue(amount: Amount): BigDecimal? {
|
||||
val value = amount.value
|
||||
|
||||
if (value == null) {
|
||||
Timber.w("Currency amount must not be null: ${amount.currencySymbol}")
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
private fun getDemoTokensAmounts(demoAmount: Amount, tokens: Set<Token>): Set<CryptoCurrencyAmount> {
|
||||
val amountValue = demoAmount.value ?: BigDecimal.ZERO
|
||||
val demoAmounts = hashSetOf<CryptoCurrencyAmount>(CryptoCurrencyAmount.Coin(amountValue))
|
||||
|
||||
return tokens.mapTo(demoAmounts) { token ->
|
||||
CryptoCurrencyAmount.Token(
|
||||
currencyRawId = token.id?.let(CryptoCurrency::RawID),
|
||||
contractAddress = token.contractAddress,
|
||||
value = amountValue,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCurrentTransactions(
|
||||
txHistoryItemConverter: TransactionDataToTxHistoryItemConverter,
|
||||
recentTransactions: Set<TransactionData.Uncompiled>,
|
||||
): Set<CryptoCurrencyTransaction> {
|
||||
val unconfirmedTransactions = recentTransactions.filter { it.status == TransactionStatus.Unconfirmed }
|
||||
|
||||
return unconfirmedTransactions.mapNotNullTo(hashSetOf()) {
|
||||
createCurrencyTransaction(
|
||||
txHistoryItemConverter = txHistoryItemConverter,
|
||||
data = it,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createCurrencyTransaction(
|
||||
txHistoryItemConverter: TransactionDataToTxHistoryItemConverter,
|
||||
data: TransactionData.Uncompiled,
|
||||
): CryptoCurrencyTransaction? {
|
||||
return when (val type = data.amount.type) {
|
||||
is AmountType.Coin -> {
|
||||
val txHistoryItem = txHistoryItemConverter.convert(data) ?: return null
|
||||
|
||||
CryptoCurrencyTransaction.Coin(txInfo = txHistoryItem)
|
||||
}
|
||||
is AmountType.Token -> {
|
||||
val txHistoryItem = txHistoryItemConverter.convert(data) ?: return null
|
||||
|
||||
CryptoCurrencyTransaction.Token(
|
||||
tokenId = type.token.id,
|
||||
contractAddress = type.token.contractAddress,
|
||||
txInfo = txHistoryItem,
|
||||
)
|
||||
}
|
||||
is AmountType.FeeResource,
|
||||
is AmountType.Reserve,
|
||||
-> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
package com.tangem.domain.walletmanager.utils
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.DerivationParams
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.blockchainsdk.BlockchainSDKFactory
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.domain.common.DerivationStyleProvider
|
||||
import com.tangem.domain.common.extensions.makePublicKey
|
||||
import com.tangem.domain.common.extensions.makeWalletManagerForApp
|
||||
import com.tangem.domain.common.util.derivationStyleProvider
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import timber.log.Timber
|
||||
|
||||
internal class WalletManagerFactory(
|
||||
private val blockchainSDKFactory: BlockchainSDKFactory,
|
||||
) {
|
||||
|
||||
suspend fun createWalletManager(
|
||||
scanResponse: ScanResponse,
|
||||
blockchain: Blockchain,
|
||||
derivationPath: DerivationPath?,
|
||||
): WalletManager? {
|
||||
val derivationParams = getDerivationParams(derivationPath, scanResponse.derivationStyleProvider)
|
||||
|
||||
return try {
|
||||
blockchainSDKFactory.getWalletManagerFactorySync()?.makeWalletManagerForApp(
|
||||
scanResponse = scanResponse,
|
||||
blockchain = blockchain,
|
||||
derivationParams = derivationParams,
|
||||
)
|
||||
} catch (e: Throwable) {
|
||||
Timber.w(e, "Failed to create wallet manager for $blockchain")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun createWalletManagerForHot(
|
||||
hotWallet: UserWallet.Hot,
|
||||
blockchain: Blockchain,
|
||||
derivationPath: DerivationPath?,
|
||||
): WalletManager? {
|
||||
val curve = blockchain.getSupportedCurves().first()
|
||||
val selectedWallet = hotWallet.wallets.orEmpty().firstOrNull { it.curve == curve }
|
||||
?: return null
|
||||
return try {
|
||||
val factory = blockchainSDKFactory.getWalletManagerFactorySync() ?: return null
|
||||
|
||||
if (derivationPath == null) {
|
||||
factory.createLegacyWalletManager(
|
||||
blockchain = blockchain,
|
||||
walletPublicKey = selectedWallet.publicKey,
|
||||
curve = selectedWallet.curve,
|
||||
)
|
||||
} else {
|
||||
factory.createWalletManager(
|
||||
blockchain = blockchain,
|
||||
publicKey = makePublicKey(
|
||||
seedKey = selectedWallet.publicKey,
|
||||
blockchain = blockchain,
|
||||
derivationPath = derivationPath,
|
||||
derivedWalletKeys = selectedWallet.derivedKeys,
|
||||
isWallet2 = true,
|
||||
) ?: return null,
|
||||
curve = selectedWallet.curve,
|
||||
)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Timber.w(e, "Failed to create wallet manager for $blockchain")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDerivationParams(
|
||||
derivationPath: DerivationPath?,
|
||||
derivationStyleProvider: DerivationStyleProvider,
|
||||
): DerivationParams? {
|
||||
val derivationStyle = derivationStyleProvider.getDerivationStyle() ?: return null
|
||||
|
||||
return if (derivationPath == null) {
|
||||
DerivationParams.Default(derivationStyle)
|
||||
} else {
|
||||
DerivationParams.Custom(derivationPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
package com.tangem.domain.common
|
||||
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class TwinsHelperTest {
|
||||
|
||||
private val pack1Twins = listOf(
|
||||
"CB610000021",
|
||||
"CB620000031",
|
||||
)
|
||||
|
||||
private val pack2Twins = listOf(
|
||||
"CB640000012",
|
||||
"CB650000011",
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `twins compatibility pack 1 success`() {
|
||||
assertTrue(TwinsHelper.isTwinsCompatible(pack1Twins[0], pack1Twins[1]))
|
||||
assertTrue(TwinsHelper.isTwinsCompatible(pack1Twins[1], pack1Twins[0]))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `twins compatibility pack 1 same cards`() {
|
||||
assertFalse(TwinsHelper.isTwinsCompatible(pack1Twins[0], pack1Twins[0]))
|
||||
assertFalse(TwinsHelper.isTwinsCompatible(pack1Twins[1], pack1Twins[1]))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `twins compatibility pack 2 success`() {
|
||||
assertTrue(TwinsHelper.isTwinsCompatible(pack2Twins[0], pack2Twins[1]))
|
||||
assertTrue(TwinsHelper.isTwinsCompatible(pack2Twins[1], pack2Twins[0]))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `twins compatibility pack 2 same cards`() {
|
||||
assertFalse(TwinsHelper.isTwinsCompatible(pack2Twins[0], pack2Twins[0]))
|
||||
assertFalse(TwinsHelper.isTwinsCompatible(pack2Twins[1], pack2Twins[1]))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `twins compatibility false for different packs`() {
|
||||
assertFalse(TwinsHelper.isTwinsCompatible(pack2Twins[0], pack1Twins[1]))
|
||||
assertFalse(TwinsHelper.isTwinsCompatible(pack1Twins[1], pack2Twins[0]))
|
||||
assertFalse(TwinsHelper.isTwinsCompatible(pack1Twins[0], pack2Twins[1]))
|
||||
assertFalse(TwinsHelper.isTwinsCompatible(pack2Twins[1], pack1Twins[0]))
|
||||
}
|
||||
}
|
||||
|
|
@ -1,176 +0,0 @@
|
|||
package com.tangem.domain.common.configs
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class Wallet2CardConfigTest {
|
||||
|
||||
private val wallet2CardConfig = Wallet2CardConfig
|
||||
private val expectedMapping = mapOf(
|
||||
Blockchain.Unknown to null,
|
||||
Blockchain.Arbitrum to EllipticCurve.Secp256k1,
|
||||
Blockchain.ArbitrumTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Avalanche to EllipticCurve.Secp256k1,
|
||||
Blockchain.AvalancheTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Binance to EllipticCurve.Secp256k1,
|
||||
Blockchain.BinanceTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.BSC to EllipticCurve.Secp256k1,
|
||||
Blockchain.BSCTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Bitcoin to EllipticCurve.Secp256k1,
|
||||
Blockchain.BitcoinTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.BitcoinCash to EllipticCurve.Secp256k1,
|
||||
Blockchain.BitcoinCashTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Cardano to EllipticCurve.Ed25519,
|
||||
Blockchain.Cosmos to EllipticCurve.Secp256k1,
|
||||
Blockchain.CosmosTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Dogecoin to EllipticCurve.Secp256k1,
|
||||
Blockchain.Ducatus to EllipticCurve.Secp256k1,
|
||||
Blockchain.Ethereum to EllipticCurve.Secp256k1,
|
||||
Blockchain.EthereumTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.EthereumClassic to EllipticCurve.Secp256k1,
|
||||
Blockchain.EthereumClassicTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Fantom to EllipticCurve.Secp256k1,
|
||||
Blockchain.FantomTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Litecoin to EllipticCurve.Secp256k1,
|
||||
Blockchain.Near to EllipticCurve.Ed25519Slip0010,
|
||||
Blockchain.NearTestnet to EllipticCurve.Ed25519Slip0010,
|
||||
Blockchain.Polkadot to EllipticCurve.Ed25519Slip0010,
|
||||
Blockchain.PolkadotTestnet to EllipticCurve.Ed25519Slip0010,
|
||||
Blockchain.Kava to EllipticCurve.Secp256k1,
|
||||
Blockchain.KavaTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Kusama to EllipticCurve.Ed25519Slip0010,
|
||||
Blockchain.Polygon to EllipticCurve.Secp256k1,
|
||||
Blockchain.PolygonTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.RSK to EllipticCurve.Secp256k1,
|
||||
Blockchain.Sei to EllipticCurve.Secp256k1,
|
||||
Blockchain.SeiTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Stellar to EllipticCurve.Ed25519Slip0010,
|
||||
Blockchain.StellarTestnet to EllipticCurve.Ed25519Slip0010,
|
||||
Blockchain.Solana to EllipticCurve.Ed25519Slip0010,
|
||||
Blockchain.SolanaTestnet to EllipticCurve.Ed25519Slip0010,
|
||||
Blockchain.Tezos to EllipticCurve.Ed25519Slip0010,
|
||||
Blockchain.Tron to EllipticCurve.Secp256k1,
|
||||
Blockchain.TronTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.XRP to EllipticCurve.Secp256k1,
|
||||
Blockchain.Gnosis to EllipticCurve.Secp256k1,
|
||||
Blockchain.Dash to EllipticCurve.Secp256k1,
|
||||
Blockchain.Optimism to EllipticCurve.Secp256k1,
|
||||
Blockchain.OptimismTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Dischain to EllipticCurve.Secp256k1,
|
||||
Blockchain.EthereumPow to EllipticCurve.Secp256k1,
|
||||
Blockchain.EthereumPowTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Kaspa to EllipticCurve.Secp256k1,
|
||||
Blockchain.Telos to EllipticCurve.Secp256k1,
|
||||
Blockchain.TelosTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.TON to EllipticCurve.Ed25519Slip0010,
|
||||
Blockchain.TONTestnet to EllipticCurve.Ed25519Slip0010,
|
||||
Blockchain.Ravencoin to EllipticCurve.Secp256k1,
|
||||
Blockchain.RavencoinTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.TerraV1 to EllipticCurve.Secp256k1,
|
||||
Blockchain.TerraV2 to EllipticCurve.Secp256k1,
|
||||
Blockchain.Cronos to EllipticCurve.Secp256k1,
|
||||
Blockchain.AlephZero to EllipticCurve.Ed25519Slip0010,
|
||||
Blockchain.AlephZeroTestnet to EllipticCurve.Ed25519Slip0010,
|
||||
Blockchain.OctaSpace to EllipticCurve.Secp256k1,
|
||||
Blockchain.OctaSpaceTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Chia to EllipticCurve.Bls12381G2Aug,
|
||||
Blockchain.ChiaTestnet to EllipticCurve.Bls12381G2Aug,
|
||||
Blockchain.Decimal to EllipticCurve.Secp256k1,
|
||||
Blockchain.DecimalTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.XDC to EllipticCurve.Secp256k1,
|
||||
Blockchain.XDCTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.VeChain to EllipticCurve.Secp256k1,
|
||||
Blockchain.VeChainTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Aptos to EllipticCurve.Ed25519Slip0010,
|
||||
Blockchain.AptosTestnet to EllipticCurve.Ed25519Slip0010,
|
||||
Blockchain.Playa3ull to EllipticCurve.Secp256k1,
|
||||
Blockchain.Shibarium to EllipticCurve.Secp256k1,
|
||||
Blockchain.ShibariumTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Algorand to EllipticCurve.Ed25519Slip0010,
|
||||
Blockchain.AlgorandTestnet to EllipticCurve.Ed25519Slip0010,
|
||||
Blockchain.Hedera to EllipticCurve.Ed25519Slip0010,
|
||||
Blockchain.HederaTestnet to EllipticCurve.Ed25519Slip0010,
|
||||
Blockchain.Aurora to EllipticCurve.Secp256k1,
|
||||
Blockchain.AuroraTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Areon to EllipticCurve.Secp256k1,
|
||||
Blockchain.AreonTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.PulseChain to EllipticCurve.Secp256k1,
|
||||
Blockchain.PulseChainTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.ZkSyncEra to EllipticCurve.Secp256k1,
|
||||
Blockchain.ZkSyncEraTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Nexa to EllipticCurve.Secp256k1,
|
||||
Blockchain.NexaTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Moonbeam to EllipticCurve.Secp256k1,
|
||||
Blockchain.MoonbeamTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Manta to EllipticCurve.Secp256k1,
|
||||
Blockchain.MantaTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.PolygonZkEVM to EllipticCurve.Secp256k1,
|
||||
Blockchain.PolygonZkEVMTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Radiant to EllipticCurve.Secp256k1,
|
||||
Blockchain.Base to EllipticCurve.Secp256k1,
|
||||
Blockchain.BaseTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Moonriver to EllipticCurve.Secp256k1,
|
||||
Blockchain.MoonriverTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Mantle to EllipticCurve.Secp256k1,
|
||||
Blockchain.MantleTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Fact0rn to EllipticCurve.Secp256k1,
|
||||
Blockchain.Flare to EllipticCurve.Secp256k1,
|
||||
Blockchain.FlareTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Taraxa to EllipticCurve.Secp256k1,
|
||||
Blockchain.TaraxaTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Koinos to EllipticCurve.Secp256k1,
|
||||
Blockchain.KoinosTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Joystream to EllipticCurve.Ed25519Slip0010,
|
||||
Blockchain.Bittensor to EllipticCurve.Ed25519Slip0010,
|
||||
Blockchain.Filecoin to EllipticCurve.Secp256k1,
|
||||
Blockchain.Blast to EllipticCurve.Secp256k1,
|
||||
Blockchain.BlastTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Cyber to EllipticCurve.Secp256k1,
|
||||
Blockchain.CyberTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.InternetComputer to EllipticCurve.Secp256k1,
|
||||
Blockchain.Sui to EllipticCurve.Ed25519Slip0010,
|
||||
Blockchain.SuiTestnet to EllipticCurve.Ed25519Slip0010,
|
||||
Blockchain.EnergyWebChain to EllipticCurve.Secp256k1,
|
||||
Blockchain.EnergyWebChainTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.EnergyWebX to EllipticCurve.Ed25519Slip0010,
|
||||
Blockchain.EnergyWebXTestnet to EllipticCurve.Ed25519Slip0010,
|
||||
Blockchain.Casper to EllipticCurve.Secp256k1,
|
||||
Blockchain.CasperTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Core to EllipticCurve.Secp256k1,
|
||||
Blockchain.CoreTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Xodex to EllipticCurve.Secp256k1,
|
||||
Blockchain.Canxium to EllipticCurve.Secp256k1,
|
||||
Blockchain.Chiliz to EllipticCurve.Secp256k1,
|
||||
Blockchain.ChilizTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Clore to EllipticCurve.Secp256k1,
|
||||
Blockchain.VanarChain to EllipticCurve.Secp256k1,
|
||||
Blockchain.VanarChainTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.OdysseyChain to EllipticCurve.Secp256k1,
|
||||
Blockchain.OdysseyChainTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Bitrock to EllipticCurve.Secp256k1,
|
||||
Blockchain.BitrockTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Sonic to EllipticCurve.Secp256k1,
|
||||
Blockchain.SonicTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.ApeChain to EllipticCurve.Secp256k1,
|
||||
Blockchain.ApeChainTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.KaspaTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Alephium to EllipticCurve.Secp256k1,
|
||||
Blockchain.AlephiumTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Scroll to EllipticCurve.Secp256k1,
|
||||
Blockchain.ScrollTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.ZkLinkNova to EllipticCurve.Secp256k1,
|
||||
Blockchain.ZkLinkNovaTestnet to EllipticCurve.Secp256k1,
|
||||
Blockchain.Pepecoin to EllipticCurve.Secp256k1,
|
||||
Blockchain.PepecoinTestnet to EllipticCurve.Secp256k1,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun blockchainCurveMappingTest() {
|
||||
Blockchain.entries.forEach {
|
||||
val resultCurve = wallet2CardConfig.primaryCurve(it)
|
||||
assertEquals(expectedMapping[it], resultCurve)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,786 +0,0 @@
|
|||
package com.tangem.domain.walletmanager.utils
|
||||
|
||||
import com.google.common.truth.Truth
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.common.address.Address
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.blockchainsdk.models.UpdateWalletManagerResult
|
||||
import com.tangem.blockchainsdk.models.UpdateWalletManagerResult.*
|
||||
import com.tangem.blockchainsdk.models.UpdateWalletManagerResult.Address.Type
|
||||
import com.tangem.common.test.domain.walletmanager.MockUpdateWalletManagerResultFactory
|
||||
import com.tangem.common.test.utils.ProvideTestModels
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.TxInfo
|
||||
import io.mockk.clearMocks
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import org.junit.jupiter.params.ParameterizedTest
|
||||
import java.math.BigDecimal
|
||||
import java.util.Calendar
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
internal class UpdateWalletManagerResultFactoryTest {
|
||||
|
||||
private val factory = UpdateWalletManagerResultFactory()
|
||||
private val mockFactory = MockUpdateWalletManagerResultFactory()
|
||||
|
||||
private val walletManager = mockk<WalletManager>()
|
||||
|
||||
@BeforeEach
|
||||
fun resetMocks() {
|
||||
clearMocks(walletManager)
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class GetResult {
|
||||
|
||||
@ParameterizedTest
|
||||
@ProvideTestModels
|
||||
fun getResult(model: GetResultTestModel) {
|
||||
// Arrange
|
||||
every { walletManager.wallet } returns model.wallet
|
||||
|
||||
// Act
|
||||
val actual = factory.getResult(walletManager = walletManager)
|
||||
|
||||
// Assert
|
||||
Truth.assertThat(actual).isEqualTo(model.expected)
|
||||
}
|
||||
|
||||
private fun provideTestModels(): List<GetResultTestModel> = listOf(
|
||||
// region Wallet without amount and transactions
|
||||
GetResultTestModel(
|
||||
wallet = createWallet(
|
||||
coinValue = null,
|
||||
addresses = setOf(Address(value = "0x1", type = AddressType.Default)),
|
||||
transactions = emptyList(),
|
||||
),
|
||||
expected = Verified(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(Address(value = "0x1", type = Type.Primary)),
|
||||
currenciesAmounts = emptySet(),
|
||||
currentTransactions = emptySet(),
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
|
||||
// region Wallet with coin amount and without transactions
|
||||
GetResultTestModel(
|
||||
wallet = createWallet(
|
||||
coinValue = BigDecimal.ONE,
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = AddressType.Default),
|
||||
),
|
||||
transactions = emptyList(),
|
||||
),
|
||||
expected = Verified(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(Address(value = "0x1", type = Type.Primary)),
|
||||
currenciesAmounts = setOf(CryptoCurrencyAmount.Coin(value = BigDecimal.ONE)),
|
||||
currentTransactions = emptySet(),
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
|
||||
// region Wallet with coin amount and coin transaction
|
||||
GetResultTestModel(
|
||||
wallet = createWallet(
|
||||
coinValue = BigDecimal.ONE,
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = AddressType.Default),
|
||||
),
|
||||
transactions = listOf(
|
||||
createRecentTransaction(
|
||||
amount = Amount(
|
||||
blockchain = Blockchain.Ethereum,
|
||||
value = BigDecimal.ONE,
|
||||
),
|
||||
status = TransactionStatus.Unconfirmed,
|
||||
),
|
||||
),
|
||||
),
|
||||
expected = Verified(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(Address(value = "0x1", type = Type.Primary)),
|
||||
currenciesAmounts = setOf(CryptoCurrencyAmount.Coin(value = BigDecimal.ONE)),
|
||||
currentTransactions = setOf(
|
||||
CryptoCurrencyTransaction.Coin(
|
||||
txInfo = createTxInfo(
|
||||
status = TxInfo.TransactionStatus.Unconfirmed,
|
||||
amount = BigDecimal.ONE,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
|
||||
// region Wallet with amounts and transactions (coin and token)
|
||||
GetResultTestModel(
|
||||
wallet = createWallet(
|
||||
coinValue = BigDecimal.ONE,
|
||||
tokensAmount = mapOf(usdtToken to BigDecimal.ZERO),
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = AddressType.Default),
|
||||
Address(value = "0x11", type = AddressType.Legacy),
|
||||
),
|
||||
transactions = listOf(
|
||||
createRecentTransaction(
|
||||
amount = Amount(
|
||||
blockchain = Blockchain.Ethereum,
|
||||
value = BigDecimal.ONE,
|
||||
),
|
||||
status = TransactionStatus.Confirmed,
|
||||
),
|
||||
createRecentTransaction(
|
||||
amount = Amount(
|
||||
value = BigDecimal.TEN,
|
||||
blockchain = Blockchain.Ethereum,
|
||||
type = AmountType.Token(token = usdtToken),
|
||||
currencySymbol = "USDT",
|
||||
),
|
||||
status = TransactionStatus.Unconfirmed,
|
||||
),
|
||||
),
|
||||
),
|
||||
expected = Verified(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = Type.Primary),
|
||||
Address(value = "0x11", type = Type.Secondary),
|
||||
),
|
||||
currenciesAmounts = setOf(
|
||||
CryptoCurrencyAmount.Coin(value = BigDecimal.ONE),
|
||||
CryptoCurrencyAmount.Token(
|
||||
value = BigDecimal.ZERO,
|
||||
currencyRawId = usdtToken.id?.let(CryptoCurrency::RawID),
|
||||
contractAddress = usdtToken.contractAddress,
|
||||
),
|
||||
),
|
||||
currentTransactions = setOf(
|
||||
CryptoCurrencyTransaction.Token(
|
||||
txInfo = createTxInfo(
|
||||
status = TxInfo.TransactionStatus.Unconfirmed,
|
||||
amount = BigDecimal.TEN,
|
||||
),
|
||||
tokenId = "0x3",
|
||||
contractAddress = "0x4",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
)
|
||||
}
|
||||
|
||||
data class GetResultTestModel(val wallet: Wallet, val expected: Verified)
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class GetDemoResult {
|
||||
|
||||
@ParameterizedTest
|
||||
@ProvideTestModels
|
||||
fun getDemoResult(model: GetDemoResultTestModel) {
|
||||
// Arrange
|
||||
every { walletManager.wallet } returns model.wallet
|
||||
every { walletManager.cardTokens } returns model.cardTokens.toMutableSet()
|
||||
|
||||
// Act
|
||||
val actual = factory.getDemoResult(walletManager = walletManager, demoAmount = model.demoAmount)
|
||||
|
||||
// Assert
|
||||
Truth.assertThat(actual).isEqualTo(model.expected)
|
||||
}
|
||||
|
||||
private fun provideTestModels(): List<GetDemoResultTestModel> {
|
||||
return listOf(
|
||||
// region Wallet without amount and transactions
|
||||
GetDemoResultTestModel(
|
||||
wallet = createWallet(
|
||||
coinValue = null,
|
||||
addresses = setOf(Address(value = "0x1", type = AddressType.Default)),
|
||||
transactions = emptyList(),
|
||||
),
|
||||
demoAmount = Amount(value = null, blockchain = Blockchain.Ethereum),
|
||||
cardTokens = emptySet(),
|
||||
expected = Verified(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = Type.Primary),
|
||||
),
|
||||
currenciesAmounts = setOf(
|
||||
CryptoCurrencyAmount.Coin(value = BigDecimal.ZERO), // default for demo
|
||||
),
|
||||
currentTransactions = emptySet(),
|
||||
),
|
||||
),
|
||||
GetDemoResultTestModel(
|
||||
wallet = createWallet(
|
||||
coinValue = null,
|
||||
addresses = setOf(Address(value = "0x1", type = AddressType.Default)),
|
||||
transactions = emptyList(),
|
||||
),
|
||||
demoAmount = Amount(value = BigDecimal.ONE, blockchain = Blockchain.Ethereum),
|
||||
cardTokens = emptySet(),
|
||||
expected = Verified(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = Type.Primary),
|
||||
),
|
||||
currenciesAmounts = setOf(
|
||||
CryptoCurrencyAmount.Coin(value = BigDecimal.ONE), // used demo amount
|
||||
),
|
||||
currentTransactions = emptySet(),
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
|
||||
// region Wallet with coin amount and without transactions
|
||||
GetDemoResultTestModel(
|
||||
wallet = createWallet(
|
||||
coinValue = BigDecimal.ONE,
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = AddressType.Default),
|
||||
),
|
||||
transactions = emptyList(),
|
||||
),
|
||||
demoAmount = Amount(value = null, blockchain = Blockchain.Ethereum),
|
||||
cardTokens = emptySet(),
|
||||
expected = Verified(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(
|
||||
Address(
|
||||
value = "0x1",
|
||||
type = Type.Primary,
|
||||
),
|
||||
),
|
||||
currenciesAmounts = setOf(CryptoCurrencyAmount.Coin(value = BigDecimal.ZERO)),
|
||||
currentTransactions = emptySet(),
|
||||
),
|
||||
),
|
||||
GetDemoResultTestModel(
|
||||
wallet = createWallet(
|
||||
coinValue = BigDecimal.ONE,
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = AddressType.Default),
|
||||
),
|
||||
transactions = emptyList(),
|
||||
),
|
||||
demoAmount = Amount(value = BigDecimal.TEN, blockchain = Blockchain.Ethereum),
|
||||
cardTokens = emptySet(),
|
||||
expected = Verified(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(
|
||||
Address(
|
||||
value = "0x1",
|
||||
type = Type.Primary,
|
||||
),
|
||||
),
|
||||
currenciesAmounts = setOf(CryptoCurrencyAmount.Coin(value = BigDecimal.TEN)),
|
||||
currentTransactions = emptySet(),
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
|
||||
// region Wallet with coin amount and coin transaction
|
||||
GetDemoResultTestModel(
|
||||
wallet = createWallet(
|
||||
coinValue = BigDecimal.ONE,
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = AddressType.Default),
|
||||
),
|
||||
transactions = listOf(
|
||||
createRecentTransaction(
|
||||
amount = Amount(
|
||||
blockchain = Blockchain.Ethereum,
|
||||
value = BigDecimal.ONE,
|
||||
),
|
||||
status = TransactionStatus.Unconfirmed,
|
||||
),
|
||||
),
|
||||
),
|
||||
demoAmount = Amount(value = BigDecimal.TEN, blockchain = Blockchain.Ethereum),
|
||||
cardTokens = emptySet(),
|
||||
expected = Verified(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = Type.Primary),
|
||||
),
|
||||
currenciesAmounts = setOf(CryptoCurrencyAmount.Coin(value = BigDecimal.TEN)),
|
||||
currentTransactions = setOf(
|
||||
CryptoCurrencyTransaction.Coin(
|
||||
txInfo = createTxInfo(
|
||||
status = TxInfo.TransactionStatus.Unconfirmed,
|
||||
amount = BigDecimal.ONE,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
|
||||
// region Wallet with amounts and transactions (coin and token)
|
||||
GetDemoResultTestModel(
|
||||
wallet = createWallet(
|
||||
coinValue = BigDecimal.TEN,
|
||||
tokensAmount = mapOf(usdtToken to BigDecimal.TEN),
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = AddressType.Default),
|
||||
Address(value = "0x11", type = AddressType.Legacy),
|
||||
),
|
||||
transactions = listOf(
|
||||
createRecentTransaction(
|
||||
amount = Amount(
|
||||
blockchain = Blockchain.Ethereum,
|
||||
value = BigDecimal.ONE,
|
||||
),
|
||||
status = TransactionStatus.Confirmed,
|
||||
),
|
||||
createRecentTransaction(
|
||||
amount = Amount(
|
||||
value = BigDecimal.TEN,
|
||||
blockchain = Blockchain.Ethereum,
|
||||
type = AmountType.Token(token = usdtToken),
|
||||
currencySymbol = "USDT",
|
||||
),
|
||||
status = TransactionStatus.Unconfirmed,
|
||||
),
|
||||
),
|
||||
),
|
||||
demoAmount = Amount(value = null, blockchain = Blockchain.Ethereum),
|
||||
cardTokens = setOf(usdtToken),
|
||||
expected = Verified(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = Type.Primary),
|
||||
Address(value = "0x11", type = Type.Secondary),
|
||||
),
|
||||
currenciesAmounts = setOf(
|
||||
CryptoCurrencyAmount.Coin(value = BigDecimal.ZERO),
|
||||
CryptoCurrencyAmount.Token(
|
||||
value = BigDecimal.ZERO,
|
||||
currencyRawId = usdtToken.id?.let(CryptoCurrency::RawID),
|
||||
contractAddress = usdtToken.contractAddress,
|
||||
),
|
||||
),
|
||||
currentTransactions = setOf(
|
||||
CryptoCurrencyTransaction.Token(
|
||||
txInfo = createTxInfo(
|
||||
status = TxInfo.TransactionStatus.Unconfirmed,
|
||||
amount = BigDecimal.TEN,
|
||||
),
|
||||
tokenId = "0x3",
|
||||
contractAddress = "0x4",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
GetDemoResultTestModel(
|
||||
wallet = createWallet(
|
||||
coinValue = BigDecimal.ONE,
|
||||
tokensAmount = mapOf(usdtToken to BigDecimal.ZERO),
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = AddressType.Default),
|
||||
Address(value = "0x11", type = AddressType.Legacy),
|
||||
),
|
||||
transactions = listOf(
|
||||
createRecentTransaction(
|
||||
amount = Amount(
|
||||
blockchain = Blockchain.Ethereum,
|
||||
value = BigDecimal.ONE,
|
||||
),
|
||||
status = TransactionStatus.Unconfirmed,
|
||||
),
|
||||
createRecentTransaction(
|
||||
amount = Amount(
|
||||
value = BigDecimal.TEN,
|
||||
blockchain = Blockchain.Ethereum,
|
||||
type = AmountType.Token(token = usdtToken),
|
||||
currencySymbol = "USDT",
|
||||
),
|
||||
status = TransactionStatus.Unconfirmed,
|
||||
),
|
||||
),
|
||||
),
|
||||
demoAmount = Amount(value = BigDecimal.TEN, blockchain = Blockchain.Ethereum),
|
||||
cardTokens = setOf(usdtToken),
|
||||
expected = Verified(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = Type.Primary),
|
||||
Address(value = "0x11", type = Type.Secondary),
|
||||
),
|
||||
currenciesAmounts = setOf(
|
||||
CryptoCurrencyAmount.Coin(value = BigDecimal.TEN),
|
||||
CryptoCurrencyAmount.Token(
|
||||
value = BigDecimal.TEN,
|
||||
currencyRawId = usdtToken.id?.let(CryptoCurrency::RawID),
|
||||
contractAddress = usdtToken.contractAddress,
|
||||
),
|
||||
),
|
||||
currentTransactions = setOf(
|
||||
CryptoCurrencyTransaction.Token(
|
||||
txInfo = createTxInfo(
|
||||
status = TxInfo.TransactionStatus.Unconfirmed,
|
||||
amount = BigDecimal.TEN,
|
||||
),
|
||||
tokenId = "0x3",
|
||||
contractAddress = "0x4",
|
||||
),
|
||||
CryptoCurrencyTransaction.Coin(
|
||||
txInfo = createTxInfo(
|
||||
status = TxInfo.TransactionStatus.Unconfirmed,
|
||||
amount = BigDecimal.ONE,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class GetDemoResultTestModel(
|
||||
val wallet: Wallet,
|
||||
val demoAmount: Amount,
|
||||
val cardTokens: Set<Token>,
|
||||
val expected: Verified,
|
||||
)
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class GetNoAccountResult {
|
||||
|
||||
@ParameterizedTest
|
||||
@ProvideTestModels
|
||||
fun getNoAccountResult(model: GetNoAccountResultModel) {
|
||||
// Arrange
|
||||
every { walletManager.wallet } returns model.wallet
|
||||
|
||||
// Act
|
||||
val actual = factory.getNoAccountResult(
|
||||
walletManager = walletManager,
|
||||
customMessage = model.customMessage,
|
||||
amountToCreateAccount = model.amountToCreateAccount,
|
||||
)
|
||||
|
||||
// Assert
|
||||
Truth.assertThat(actual).isEqualTo(model.expected)
|
||||
}
|
||||
|
||||
private fun provideTestModels() = listOf(
|
||||
// region amountToCreateAccount is null
|
||||
GetNoAccountResultModel(
|
||||
wallet = createWallet(
|
||||
coinValue = null,
|
||||
addresses = setOf(Address(value = "0x1", type = AddressType.Default)),
|
||||
transactions = emptyList(),
|
||||
),
|
||||
customMessage = "",
|
||||
amountToCreateAccount = null,
|
||||
expected = Unreachable(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(Address(value = "0x1", type = Type.Primary)),
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
|
||||
// region Wallet without amount and transactions
|
||||
GetNoAccountResultModel(
|
||||
wallet = createWallet(
|
||||
coinValue = null,
|
||||
addresses = setOf(Address(value = "0x1", type = AddressType.Default)),
|
||||
transactions = emptyList(),
|
||||
),
|
||||
customMessage = "",
|
||||
amountToCreateAccount = BigDecimal.ONE,
|
||||
expected = mockFactory.createNoAccount(),
|
||||
),
|
||||
// endregion
|
||||
|
||||
// region Wallet with coin amount and without transactions
|
||||
GetNoAccountResultModel(
|
||||
wallet = createWallet(
|
||||
coinValue = BigDecimal.ONE,
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = AddressType.Default),
|
||||
),
|
||||
transactions = emptyList(),
|
||||
),
|
||||
customMessage = "custom message",
|
||||
amountToCreateAccount = BigDecimal.ONE,
|
||||
expected = NoAccount(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(Address(value = "0x1", type = Type.Primary)),
|
||||
amountToCreateAccount = BigDecimal.ONE,
|
||||
errorMessage = "custom message",
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
|
||||
// region Wallet with coin amount and coin transaction
|
||||
GetNoAccountResultModel(
|
||||
wallet = createWallet(
|
||||
coinValue = BigDecimal.ONE,
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = AddressType.Default),
|
||||
),
|
||||
transactions = listOf(
|
||||
createRecentTransaction(
|
||||
amount = Amount(
|
||||
blockchain = Blockchain.Ethereum,
|
||||
value = BigDecimal.ONE,
|
||||
),
|
||||
status = TransactionStatus.Unconfirmed,
|
||||
),
|
||||
),
|
||||
),
|
||||
customMessage = "",
|
||||
amountToCreateAccount = BigDecimal.ZERO,
|
||||
expected = NoAccount(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(Address(value = "0x1", type = Type.Primary)),
|
||||
amountToCreateAccount = BigDecimal.ZERO,
|
||||
errorMessage = "",
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
|
||||
// region Wallet with amounts and transactions (coin and token)
|
||||
GetNoAccountResultModel(
|
||||
wallet = createWallet(
|
||||
coinValue = BigDecimal.ONE,
|
||||
tokensAmount = mapOf(usdtToken to BigDecimal.ZERO),
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = AddressType.Default),
|
||||
Address(value = "0x11", type = AddressType.Legacy),
|
||||
),
|
||||
transactions = listOf(
|
||||
createRecentTransaction(
|
||||
amount = Amount(
|
||||
blockchain = Blockchain.Ethereum,
|
||||
value = BigDecimal.ONE,
|
||||
),
|
||||
status = TransactionStatus.Unconfirmed,
|
||||
),
|
||||
createRecentTransaction(
|
||||
amount = Amount(
|
||||
value = BigDecimal.TEN,
|
||||
blockchain = Blockchain.Ethereum,
|
||||
type = AmountType.Token(token = usdtToken),
|
||||
currencySymbol = "USDT",
|
||||
),
|
||||
status = TransactionStatus.Unconfirmed,
|
||||
),
|
||||
),
|
||||
),
|
||||
customMessage = "",
|
||||
amountToCreateAccount = BigDecimal.ZERO,
|
||||
expected = NoAccount(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = Type.Primary),
|
||||
Address(value = "0x11", type = Type.Secondary),
|
||||
),
|
||||
amountToCreateAccount = BigDecimal.ZERO,
|
||||
errorMessage = "",
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
)
|
||||
}
|
||||
|
||||
data class GetNoAccountResultModel(
|
||||
val wallet: Wallet,
|
||||
val customMessage: String,
|
||||
val amountToCreateAccount: BigDecimal?,
|
||||
val expected: UpdateWalletManagerResult,
|
||||
)
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class GetUnreachableResult {
|
||||
|
||||
@ParameterizedTest
|
||||
@ProvideTestModels
|
||||
fun getUnreachableResult(model: GetUnreachableResultModel) {
|
||||
// Arrange
|
||||
every { walletManager.wallet } returns model.wallet
|
||||
|
||||
// Act
|
||||
val actual = factory.getUnreachableResult(walletManager = walletManager)
|
||||
|
||||
// Assert
|
||||
Truth.assertThat(actual).isEqualTo(model.expected)
|
||||
}
|
||||
|
||||
private fun provideTestModels() = listOf(
|
||||
// region Wallet without amount and transactions
|
||||
GetUnreachableResultModel(
|
||||
wallet = createWallet(
|
||||
coinValue = null,
|
||||
addresses = setOf(Address(value = "0x1", type = AddressType.Default)),
|
||||
transactions = emptyList(),
|
||||
),
|
||||
expected = Unreachable(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(Address(value = "0x1", type = Type.Primary)),
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
|
||||
// region Wallet with coin amount and without transactions
|
||||
GetUnreachableResultModel(
|
||||
wallet = createWallet(
|
||||
coinValue = BigDecimal.ONE,
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = AddressType.Default),
|
||||
),
|
||||
transactions = emptyList(),
|
||||
),
|
||||
expected = Unreachable(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(Address(value = "0x1", type = Type.Primary)),
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
|
||||
// region Wallet with coin amount and coin transaction
|
||||
GetUnreachableResultModel(
|
||||
wallet = createWallet(
|
||||
coinValue = BigDecimal.ONE,
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = AddressType.Default),
|
||||
),
|
||||
transactions = listOf(
|
||||
createRecentTransaction(
|
||||
amount = Amount(
|
||||
blockchain = Blockchain.Ethereum,
|
||||
value = BigDecimal.ONE,
|
||||
),
|
||||
status = TransactionStatus.Unconfirmed,
|
||||
),
|
||||
),
|
||||
),
|
||||
expected = Unreachable(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(Address(value = "0x1", type = Type.Primary)),
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
|
||||
// region Wallet with amounts and transactions (coin and token)
|
||||
GetUnreachableResultModel(
|
||||
wallet = createWallet(
|
||||
coinValue = BigDecimal.ONE,
|
||||
tokensAmount = mapOf(usdtToken to BigDecimal.ZERO),
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = AddressType.Default),
|
||||
Address(value = "0x11", type = AddressType.Legacy),
|
||||
),
|
||||
transactions = listOf(
|
||||
createRecentTransaction(
|
||||
amount = Amount(
|
||||
blockchain = Blockchain.Ethereum,
|
||||
value = BigDecimal.ONE,
|
||||
),
|
||||
status = TransactionStatus.Unconfirmed,
|
||||
),
|
||||
createRecentTransaction(
|
||||
amount = Amount(
|
||||
value = BigDecimal.TEN,
|
||||
blockchain = Blockchain.Ethereum,
|
||||
type = AmountType.Token(token = usdtToken),
|
||||
currencySymbol = "USDT",
|
||||
),
|
||||
status = TransactionStatus.Unconfirmed,
|
||||
),
|
||||
),
|
||||
),
|
||||
expected = Unreachable(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = Type.Primary),
|
||||
Address(value = "0x11", type = Type.Secondary),
|
||||
),
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
)
|
||||
}
|
||||
|
||||
data class GetUnreachableResultModel(
|
||||
val wallet: Wallet,
|
||||
val expected: UpdateWalletManagerResult,
|
||||
)
|
||||
|
||||
private fun createWallet(
|
||||
coinValue: BigDecimal?,
|
||||
tokensAmount: Map<Token, BigDecimal> = emptyMap(),
|
||||
addresses: Set<Address>,
|
||||
transactions: List<TransactionData.Uncompiled>,
|
||||
): Wallet {
|
||||
return Wallet(
|
||||
blockchain = Blockchain.Ethereum,
|
||||
addresses = addresses,
|
||||
publicKey = mockk(),
|
||||
tokens = setOf(),
|
||||
).apply {
|
||||
coinValue?.let(::setCoinValue)
|
||||
tokensAmount.forEach { (token, amount) -> addTokenValue(value = amount, token = token) }
|
||||
recentTransactions += transactions
|
||||
}
|
||||
}
|
||||
|
||||
private fun createRecentTransaction(amount: Amount, status: TransactionStatus): TransactionData.Uncompiled {
|
||||
return TransactionData.Uncompiled(
|
||||
amount = amount,
|
||||
fee = null,
|
||||
sourceAddress = "0x1",
|
||||
destinationAddress = "0x2",
|
||||
status = status,
|
||||
hash = "hash",
|
||||
date = Calendar.getInstance().apply {
|
||||
timeInMillis = 1748251839317
|
||||
},
|
||||
extras = null,
|
||||
contractAddress = null,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createTxInfo(status: TxInfo.TransactionStatus, amount: BigDecimal): TxInfo {
|
||||
return TxInfo(
|
||||
txHash = "hash",
|
||||
timestampInMillis = 1748251839317,
|
||||
isOutgoing = true,
|
||||
destinationType = TxInfo.DestinationType.Single(
|
||||
addressType = TxInfo.AddressType.User(address = "0x2"),
|
||||
),
|
||||
sourceType = TxInfo.SourceType.Single(address = "0x1"),
|
||||
interactionAddressType = TxInfo.InteractionAddressType.User(address = "0x2"),
|
||||
status = status,
|
||||
type = TxInfo.TransactionType.Transfer,
|
||||
amount = amount,
|
||||
)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
||||
val usdtToken = Token(
|
||||
id = "0x3",
|
||||
contractAddress = "0x4",
|
||||
symbol = "USDT",
|
||||
decimals = 6,
|
||||
name = "Tether",
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue