Updated on 2026-08-14
This commit is contained in:
commit
4f2bf31ec3
4 changed files with 91 additions and 85 deletions
|
|
@ -13,7 +13,7 @@ import androidx.compose.ui.unit.sp
|
|||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.extensions.VoidCallback
|
||||
import com.tangem.domain.common.form.Field
|
||||
import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction
|
||||
import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.*
|
||||
import com.tangem.domain.redux.domainStore
|
||||
import com.tangem.wallet.BuildConfig
|
||||
|
||||
|
|
@ -52,11 +52,6 @@ private fun AllInOne() {
|
|||
name = "true",
|
||||
address = "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
|
||||
)
|
||||
// active = false, decimalCount != null
|
||||
ContractAddressButton(
|
||||
name = "false",
|
||||
address = "0x2147efff675e4a4ee1c2f918d181cdbd7a8e208f"
|
||||
)
|
||||
// more than one network
|
||||
ContractAddressButton(
|
||||
name = ">1 network",
|
||||
|
|
@ -68,8 +63,12 @@ private fun AllInOne() {
|
|||
address = "0x1111111111111111112111111111111111111113"
|
||||
)
|
||||
ContractAddressButton(
|
||||
name = "solana",
|
||||
address = "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"
|
||||
name = "full unk",
|
||||
address = "0x3019BF2a2eF8040C242C9a4c5c4BD4C81678b2A1",
|
||||
tokenNetwork = Blockchain.Ethereum,
|
||||
tokenName = "Test unknown",
|
||||
tokenSymbol = "TU",
|
||||
tokenDecimals = "5",
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -179,10 +178,26 @@ private fun ActionButton(
|
|||
private fun ContractAddressButton(
|
||||
name: String,
|
||||
address: String,
|
||||
tokenNetwork: Blockchain? = null,
|
||||
tokenName: String? = null,
|
||||
tokenSymbol: String? = null,
|
||||
tokenDecimals: String? = null,
|
||||
) {
|
||||
ActionButton(name = name) {
|
||||
resetTokenValues()
|
||||
domainStore.dispatch(AddCustomTokenAction.OnTokenContractAddressChanged(Field.Data(address, false)))
|
||||
domainStore.dispatch(OnTokenContractAddressChanged(Field.Data(address, false)))
|
||||
tokenNetwork?.let {
|
||||
domainStore.dispatch(OnTokenNetworkChanged(Field.Data(tokenNetwork, false)))
|
||||
}
|
||||
tokenName?.let {
|
||||
domainStore.dispatch(OnTokenNameChanged(Field.Data(tokenName, false)))
|
||||
}
|
||||
tokenSymbol?.let {
|
||||
domainStore.dispatch(OnTokenSymbolChanged(Field.Data(tokenSymbol, false)))
|
||||
}
|
||||
tokenDecimals?.let {
|
||||
domainStore.dispatch(OnTokenDecimalsChanged(Field.Data(tokenDecimals, false)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -203,7 +218,7 @@ private fun CustomActionButton(
|
|||
}
|
||||
|
||||
private fun resetTokenValues() {
|
||||
domainStore.dispatch(AddCustomTokenAction.OnTokenNetworkChanged(Field.Data(Blockchain.Unknown, false)))
|
||||
domainStore.dispatch(OnTokenNetworkChanged(Field.Data(Blockchain.Unknown, false)))
|
||||
// domainStore.dispatch(AddCustomTokenAction.OnTokenNameChanged(Field.Data("", false)))
|
||||
// domainStore.dispatch(AddCustomTokenAction.OnTokenSymbolChanged(Field.Data("", false)))
|
||||
// domainStore.dispatch(AddCustomTokenAction.OnTokenDecimalsChanged(Field.Data("", false)))
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ 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
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
|
|
@ -170,7 +169,7 @@ class TokensMiddleware {
|
|||
onSuccess(updatedScanResponse)
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
store.dispatchErrorNotification(TapError.CustomError("Error adding tokens"))
|
||||
store.dispatchDebugErrorNotification(TapError.CustomError("Error adding tokens"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.tap.features.wallet.redux.reducers
|
|||
|
||||
import com.tangem.blockchain.common.AmountType
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.tap.common.extensions.toFiatString
|
||||
import com.tangem.tap.common.extensions.toFormattedCurrencyString
|
||||
|
|
@ -14,6 +15,7 @@ import com.tangem.tap.features.wallet.ui.BalanceStatus
|
|||
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
|
||||
import com.tangem.tap.features.wallet.ui.TokenData
|
||||
import com.tangem.tap.store
|
||||
import java.math.BigDecimal
|
||||
|
||||
class MultiWalletReducer {
|
||||
fun reduce(action: WalletAction.MultiWallet, state: WalletState): WalletState {
|
||||
|
|
@ -22,7 +24,7 @@ class MultiWalletReducer {
|
|||
val wallets: List<WalletStore> = action.blockchains.mapNotNull { blockchain ->
|
||||
val walletManager = action.walletManagers.firstOrNull {
|
||||
it.wallet.blockchain == blockchain.blockchain &&
|
||||
(it.wallet.publicKey.derivationPath?.rawPath == blockchain.derivationPath)
|
||||
(it.wallet.publicKey.derivationPath?.rawPath == blockchain.derivationPath)
|
||||
} ?: return@mapNotNull null
|
||||
val wallet = walletManager.wallet
|
||||
val cardToken = if (!state.isMultiwalletAllowed) {
|
||||
|
|
@ -100,11 +102,11 @@ class MultiWalletReducer {
|
|||
}
|
||||
is WalletAction.MultiWallet.TokenLoaded -> {
|
||||
val currency = Currency.fromBlockchainNetwork(action.blockchain, action.token)
|
||||
val pendingTransactions = state.getWalletManager(currency)
|
||||
?.wallet?.let { wallet ->
|
||||
wallet.recentTransactions.toPendingTransactions(wallet.address)
|
||||
} ?: emptyList()
|
||||
val wallet = state.getWalletManager(currency)?.wallet.guard {
|
||||
throw NullPointerException("MultiWallet.TokenLoaded: WalletManager must be no NULL")
|
||||
}
|
||||
|
||||
val pendingTransactions = wallet.recentTransactions.toPendingTransactions(wallet.address)
|
||||
val sendButtonEnabled =
|
||||
action.amount.value?.isZero() == false && pendingTransactions.isEmpty()
|
||||
val tokenPendingTransactions = pendingTransactions
|
||||
|
|
@ -125,7 +127,8 @@ class MultiWalletReducer {
|
|||
fiatAmountFormatted = tokenWalletData.fiatRate?.let {
|
||||
action.amount.value
|
||||
?.toFiatString(it, store.state.globalState.appCurrency)
|
||||
}
|
||||
},
|
||||
blockchainAmount = wallet.amounts[AmountType.Coin]?.value ?: BigDecimal.ZERO
|
||||
),
|
||||
pendingTransactions = pendingTransactions.removeUnknownTransactions(),
|
||||
mainButton = WalletMainButton.SendButton(sendButtonEnabled),
|
||||
|
|
|
|||
|
|
@ -59,37 +59,14 @@ internal class AddCustomTokenHub : BaseStoreHub<AddCustomTokenState>("AddCustomT
|
|||
}
|
||||
is OnDestroy -> cancelAll()
|
||||
is OnTokenContractAddressChanged -> {
|
||||
val contractAddress = action.contractAddress.value
|
||||
|
||||
validateContractAddressAndNotify(contractAddress)
|
||||
validateContractAddressAndNotify(action.contractAddress.value)
|
||||
}
|
||||
is OnTokenNetworkChanged -> {
|
||||
if (!action.blockchainNetwork.isUserInput) return
|
||||
|
||||
val contractAddress = ContractAddress.getFieldValue<String>()
|
||||
validateContractAddressAndNotify(contractAddress)
|
||||
|
||||
if (!hubState.tokensAnyFieldsIsFilled()) {
|
||||
val isAlreadyAdded = isBlockchainPersistIntoAppSavedTokensList(
|
||||
selectedNetwork = action.blockchainNetwork.value
|
||||
)
|
||||
updateWarningAlreadyAdded(isAlreadyAdded)
|
||||
}
|
||||
checkAndUpdateAddButton()
|
||||
validateContractAddressAndNotify(ContractAddress.getFieldValue())
|
||||
}
|
||||
is OnTokenDerivationPathChanged -> {
|
||||
val isAlreadyAdded = if (ContractAddress.isFilled()) {
|
||||
// token branch
|
||||
isTokenPersistIntoAppSavedTokensList(
|
||||
selectedDerivation = action.blockchainDerivationPath.value
|
||||
)
|
||||
} else {
|
||||
// blockchain branch
|
||||
isBlockchainPersistIntoAppSavedTokensList(
|
||||
selectedDerivation = action.blockchainDerivationPath.value
|
||||
)
|
||||
}
|
||||
updateWarningAlreadyAdded(isAlreadyAdded)
|
||||
checkAndUpdateAddButton()
|
||||
}
|
||||
is OnTokenNameChanged, is OnTokenSymbolChanged, is OnTokenDecimalsChanged -> {
|
||||
|
|
@ -119,35 +96,47 @@ internal class AddCustomTokenHub : BaseStoreHub<AddCustomTokenState>("AddCustomT
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun validateContractAddressAndNotify(contractAddress: String): AddCustomTokenError? {
|
||||
if (!Network.isFilled()) return null
|
||||
|
||||
private suspend fun validateContractAddressAndNotify(contractAddress: String) {
|
||||
val error = ContractAddress.validateValue(contractAddress)
|
||||
when (error) {
|
||||
null -> {
|
||||
// valid contract address
|
||||
ContractAddress.removeError()
|
||||
updateTokenDetailFields(false)
|
||||
changeBlockchainNetworkList()
|
||||
checkAndUpdateAddButton()
|
||||
manageFoundTokenChanges(requestInfoAboutToken(contractAddress))
|
||||
if (Network.isFilled()) {
|
||||
when (error) {
|
||||
null -> {
|
||||
// valid contract address
|
||||
ContractAddress.removeError()
|
||||
findTokenAndUpdateFields(contractAddress)
|
||||
}
|
||||
AddCustomTokenError.InvalidContractAddress -> {
|
||||
// invalid contract address
|
||||
ContractAddress.addError(error)
|
||||
updateTokenDetailFields(hubState.tokensAnyFieldsIsFilled())
|
||||
changeBlockchainNetworkList()
|
||||
checkAndUpdateAddButton()
|
||||
}
|
||||
AddCustomTokenError.FieldIsEmpty -> {
|
||||
// empty contract address
|
||||
ContractAddress.removeError()
|
||||
clearTokenDetailsFields()
|
||||
updateTokenDetailFields(false)
|
||||
changeBlockchainNetworkList()
|
||||
checkAndUpdateAddButton()
|
||||
}
|
||||
}
|
||||
AddCustomTokenError.FieldIsEmpty -> {
|
||||
// empty contract address
|
||||
ContractAddress.removeError()
|
||||
clearTokenDetailsFields()
|
||||
updateTokenDetailFields(false)
|
||||
changeBlockchainNetworkList()
|
||||
checkAndUpdateAddButton()
|
||||
}
|
||||
AddCustomTokenError.InvalidContractAddress -> {
|
||||
ContractAddress.addError(error)
|
||||
updateTokenDetailFields(hubState.tokensAnyFieldsIsFilled())
|
||||
changeBlockchainNetworkList()
|
||||
checkAndUpdateAddButton()
|
||||
} else {
|
||||
when (error) {
|
||||
null -> {
|
||||
// valid contract address
|
||||
ContractAddress.removeError()
|
||||
findTokenAndUpdateFields(contractAddress)
|
||||
}
|
||||
else -> {
|
||||
ContractAddress.removeError()
|
||||
clearTokenDetailsFields()
|
||||
updateTokenDetailFields(false)
|
||||
changeBlockchainNetworkList()
|
||||
checkAndUpdateAddButton()
|
||||
}
|
||||
}
|
||||
}
|
||||
return error
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -186,14 +175,6 @@ internal class AddCustomTokenHub : BaseStoreHub<AddCustomTokenState>("AddCustomT
|
|||
dispatchOnMain(UpdateForm(hubState))
|
||||
}
|
||||
|
||||
private suspend fun updateWarningAlreadyAdded(isInAppSavedList: Boolean) {
|
||||
if (isInAppSavedList) {
|
||||
TokenAlreadyAdded.add()
|
||||
} else {
|
||||
TokenAlreadyAdded.remove()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun requestInfoAboutToken(
|
||||
contractAddress: String,
|
||||
): List<CoinsResponse.Coin> {
|
||||
|
|
@ -222,10 +203,10 @@ internal class AddCustomTokenHub : BaseStoreHub<AddCustomTokenState>("AddCustomT
|
|||
return result
|
||||
}
|
||||
|
||||
private suspend fun manageFoundTokenChanges(foundTokens: List<CoinsResponse.Coin>) {
|
||||
private suspend fun findTokenAndUpdateFields(contractAddress: String) {
|
||||
val foundTokens = requestInfoAboutToken(contractAddress)
|
||||
if (foundTokens.isEmpty()) {
|
||||
// token not found - it's completely custom
|
||||
TokenAlreadyAdded.remove()
|
||||
PotentialScamToken.add()
|
||||
|
||||
dispatchOnMain(SetFoundTokenId(null))
|
||||
|
|
@ -250,7 +231,7 @@ internal class AddCustomTokenHub : BaseStoreHub<AddCustomTokenState>("AddCustomT
|
|||
if (isInAppSavedTokens) {
|
||||
updateTokenDetailFields(false)
|
||||
updateAddButton(false)
|
||||
PotentialScamToken.replace(TokenAlreadyAdded)
|
||||
PotentialScamToken.replaceBy(TokenAlreadyAdded)
|
||||
} else {
|
||||
// not in the saved coins list
|
||||
if (foundToken.active) {
|
||||
|
|
@ -269,8 +250,6 @@ internal class AddCustomTokenHub : BaseStoreHub<AddCustomTokenState>("AddCustomT
|
|||
}
|
||||
}
|
||||
else -> {
|
||||
PotentialScamToken.replace(TokenAlreadyAdded)
|
||||
|
||||
val dialog = DomainDialog.SelectTokenDialog(
|
||||
items = foundToken.networks,
|
||||
networkIdConverter = { networkId ->
|
||||
|
|
@ -283,9 +262,10 @@ internal class AddCustomTokenHub : BaseStoreHub<AddCustomTokenState>("AddCustomT
|
|||
onSelect = { selectedContract ->
|
||||
hubScope.launch {
|
||||
// find how to connect to the upper coroutineContext and dispatch through them
|
||||
PotentialScamToken.remove()
|
||||
fillTokenFields(foundToken, selectedContract)
|
||||
updateTokenDetailFields(false)
|
||||
updateAddButton(true)
|
||||
checkAndUpdateAddButton()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
@ -295,11 +275,20 @@ internal class AddCustomTokenHub : BaseStoreHub<AddCustomTokenState>("AddCustomT
|
|||
}
|
||||
|
||||
private suspend fun checkAndUpdateAddButton() {
|
||||
val state = hubState
|
||||
if (state.warnings.contains(TokenAlreadyAdded)) {
|
||||
val alreadyAddedToAppTokensList = if (hubState.tokensAnyFieldsIsFilled()) {
|
||||
isTokenPersistIntoAppSavedTokensList()
|
||||
} else {
|
||||
isBlockchainPersistIntoAppSavedTokensList()
|
||||
}
|
||||
if (alreadyAddedToAppTokensList) {
|
||||
TokenAlreadyAdded.add()
|
||||
updateAddButton(false)
|
||||
return
|
||||
} else {
|
||||
TokenAlreadyAdded.remove()
|
||||
}
|
||||
|
||||
val state = hubState
|
||||
when {
|
||||
// token
|
||||
state.tokensFieldsIsFilled() && state.networkIsSelected() -> {
|
||||
|
|
@ -519,7 +508,7 @@ internal class AddCustomTokenHub : BaseStoreHub<AddCustomTokenState>("AddCustomT
|
|||
dispatchOnMain(Warning.Remove(setOf(this)))
|
||||
}
|
||||
|
||||
private suspend fun AddCustomTokenError.Warning.replace(to: AddCustomTokenError.Warning) {
|
||||
private suspend fun AddCustomTokenError.Warning.replaceBy(to: AddCustomTokenError.Warning) {
|
||||
dispatchOnMain(Warning.Replace(setOf(this), setOf(to)))
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue