Skip to content

Commit

Permalink
IOS-7809 Show provider slippage
Browse files Browse the repository at this point in the history
  • Loading branch information
Vyachesl0ve committed Sep 30, 2024
1 parent 0ca9260 commit a1697c0
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ final class ExpressCurrencyViewModel: ObservableObject, Identifiable {

// Bottom fiat
@Published private(set) var fiatAmountState: LoadableTextView.State
@Published private(set) var fiatAfterSlippageValue: String = ""
@Published private(set) var priceChangeState: PriceChangeState?

// Trailing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ class ReceiveCurrencyViewModel: ObservableObject, Identifiable {
@Published private(set) var expressCurrencyViewModel: ExpressCurrencyViewModel
@Published private(set) var cryptoAmountState: LoadableTextView.State

private(set) var expectAmount: Decimal = 0
private(set) var expectAmountDecimals: Int = 0

init(
expressCurrencyViewModel: ExpressCurrencyViewModel,
cryptoAmountState: LoadableTextView.State = .initialized
Expand All @@ -31,10 +34,15 @@ class ReceiveCurrencyViewModel: ObservableObject, Identifiable {

guard let expectAmount else {
update(cryptoAmountState: .loaded(text: "0"))
self.expectAmount = 0
expectAmountDecimals = 0
return
}

self.expectAmount = expectAmount

let decimals = tokenItem?.decimalCount ?? 8
expectAmountDecimals = decimals
let formatter = DecimalNumberFormatter(maximumFractionDigits: decimals)
let formatted: String = formatter.format(value: expectAmount)
update(cryptoAmountState: .loaded(text: formatted))
Expand Down
79 changes: 68 additions & 11 deletions Tangem/Modules/ExpressView/ExpressViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -108,22 +108,30 @@ final class ExpressViewModel: ObservableObject {
}

func userDidTapPriceChangeInfoButton(isBigLoss: Bool) {
runTask(in: self) { viewModel in
guard let providerType = await viewModel.interactor.getSelectedProvider()?.provider.type else {
runTask(in: self) { [weak self] viewModel in
guard
let selectedProvider = await viewModel.interactor.getSelectedProvider()?.provider,
let tokenItemSymbol = viewModel.interactor.getDestination()?.tokenItem.currencySymbol
else {
return
}

let message: String = {
switch providerType {
let message: String = { [weak self] in

guard let self else { return "" }

switch selectedProvider.type {
case .cex:
let tokenItemSymbol = viewModel.interactor.getDestination()?.tokenItem.currencySymbol ?? ""
return Localization.swappingAlertCexDescription(tokenItemSymbol)
return formSlippageMessage(
tokenItemSymbol: tokenItemSymbol,
providerName: selectedProvider.name
)
case .dex, .dexBridge:
if isBigLoss {
return "\(Localization.swappingHighPriceImpactDescription)\n\n\(Localization.swappingAlertDexDescription)"
}

return Localization.swappingAlertDexDescription
return formSlippageMessage(
tokenItemSymbol: tokenItemSymbol,
providerName: selectedProvider.name,
isBigLoss: isBigLoss
)
}
}()

Expand Down Expand Up @@ -166,6 +174,55 @@ final class ExpressViewModel: ObservableObject {
}
}

// MARK: - Provider slippage message

private extension ExpressViewModel {
// a crutch for the time being while this information is not on the backend
func fetchMaximumSlippage(for providerName: String) -> Decimal {
switch providerName.lowercased() {
case "changenow": 3
case "changelly", "simpleswap", "changehero": 5
case "1inch", "okx onchain": 2
case "okx crosschain": 3.5
default: 0
}
}

func fetchAmountAfterSlippage(for providerName: String) -> Decimal {
let slippagePercent = (100 - fetchMaximumSlippage(for: providerName)) / 100
let expectAmount: Decimal = receiveCurrencyViewModel?.expectAmount ?? 0

return expectAmount * slippagePercent
}

func formattedAmountAfterSlippage(for providerName: String) -> String {
let formatter = DecimalNumberFormatter(maximumFractionDigits: receiveCurrencyViewModel?.expectAmountDecimals ?? 8)

return formatter.format(value: fetchAmountAfterSlippage(for: providerName))
}

func formSlippageMessage(tokenItemSymbol: String, providerName: String) -> String {
Localization.swappingAlertCexDescription(
tokenItemSymbol,
"\(fetchMaximumSlippage(for: providerName))%",
"\(formattedAmountAfterSlippage(for: providerName)) " + tokenItemSymbol
)
}

func formSlippageMessage(tokenItemSymbol: String, providerName: String, isBigLoss: Bool) -> String {
let swappingAlertDexDescription = Localization.swappingAlertDexDescription(
"\(fetchMaximumSlippage(for: providerName))%",
"\(formattedAmountAfterSlippage(for: providerName)) " + tokenItemSymbol
)

if isBigLoss {
return "\(Localization.swappingHighPriceImpactDescription)\n\n\(swappingAlertDexDescription)"
}

return swappingAlertDexDescription
}
}

// MARK: - Navigation

private extension ExpressViewModel {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -744,7 +744,7 @@
"story_web3_title" = "Web 3.0-kompatibel";
"swap_promo_text" = "Tausche mehr Token zu besseren Kursen direkt in deiner Brieftasche.";
"swap_promo_title" = "Neuer Swap-Anbieter verfügbar!";
"swapping_alert_cex_description" = "Der Betrag umfasst:\n- Gebühr des Dienstanbieters\n- Netzgebühr für die Rücksendung von %@ von der Vermittlungsstelle an die Adresse des Nutzers.";
"swapping_alert_cex_description" = "Der Betrag beinhaltet:\n• Honorar des Dienstleisters\n• Netzwerkgebühr für das Senden %@ von der Börse zurück an die Adresse des Benutzers. \n\n Provider-Slippage kann bis zu %@ \n Bei maximaler Abweichung beträgt der zu erhaltende Betrag %@";
"swapping_alert_dex_description" = "Der Betrag enthält die Gebühren des Dienstleisters.";
"swapping_alert_title" = "Gebühren";
"swapping_approve_information_text" = "Alle dezentralen Börsen benötigen Genehmigungen, um zu verhindern, dass intelligente Verträge ohne Ihre Erlaubnis auf Ihre Geldbörse zugreifen. Smart Contracts können nicht auf Ihre Token zugreifen, wenn Sie nicht zustimmen. Indem Sie Ihre Token \"freischalten\", ermächtigen Sie den 1-Zoll-Smart-Contract, sie auszugeben. Die Miner des Netzwerks erhalten eine (von Ihnen bezahlte) Gasgebühr, um diese Aktion in der Blockchain aufzuzeichnen. Sie können Ihre Token tauschen, nachdem Sie Ihre Zustimmung gegeben haben.";
Expand Down
4 changes: 2 additions & 2 deletions Tangem/Resources/Localizations/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -744,8 +744,8 @@
"story_web3_title" = "Web 3.0 Compatible";
"swap_promo_text" = "Exchange more tokens at better rates directly in your wallet.";
"swap_promo_title" = "New Swap Provider Available!";
"swapping_alert_cex_description" = "The amount includes:\n• service provider's fee\n• network fee for sending %@ from the exchange back to the user's address.";
"swapping_alert_dex_description" = "The amount includes the service provider's fee.";
"swapping_alert_cex_description" = "The amount includes:\n• service provider's fee\n• network fee for sending %@ from the exchange back to the user's address. \n\n Provider slippage is up to %@ \n At maximum slippage, the amount to be received will be %@";
"swapping_alert_dex_description" = "The amount includes the service provider's fee. \n\nProvider slippage is up to %@ \n At maximum slippage, the amount to be received will be %@";
"swapping_alert_title" = "Fees";
"swapping_approve_information_text" = "All decentralized exchanges require approvals to prevent smart contracts from accessing your wallet without your permission. By design, smart contracts can't access your tokens unless you approve. By \"unlocking\" your tokens, you authorize the 1-inch smart contract to spend them. The network's miners receive a gas fee (paid by you) to record this action on the blockchain. You can swap your token after giving approval.";
"swapping_approve_information_title" = "Approve";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -744,7 +744,7 @@
"story_web3_title" = "Web3.0対応";
"swap_promo_text" = "より多くのトークンをより良いレートで、ウォレット内にて直接交換します。";
"swap_promo_title" = "新しいスワッププロバイダーが利用可能になりました!";
"swapping_alert_cex_description" = "この金額には以下が含まれます:\n- サービスプロバイダーの手数料\n- 取引所からユーザーのアドレスに%@ を送り返すためのネットワーク手数料。";
"swapping_alert_cex_description" = "金額には以下が含まれます: \n • サービス プロバイダーの手数料\n • 取引所からユーザーのアドレスに%@を送金するためのネットワーク手数料。 \n\nプロバイダーのスリッページは最大%@です\n最大スリッページの場合、受け取る金額は%@になります。";
"swapping_alert_dex_description" = "この金額には、サービスプロバイダーの手数料が含まれています。";
"swapping_alert_title" = "手数料";
"swapping_approve_information_text" = "すべての分散型取引所は、スマートコントラクトがあなたの許可なくウォレットにアクセスするのを防ぐために承認を必要とします。設計上、スマートコントラクトは承認なしでトークンにアクセスできません。トークンを「ロック解除」することで、あなたは1-inchのスマートコントラクトがトークンを使うことを承認します。ネットワークのマイナーは、このアクションをブロックチェーンに記録するためのガス料金(あなたが支払う)を受け取ります。承認後、トークンを交換することができます。";
Expand Down
4 changes: 2 additions & 2 deletions Tangem/Resources/Localizations/ru.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -744,8 +744,8 @@
"story_web3_title" = "Поддержка Web 3.0";
"swap_promo_text" = "Обменивайте больше токенов по лучшим курсам прямо в вашем кошельке.";
"swap_promo_title" = "Новый провайдер обмена!";
"swapping_alert_cex_description" = "В сумму включено: \n• комиссия провайдера сервиса\n• комиссия сети за отправку %@ от биржи обратно на адрес пользователя";
"swapping_alert_dex_description" = "В сумму включена комиссия провайдера сервиса.";
"swapping_alert_cex_description" = "В сумму включено: \n• комиссия провайдера сервиса\n• комиссия сети за отправку %@ от биржи обратно на адрес пользователя \n\nПроскальзывание провайдера составляет до %@ \nПри максимальном проскальзывании сумма к получению будет %@";
"swapping_alert_dex_description" = "В сумму включена комиссия провайдера сервиса. \n\nПроскальзывание провайдера составляет до %@ \nПри максимальном проскальзывании сумма к получению будет %@";
"swapping_alert_title" = "Комиссии";
"swapping_approve_information_text" = "Подтверждения считаются отраслевым стандартом для всех децентрализованных бирж и защищают ваш кошелек от доступа со стороны смарт-контракта без вашего разрешения. По замыслу смарт-контракты не могут получить доступ к вашим токенам, если вы не одобрите доступ со своей стороны. «Разблокируя» свои токены, вы даете смарт-контракту 1inch разрешение тратить ваши активы. Майнеры сети получают компенсацию за газ (оплачиваемый вами) за запись этого действия в блокчейне. Как только разрешение будет предоставлено, вы сможете обменять свой токен.";
"swapping_approve_information_title" = "Подтвердить";
Expand Down

0 comments on commit a1697c0

Please sign in to comment.