Updated on 2026-08-14
This commit is contained in:
parent
d5f9410905
commit
498e73d6df
16 changed files with 63 additions and 7 deletions
|
|
@ -67,6 +67,7 @@ interface TangemExpressApi {
|
|||
@Query("refundAddress") refundAddress: String?, // for cex only
|
||||
@Query("refundExtraId") refundExtraId: String?, // for cex only
|
||||
@Query("partnerOperationType") partnerOperationType: String?, // swap/ swap-and-send
|
||||
@Query("toExtraId") toExtraId: String?, // swap-and-send memo
|
||||
): ApiResponse<ExchangeDataResponse>
|
||||
|
||||
@GET("exchange-status")
|
||||
|
|
|
|||
|
|
@ -35,6 +35,9 @@ data class ExchangeProvider(
|
|||
|
||||
@Json(name = "exchangeOnlyWithinSingleAddress")
|
||||
val isExchangeOnlyWithinSingleAddress: Boolean = false,
|
||||
|
||||
@Json(name = "isExtraIdSupported")
|
||||
val isExtraIdSupported: Boolean = false,
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = false)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ internal class ExpressProviderConverter : Converter<ExchangeProvider, ExpressPro
|
|||
isRecommended = value.isRecommended,
|
||||
slippage = value.slippage,
|
||||
isExchangeOnlyWithinSingleAddress = value.isExchangeOnlyWithinSingleAddress,
|
||||
isExtraIdSupported = value.isExtraIdSupported,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -208,6 +208,7 @@ internal class DefaultSwapRepositoryV2 @Inject constructor(
|
|||
toCryptoCurrency: CryptoCurrency,
|
||||
fromAmount: String,
|
||||
toAddress: String,
|
||||
toExtraId: String?,
|
||||
expressProvider: ExpressProvider,
|
||||
rateType: ExpressRateType,
|
||||
expressOperationType: ExpressOperationType,
|
||||
|
|
@ -247,6 +248,7 @@ internal class DefaultSwapRepositoryV2 @Inject constructor(
|
|||
userWallet = userWallet,
|
||||
appPreferencesStore = appPreferencesStore,
|
||||
),
|
||||
toExtraId = toExtraId,
|
||||
).getOrThrow()
|
||||
|
||||
if (dataSignatureVerifier.verifySignature(response.signature, response.txDetailsJson)) {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import java.math.BigDecimal
|
|||
* @property isRecommended flag that indicates if this provider is recommended
|
||||
* @property slippage provider slippage
|
||||
* @property isExchangeOnlyWithinSingleAddress flag that indicates if exchange is only allowed within a single address
|
||||
* @property isExtraIdSupported flag that indicates if provider supports transaction extras (memo, destination tag)
|
||||
*
|
||||
* Uses to store transaction data in datastore, when extends - should always add default value
|
||||
* to support backward compatibility
|
||||
|
|
@ -43,4 +44,6 @@ data class ExpressProvider(
|
|||
val slippage: BigDecimal?,
|
||||
@Json(name = "exchangeOnlyWithinSingleAddress")
|
||||
val isExchangeOnlyWithinSingleAddress: Boolean = false,
|
||||
@Json(name = "isExtraIdSupported")
|
||||
val isExtraIdSupported: Boolean = false,
|
||||
)
|
||||
|
|
@ -83,6 +83,7 @@ interface SwapRepositoryV2 {
|
|||
toCryptoCurrency: CryptoCurrency,
|
||||
fromAmount: String,
|
||||
toAddress: String,
|
||||
toExtraId: String?,
|
||||
expressProvider: ExpressProvider,
|
||||
rateType: ExpressRateType,
|
||||
expressOperationType: ExpressOperationType,
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ class GetSwapDataUseCase(
|
|||
fromAmount: String,
|
||||
toCryptoCurrency: CryptoCurrency,
|
||||
toAddress: String,
|
||||
toExtraId: String?,
|
||||
expressProvider: ExpressProvider,
|
||||
rateType: ExpressRateType,
|
||||
expressOperationType: ExpressOperationType,
|
||||
|
|
@ -34,6 +35,7 @@ class GetSwapDataUseCase(
|
|||
fromAmount = fromAmount,
|
||||
toCryptoCurrency = toCryptoCurrency,
|
||||
toAddress = toAddress,
|
||||
toExtraId = toExtraId,
|
||||
expressProvider = expressProvider,
|
||||
rateType = rateType,
|
||||
expressOperationType = expressOperationType,
|
||||
|
|
|
|||
|
|
@ -69,12 +69,20 @@ class GetSwapSupportedPairsUseCase(
|
|||
.filter { pair ->
|
||||
// Search available to swap currency
|
||||
cryptoCurrencyList.any { currencyStatus ->
|
||||
// Allowed only on networks without tx extras (e.i. memo and destination tag)
|
||||
val isExtrasSupported = currencyStatus.network.transactionExtrasType.isTxExtrasSupported()
|
||||
currencyStatus.id == pair.to.currency.id && !isExtrasSupported
|
||||
currencyStatus.id == pair.to.currency.id
|
||||
}
|
||||
}
|
||||
.map { pair -> SwapCryptoCurrency(groupingCurrency(pair), pair.providers) }
|
||||
.map { pair ->
|
||||
val toCurrency = groupingCurrency(pair)
|
||||
val isTxExtrasSupported = toCurrency.currency.network.transactionExtrasType.isTxExtrasSupported()
|
||||
val filteredProviders = if (isTxExtrasSupported) {
|
||||
pair.providers.filter { it.isExtraIdSupported }
|
||||
} else {
|
||||
pair.providers
|
||||
}
|
||||
SwapCryptoCurrency(toCurrency, filteredProviders)
|
||||
}
|
||||
.filter { it.providers.isNotEmpty() }
|
||||
.toList()
|
||||
|
||||
val unavailableCryptoCurrencies =
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ internal data class ConfirmData(
|
|||
val reduceAmountBy: BigDecimal,
|
||||
val isIgnoreReduce: Boolean,
|
||||
val enteredDestination: String?,
|
||||
val enteredMemo: String?,
|
||||
val fee: Fee?,
|
||||
val feeError: GetFeeError?,
|
||||
val fromCryptoCurrencyStatus: CryptoCurrencyStatus?,
|
||||
|
|
|
|||
|
|
@ -142,6 +142,7 @@ internal class SendWithSwapConfirmModel @Inject constructor(
|
|||
reduceAmountBy = amountState?.reduceAmountBy.takeIf { isQuoteContent }.orZero(),
|
||||
isIgnoreReduce = amountState?.isIgnoreReduce == true,
|
||||
enteredDestination = destinationUM?.addressTextField?.actualAddress,
|
||||
enteredMemo = destinationUM?.memoTextField?.value,
|
||||
fee = feeSelectorUM?.selectedFeeItem?.fee.takeIf { isQuoteContent },
|
||||
feeError = (uiState.value.feeSelectorUM as? FeeSelectorUM.Error)?.error.takeIf { isQuoteContent },
|
||||
fromCryptoCurrencyStatus = amountUM?.swapDirection?.withSwapDirection(
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ internal class SwapTransactionSender @AssistedInject constructor(
|
|||
fromAmount = fromAmount.toStringWithRightOffset(fromStatus.currency.decimals),
|
||||
toCryptoCurrency = toStatus.currency,
|
||||
toAddress = destination,
|
||||
toExtraId = confirmData.enteredMemo,
|
||||
expressProvider = provider,
|
||||
rateType = rateType,
|
||||
expressOperationType = expressOperationType,
|
||||
|
|
|
|||
|
|
@ -125,7 +125,10 @@ private fun SuccessContent(sendWithSwapUM: SendWithSwapUM, modifier: Modifier =
|
|||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(TangemTheme.colors.background.action),
|
||||
)
|
||||
DestinationBlock(destinationUM.addressTextField)
|
||||
DestinationBlock(
|
||||
address = destinationUM.addressTextField,
|
||||
memo = destinationUM.memoTextField,
|
||||
)
|
||||
FeeBlock(feeSelectorUM = feeSelectorUM)
|
||||
Spacer(Modifier.height(16.dp))
|
||||
}
|
||||
|
|
@ -247,7 +250,11 @@ private fun FeeBlock(feeSelectorUM: FeeSelectorUM.Content) {
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun DestinationBlock(address: DestinationTextFieldUM.RecipientAddress, modifier: Modifier = Modifier) {
|
||||
private fun DestinationBlock(
|
||||
address: DestinationTextFieldUM.RecipientAddress,
|
||||
memo: DestinationTextFieldUM.RecipientMemo?,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
|
|
@ -279,6 +286,14 @@ private fun DestinationBlock(address: DestinationTextFieldUM.RecipientAddress, m
|
|||
.background(TangemTheme.colors.background.tertiary),
|
||||
)
|
||||
}
|
||||
if (memo != null && memo.value.isNotBlank()) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.send_memo, memo.value),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
modifier = Modifier.padding(top = TangemTheme.dimens.spacing8),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -304,7 +319,17 @@ private fun SendWithSwapSuccessContent_Preview() {
|
|||
isValuePasted = false,
|
||||
blockchainAddress = "0x391316d97a07027a0702c8A002c8A0C25d8470",
|
||||
),
|
||||
memoTextField = null,
|
||||
memoTextField = DestinationTextFieldUM.RecipientMemo(
|
||||
value = "123123123",
|
||||
keyboardOptions = KeyboardOptions(),
|
||||
placeholder = TextReference.EMPTY,
|
||||
label = resourceReference(R.string.send_recipient),
|
||||
isError = false,
|
||||
error = null,
|
||||
isValuePasted = false,
|
||||
isEnabled = true,
|
||||
disabledText = TextReference.EMPTY,
|
||||
),
|
||||
recent = persistentListOf(),
|
||||
wallets = persistentListOf(),
|
||||
networkName = "Polygon",
|
||||
|
|
|
|||
|
|
@ -286,6 +286,7 @@ internal class DefaultSwapRepository(
|
|||
expressOperationType: ExpressOperationType,
|
||||
refundAddress: String?, // for cex only
|
||||
refundExtraId: String?, // for cex only
|
||||
toExtraId: String?, // for networks with memo only
|
||||
): Either<ExpressDataError, SwapDataModel> {
|
||||
return withContext(coroutineDispatcher.io) {
|
||||
try {
|
||||
|
|
@ -311,6 +312,7 @@ internal class DefaultSwapRepository(
|
|||
userWallet = userWallet,
|
||||
appPreferencesStore = appPreferencesStore,
|
||||
),
|
||||
toExtraId = toExtraId,
|
||||
).getOrThrow()
|
||||
if (dataSignatureVerifier.verifySignature(response.signature, response.txDetailsJson)) {
|
||||
val txDetails = parseTxDetails(response.txDetailsJson)
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ class SwapPairInfoConverter : Converter<SwapPairsWithProviders, PairsWithProvide
|
|||
privacyPolicy = exchangeProvider.privacyPolicy,
|
||||
isRecommended = exchangeProvider.isRecommended,
|
||||
slippage = exchangeProvider.slippage,
|
||||
isExtraIdSupported = exchangeProvider.isExtraIdSupported,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -67,6 +68,7 @@ class SwapPairInfoConverter : Converter<SwapPairsWithProviders, PairsWithProvide
|
|||
privacyPolicy = additionalProvider.privacyPolicy,
|
||||
isRecommended = additionalProvider.isRecommended,
|
||||
slippage = additionalProvider.slippage,
|
||||
isExtraIdSupported = additionalProvider.isExtraIdSupported,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ interface SwapRepository {
|
|||
expressOperationType: ExpressOperationType,
|
||||
refundAddress: String? = null, // for cex only
|
||||
refundExtraId: String? = null, // for cex only
|
||||
toExtraId: String? = null, // for networks with memo only
|
||||
): Either<ExpressDataError, SwapDataModel>
|
||||
|
||||
// TODO: Add target error handling, remove either ([REDACTED_JIRA])
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ data class SwapProvider(
|
|||
val isRecommended: Boolean = false,
|
||||
@Json(name = "slippage")
|
||||
val slippage: BigDecimal?,
|
||||
@Json(name = "isExtraIdSupported")
|
||||
val isExtraIdSupported: Boolean = false,
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = false)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue