Updated on 2026-08-14
This commit is contained in:
parent
4fa8ad0f9b
commit
d6d43479e2
9 changed files with 557 additions and 313 deletions
|
|
@ -26,6 +26,8 @@ import com.tangem.tap.features.wallet.ui.WalletDetailsFragment
|
|||
import com.tangem.tap.features.wallet.ui.WalletFragment
|
||||
import com.tangem.wallet.R
|
||||
|
||||
private class Navigation
|
||||
|
||||
fun FragmentActivity.openFragment(
|
||||
screen: AppScreen,
|
||||
addToBackstack: Boolean,
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ fun TokenNetworkView(screenFieldData: ScreenFieldData, state: AddCustomTokenStat
|
|||
itemList = networkField.itemList,
|
||||
selectedItem = networkField.data,
|
||||
isEnabled = screenFieldData.viewState.isEnabled,
|
||||
textFieldConverter = { state.convertBlockchainName(it, notSelected) },
|
||||
textFieldConverter = { state.blockchainToName(it) ?: notSelected },
|
||||
) { domainStore.dispatch(AddCustomTokenAction.OnTokenNetworkChanged(Field.Data(it))) }
|
||||
SpacerH8()
|
||||
}
|
||||
|
|
@ -123,11 +123,11 @@ fun TokenDerivationPathView(screenFieldData: ScreenFieldData, state: AddCustomTo
|
|||
itemList = networkField.itemList,
|
||||
selectedItem = networkField.data,
|
||||
isEnabled = screenFieldData.viewState.isEnabled,
|
||||
textFieldConverter = { state.convertBlockchainName(it, notSelected) },
|
||||
textFieldConverter = { state.blockchainToName(it) ?: notSelected },
|
||||
dropdownItemView = { blockchain ->
|
||||
val derivationPathLabel = state.convertDerivationPathLabel(blockchain, notSelected)
|
||||
val blockchainName = state.convertBlockchainName(blockchain, notSelected)
|
||||
TitleSubtitle(derivationPathLabel, blockchainName)
|
||||
val derivationPathName = state.blockchainToName(blockchain, true) ?: notSelected
|
||||
val blockchainName = state.blockchainToName(blockchain) ?: notSelected
|
||||
TitleSubtitle(derivationPathName, blockchainName)
|
||||
}
|
||||
) { domainStore.dispatch(AddCustomTokenAction.OnTokenDerivationPathChanged(Field.Data(it))) }
|
||||
SpacerH8()
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.tap.features.tokens.redux
|
|||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.DerivationParams
|
||||
import com.tangem.blockchain.common.DerivationStyle
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
|
|
@ -13,12 +14,12 @@ import com.tangem.domain.common.KeyWalletPublicKey
|
|||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.domain.common.TapWorkarounds.derivationStyle
|
||||
import com.tangem.domain.common.TapWorkarounds.isTestCard
|
||||
import com.tangem.domain.features.addCustomToken.CompleteData
|
||||
import com.tangem.domain.features.addCustomToken.CustomCurrency
|
||||
import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction
|
||||
import com.tangem.domain.features.addCustomToken.redux.AddedCurrencies
|
||||
import com.tangem.domain.redux.domainStore
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||
import com.tangem.tap.*
|
||||
import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
|
||||
import com.tangem.tap.common.extensions.dispatchErrorNotification
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
|
|
@ -28,19 +29,16 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
|
|||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.extensions.makeWalletManagerForApp
|
||||
import com.tangem.tap.domain.tokens.BlockchainNetwork
|
||||
import com.tangem.tap.features.wallet.redux.Currency
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Middleware
|
||||
import timber.log.Timber
|
||||
|
||||
class TokensMiddleware {
|
||||
|
||||
val tokensMiddleware: Middleware<AppState> = { dispatch, state ->
|
||||
val tokensMiddleware: Middleware<AppState> = { _, _ ->
|
||||
{ next ->
|
||||
{ action ->
|
||||
when (action) {
|
||||
|
|
@ -58,8 +56,10 @@ class TokensMiddleware {
|
|||
val isTestcard = scanResponse?.card?.isTestCard ?: false
|
||||
|
||||
scope.launch {
|
||||
val currencies = async { currenciesRepository.getSupportedTokens(isTestcard)
|
||||
.filter(action.supportedBlockchains?.toSet()) }
|
||||
val currencies = async {
|
||||
currenciesRepository.getSupportedTokens(isTestcard)
|
||||
.filter(action.supportedBlockchains?.toSet())
|
||||
}
|
||||
val delay = async { delay(600) }
|
||||
delay.await()
|
||||
store.dispatchOnMain(TokensAction.LoadCurrencies.Success(currencies.await()))
|
||||
|
|
@ -69,6 +69,7 @@ class TokensMiddleware {
|
|||
private fun handleSaveChanges(action: TokensAction.SaveChanges) {
|
||||
val scanResponse = store.state.globalState.scanResponse ?: return
|
||||
|
||||
//TODO: bad things happens.
|
||||
val currentTokens = store.state.tokensState.addedWallets.toTokens()
|
||||
val currentBlockchains = store.state.tokensState.addedWallets.toBlockchains(
|
||||
store.state.tokensState.derivationStyle
|
||||
|
|
@ -83,44 +84,42 @@ class TokensMiddleware {
|
|||
removeCurrenciesIfNeeded(blockchainsToRemove, tokensToRemove)
|
||||
|
||||
if (tokensToAdd.isEmpty() && blockchainsToAdd.isEmpty()) {
|
||||
store.dispatchDebugErrorNotification("Nothing to save")
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
return
|
||||
}
|
||||
|
||||
val derivationStyle = scanResponse.card.derivationStyle
|
||||
val currencyList = blockchainsToAdd.map {
|
||||
Currency.Blockchain(it, it.derivationPath(derivationStyle)?.rawPath)
|
||||
} + tokensToAdd.map {
|
||||
Currency.Token(it.token, it.blockchain, it.blockchain.derivationPath(derivationStyle)?.rawPath)
|
||||
}
|
||||
if (scanResponse.supportsHdWallet()) {
|
||||
deriveMissingBlockchains(scanResponse, blockchainsToAdd, tokensToAdd)
|
||||
deriveMissingBlockchains(scanResponse, currencyList) {
|
||||
submitAdd(it, currencyList)
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo())
|
||||
}
|
||||
} else {
|
||||
submitAdd(blockchainsToAdd, tokensToAdd, scanResponse)
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
submitAdd(scanResponse, currencyList)
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo())
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleAddingCustomToken(action: TokensAction.PrepareAndNavigateToAddCustomToken) {
|
||||
val tokensState = store.state.tokensState
|
||||
val addedTokensList = tokensState.addedTokens.map {
|
||||
DomainWrapped.TokenWithBlockchain(it.token.copy(), it.blockchain)
|
||||
}
|
||||
val addedBlockchains = tokensState.addedBlockchains.map { it }
|
||||
val addedCurrencies = AddedCurrencies(addedTokensList, addedBlockchains)
|
||||
domainStore.dispatch(AddCustomTokenAction.Init.SetAddedCurrencies(addedCurrencies))
|
||||
|
||||
val callback = fun(data: CompleteData) {
|
||||
Timber.e("Yoooohhhoooo")
|
||||
}
|
||||
domainStore.dispatch(AddCustomTokenAction.Init.SetOnAddTokenCallback(callback))
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.AddCustomToken))
|
||||
}
|
||||
|
||||
private fun deriveMissingBlockchains(
|
||||
scanResponse: ScanResponse,
|
||||
blockchains: List<Blockchain>,
|
||||
tokens: List<TokenWithBlockchain>
|
||||
currencyList: List<Currency>,
|
||||
onSuccess: (ScanResponse) -> Unit,
|
||||
) {
|
||||
val derivationDataList = listOfNotNull(
|
||||
getDerivations(EllipticCurve.Secp256k1, scanResponse, blockchains, tokens),
|
||||
getDerivations(EllipticCurve.Ed25519, scanResponse, blockchains, tokens)
|
||||
getDerivations(EllipticCurve.Secp256k1, scanResponse, currencyList),
|
||||
getDerivations(EllipticCurve.Ed25519, scanResponse, currencyList)
|
||||
)
|
||||
val derivations = derivationDataList.map { it.derivations }.toMap()
|
||||
val derivations = derivationDataList.associate { it.derivations }
|
||||
if (derivations.isEmpty()) {
|
||||
onSuccess(scanResponse)
|
||||
return
|
||||
}
|
||||
|
||||
scope.launch {
|
||||
val result = tangemSdkManager.derivePublicKeys(
|
||||
|
|
@ -145,10 +144,8 @@ class TokensMiddleware {
|
|||
derivedKeys = updatedDerivedKeys
|
||||
)
|
||||
store.dispatchOnMain(GlobalAction.SaveScanNoteResponse(updatedScanResponse))
|
||||
submitAdd(blockchains, tokens, updatedScanResponse)
|
||||
|
||||
delay(DELAY_SDK_DIALOG_CLOSE)
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo())
|
||||
onSuccess(updatedScanResponse)
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
store.dispatchErrorNotification(TapError.CustomError("Error adding tokens"))
|
||||
|
|
@ -160,20 +157,31 @@ class TokensMiddleware {
|
|||
private fun getDerivations(
|
||||
curve: EllipticCurve,
|
||||
scanResponse: ScanResponse,
|
||||
blockchains: List<Blockchain>,
|
||||
tokens: List<TokenWithBlockchain>
|
||||
currencyList: List<Currency>,
|
||||
): DerivationData? {
|
||||
val wallet = scanResponse.card.wallets.firstOrNull { it.curve == curve } ?: return null
|
||||
|
||||
val derivationPathsCandidates = (blockchains + tokens.map { it.blockchain }).distinct()
|
||||
.mapNotNull { it.derivationPath(scanResponse.card.derivationStyle) }
|
||||
val manageTokensCandidates = currencyList.map { it.blockchain }.distinct().filter {
|
||||
it.getSupportedCurves().contains(curve)
|
||||
}.mapNotNull {
|
||||
it.derivationPath(scanResponse.card.derivationStyle)
|
||||
}
|
||||
|
||||
val customTokensCandidates = currencyList.filter {
|
||||
it.blockchain.getSupportedCurves().contains(curve)
|
||||
}.mapNotNull { it.derivationPath }.map { DerivationPath(it) }
|
||||
|
||||
val bothCandidates = (manageTokensCandidates + customTokensCandidates).distinct()
|
||||
if (bothCandidates.isEmpty()) return null
|
||||
|
||||
val mapKeyOfWalletPublicKey = wallet.publicKey.toMapKey()
|
||||
val alreadyDerivedKeys: ExtendedPublicKeysMap =
|
||||
scanResponse.derivedKeys[mapKeyOfWalletPublicKey] ?: ExtendedPublicKeysMap(emptyMap())
|
||||
val alreadyDerivedPaths = alreadyDerivedKeys.keys.toList()
|
||||
|
||||
val toDerive = derivationPathsCandidates.filterNot { alreadyDerivedPaths.contains(it) }
|
||||
val toDerive = bothCandidates.filterNot { alreadyDerivedPaths.contains(it) }
|
||||
if (toDerive.isEmpty()) return null
|
||||
|
||||
return DerivationData(
|
||||
derivations = mapKeyOfWalletPublicKey to toDerive,
|
||||
alreadyDerivedKeys = alreadyDerivedKeys,
|
||||
|
|
@ -188,20 +196,48 @@ class TokensMiddleware {
|
|||
)
|
||||
|
||||
private fun submitAdd(
|
||||
blockchains: List<Blockchain>, tokens: List<TokenWithBlockchain>, scanResponse: ScanResponse,
|
||||
scanResponse: ScanResponse,
|
||||
currencyList: List<Currency>,
|
||||
) {
|
||||
val factory = store.state.globalState.tapWalletManager.walletManagerFactory
|
||||
val derivationStyle = scanResponse.card.derivationStyle
|
||||
|
||||
(blockchains.mapNotNull {
|
||||
val walletManager = factory.makeWalletManagerForApp(
|
||||
scanResponse, it,
|
||||
scanResponse.card.derivationStyle?.let { DerivationParams.Default(it) }
|
||||
) ?: return@mapNotNull null
|
||||
WalletAction.MultiWallet.AddBlockchain(BlockchainNetwork.fromWalletManager(walletManager), walletManager)
|
||||
} + tokens.map {
|
||||
val blockchainNetwork = BlockchainNetwork(it.blockchain, scanResponse.card)
|
||||
WalletAction.MultiWallet.AddToken(it.token, blockchainNetwork)
|
||||
}).forEach { store.dispatchOnMain(it) }
|
||||
val addActions = currencyList.mapNotNull { currency ->
|
||||
when (currency) {
|
||||
is Currency.Blockchain -> {
|
||||
val derivationPath = currency.derivationPath?.let { DerivationPath(it) }
|
||||
|
||||
val derivationParams = if (derivationStyle == null) {
|
||||
null
|
||||
} else {
|
||||
if (derivationPath == null) {
|
||||
DerivationParams.Default(derivationStyle)
|
||||
} else {
|
||||
when (derivationStyle) {
|
||||
DerivationStyle.LEGACY -> DerivationParams.Custom(derivationPath)
|
||||
DerivationStyle.NEW -> DerivationParams.Default(derivationStyle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val walletManager = factory.makeWalletManagerForApp(
|
||||
scanResponse = scanResponse,
|
||||
blockchain = currency.blockchain,
|
||||
derivationParams = derivationParams
|
||||
) ?: return@mapNotNull null
|
||||
val blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager)
|
||||
WalletAction.MultiWallet.AddBlockchain(blockchainNetwork, walletManager)
|
||||
}
|
||||
is Currency.Token -> {
|
||||
val rawDerivationPath = currency.derivationPath
|
||||
?: currency.blockchain.derivationPath(derivationStyle)?.rawPath
|
||||
|
||||
val blockchainNetwork = BlockchainNetwork(currency.blockchain, rawDerivationPath, emptyList())
|
||||
WalletAction.MultiWallet.AddToken(currency.token, blockchainNetwork)
|
||||
}
|
||||
}
|
||||
}
|
||||
addActions.forEach { store.dispatchOnMain(it) }
|
||||
}
|
||||
|
||||
private fun removeCurrenciesIfNeeded(blockchains: List<Blockchain>, tokens: List<Token>) {
|
||||
|
|
@ -221,4 +257,45 @@ class TokensMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
private fun isNeedToDerive(scanResponse: ScanResponse, currency: Currency): Boolean {
|
||||
return currency.derivationPath?.let {
|
||||
!scanResponse.hasDerivation(currency.blockchain, it)
|
||||
} ?: false
|
||||
}
|
||||
|
||||
private fun handleAddingCustomToken(action: TokensAction.PrepareAndNavigateToAddCustomToken) {
|
||||
val onAddCustomToken = fun(customCurrency: CustomCurrency) {
|
||||
val scanResponse = store.state.globalState.scanResponse ?: return
|
||||
|
||||
fun submitAndPopBack(scanResponse: ScanResponse, currencyList: List<Currency>) {
|
||||
submitAdd(scanResponse, currencyList)
|
||||
// pop from the AddCustomTokenScreen
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo())
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo())
|
||||
}
|
||||
|
||||
val currency = Currency.fromCustomCurrency(customCurrency)
|
||||
val isNeedToDerive = isNeedToDerive(scanResponse, currency)
|
||||
val currencyList = listOf(currency)
|
||||
if (isNeedToDerive) {
|
||||
deriveMissingBlockchains(scanResponse, currencyList) {
|
||||
submitAndPopBack(it, currencyList)
|
||||
}
|
||||
} else {
|
||||
submitAndPopBack(scanResponse, currencyList)
|
||||
}
|
||||
}
|
||||
|
||||
val addedCurrencies = store.state.walletState.wallets.map { walletStore ->
|
||||
walletStore.walletsData.map { walletData -> walletData.currency }
|
||||
}.flatten().map {
|
||||
when (it) {
|
||||
is Currency.Blockchain -> DomainWrapped.Currency.Blockchain(it.blockchain, it.derivationPath)
|
||||
is Currency.Token -> DomainWrapped.Currency.Token(it.token, it.blockchain, it.derivationPath)
|
||||
}
|
||||
}
|
||||
domainStore.dispatch(AddCustomTokenAction.Init.SetAddedCurrencies(addedCurrencies))
|
||||
domainStore.dispatch(AddCustomTokenAction.Init.SetOnAddTokenCallback(onAddCustomToken))
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.AddCustomToken))
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue