Skip to content

Commit

Permalink
Merge pull request #1637 from p2p-org/feature/pwn-698
Browse files Browse the repository at this point in the history
Support swap deeplink with from and to symbol
  • Loading branch information
bigearsenal authored Jan 24, 2024
2 parents 83bad82 + f0c6d66 commit 9b1bdba
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 25 deletions.
2 changes: 2 additions & 0 deletions p2p_wallet/Resources/Base.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,8 @@
"Confirm selection" = "Confirm selection";
"The token %@ is out of the strict list" = "The token %@ is out of the strict list";
"Make sure the mint address %@ is correct before confirming" = "Make sure the mint address %@ is correct before confirming";
"Be save and carefull" = "Be save and carefull";
"The token %@ from your swap link seems suspicious, therefore we've refreshed swap pair to default." = "The token %@ from your swap link seems suspicious, therefore we've refreshed swap pair to default.";
"Charge that you need to pay to send or receive tokenA. It helps maintain the network and ensure smooth transactions." = "Charge that you need to pay to send or receive tokenA. It helps maintain the network and ensure smooth transactions.";
"Unfortunately, you can not cashout in %@, but you can still use other Key App features" = "Unfortunately, you can not cashout in %@, but you can still use other Key App features";
"Token 2022 details" = "Token 2022 details";
Expand Down
5 changes: 5 additions & 0 deletions p2p_wallet/Resources/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,11 @@
"✌️ Great! Your new PIN is set." = "✌️ Great! Your new PIN is set.";
"😓 name is not available" = "😓 name is not available";
"😢 PIN doesn't match. Please try again" = "😢 PIN doesn't match. Please try again";
"Confirm selection" = "Confirm selection";
"The token %@ is out of the strict list" = "The token %@ is out of the strict list";
"Make sure the mint address %@ is correct before confirming" = "Make sure the mint address %@ is correct before confirming";
"Be save and carefull" = "Be save and carefull";
"The token %@ from your swap link seems suspicious, therefore we've refreshed swap pair to default." = "The token %@ from your swap link seems suspicious, therefore we've refreshed swap pair to default.";
"Charge that you need to pay to send or receive tokenA. It helps maintain the network and ensure smooth transactions." = "Charge that you need to pay to send or receive tokenA. It helps maintain the network and ensure smooth transactions.";
"Confirm selection" = "Confirm selection";
"The token %@ is out of the strict list" = "The token %@ is out of the strict list";
Expand Down
16 changes: 8 additions & 8 deletions p2p_wallet/Scenes/Main/Swap/Swap/JupiterSwapCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ struct JupiterSwapParameters {
let preChosenWallet: SolanaAccount?
let destinationWallet: SolanaAccount?

let inputMint: String?
let outputMint: String?
let inputToken: String?
let outputToken: String?

let dismissAfterCompletion: Bool
let openKeyboardOnStart: Bool
Expand All @@ -28,15 +28,15 @@ struct JupiterSwapParameters {
source: JupiterSwapSource,
preChosenWallet: SolanaAccount? = nil,
destinationWallet: SolanaAccount? = nil,
inputMint: String? = nil,
outputMint: String? = nil,
inputToken: String? = nil,
outputToken: String? = nil,
hideTabBar: Bool = false
) {
self.preChosenWallet = preChosenWallet
self.destinationWallet = destinationWallet

self.inputMint = inputMint
self.outputMint = outputMint
self.inputToken = inputToken
self.outputToken = outputToken

self.dismissAfterCompletion = dismissAfterCompletion
self.openKeyboardOnStart = openKeyboardOnStart
Expand Down Expand Up @@ -92,8 +92,8 @@ final class JupiterSwapCoordinator: Coordinator<Void> {
source: params.source,
preChosenWallet: params.preChosenWallet,
destinationWallet: params.destinationWallet,
inputMint: params.inputMint,
outputMint: params.outputMint
inputToken: params.inputToken,
outputToken: params.outputToken
)

// view
Expand Down
9 changes: 9 additions & 0 deletions p2p_wallet/Scenes/Main/Swap/Swap/SwapView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ struct SwapView: View {
.onDisappear {
viewModel.isViewAppeared = false
}
.alert(isPresented: $viewModel.deeplinkSuspicionAlert) {
Alert(
title: Text(L10n.beSaveAndCarefull),
message: Text(L10n
.theTokenFromYourSwapLinkSeemsSuspiciousThereforeWeVeRefreshedSwapPairToDefault(viewModel
.deeplinkSuspicionTokens.joined(separator: ", "))),
dismissButton: .default(Text(L10n.okay))
)
}
}
}

Expand Down
95 changes: 85 additions & 10 deletions p2p_wallet/Scenes/Main/Swap/Swap/SwapViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ final class SwapViewModel: BaseViewModel, ObservableObject {
@Published var warningState: SwapPriceImpactView.Model?
@Published var isViewAppeared = false

@Published var deeplinkSuspicionAlert: Bool = false
@Published var deeplinkSuspicionTokens: [String] = []

#if !RELEASE
@Published var errorLogs: [String]?
#endif
Expand All @@ -64,24 +67,37 @@ final class SwapViewModel: BaseViewModel, ObservableObject {
private let preChosenWallet: SolanaAccount?
private let destinationWallet: SolanaAccount?

private var inputMint: String?
private var outputMint: String?
/// Mint address or ticker
private var inputToken: String?

/// Mint address or ticker
private var outputToken: String?

private var timer: Timer?
private let source: JupiterSwapSource
private var wasMinToastShown = false // Special flag not to show toast again if state has not changed

// MARK: - Init

/// <#Description#>
/// - Parameters:
/// - stateMachine: initial state
/// - fromTokenInputViewModel: view model for controlling input field
/// - toTokenInputViewModel: view model for controlling output field
/// - source: source of caller. Used for analytics
/// - preChosenWallet: Token A account (optional)
/// - destinationWallet: Token B account (optional)
/// - inputToken: mint address or ticker
/// - outputToken: mint address or ticker
init(
stateMachine: JupiterSwapStateMachine,
fromTokenInputViewModel: SwapInputViewModel,
toTokenInputViewModel: SwapInputViewModel,
source: JupiterSwapSource,
preChosenWallet: SolanaAccount? = nil,
destinationWallet: SolanaAccount? = nil,
inputMint: String? = nil,
outputMint: String? = nil
inputToken: String? = nil,
outputToken: String? = nil
) {
self.fromTokenInputViewModel = fromTokenInputViewModel
self.toTokenInputViewModel = toTokenInputViewModel
Expand All @@ -90,8 +106,8 @@ final class SwapViewModel: BaseViewModel, ObservableObject {
self.preChosenWallet = preChosenWallet
self.destinationWallet = destinationWallet

self.inputMint = inputMint
self.outputMint = outputMint
self.inputToken = inputToken
self.outputToken = outputToken

self.source = source
super.init()
Expand Down Expand Up @@ -252,15 +268,74 @@ private extension SwapViewModel {
.store(in: &subscriptions)
}

func initialize(jupiterTokens: [TokenMetadata]) async {
func initialize(jupiterTokens: [TokenMetadata], routeMap: RouteMap) async {
// Find token by ticker.
func findTokenMintBySymbol(symbol: String?) -> TokenMetadata? {
guard let symbol else { return nil }

// We filter tokens by symbol
let tokens = jupiterTokens
.filter { token in
token.symbol == symbol
}

if tokens.count == 1 {
// Return it if we found only one token
return tokens.first
} else {
// Otherwise we need to find token by tags
if let strictToken = tokens
.first(where: { token in token.tags.map(\.name).contains(where: { tokenTag in
["old-registry", "community", "wormhole"].contains(tokenTag)
}) })
{
return strictToken
} else {
return nil
}
}
}

// In case if input is ticker, we need to extract mint address.
func extract(_ input: String?) -> String? {
guard let input else { return nil }

if input.count < 32 {
// Symbol mode
let token = findTokenMintBySymbol(symbol: input)

guard let token else {
deeplinkSuspicionAlert = true
deeplinkSuspicionTokens.append(input)
return nil
}

if token.tags.contains(where: { $0.name == "unknown" }) {
deeplinkSuspicionTokens.append(token.symbol)
deeplinkSuspicionAlert = true
return nil
}

return token.mintAddress
} else {
// Mint mode
return input
}
}

let preChosenFromTokenMintAddress = preChosenWallet?.mintAddress ?? extract(inputToken) ?? Defaults
.fromTokenAddress
let preChosenToTokenMintAddress = destinationWallet?.mintAddress ?? extract(outputToken) ?? Defaults
.toTokenAddress

let newState = await stateMachine
.accept(
action: .initialize(
account: userWalletManager.wallet?.account,
jupiterTokens: jupiterTokens,
preChosenFromTokenMintAddress: preChosenWallet?.mintAddress ?? inputMint ?? Defaults
.fromTokenAddress,
preChosenToTokenMintAddress: destinationWallet?.mintAddress ?? outputMint ?? Defaults.toTokenAddress
routeMap: routeMap,
preChosenFromTokenMintAddress: preChosenFromTokenMintAddress,
preChosenToTokenMintAddress: preChosenToTokenMintAddress
)
)
if source != .tapMain {
Expand Down
23 changes: 16 additions & 7 deletions p2p_wallet/Scenes/TabBar/TabBarCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,19 @@ final class TabBarCoordinator: Coordinator<Void> {
.navigationController else { return }

let urlComponent = URLComponents(url: url, resolvingAgainstBaseURL: true)
let inputMint = urlComponent?.queryItems?.first { $0.name == "inputMint" }?.value
let outputMint = urlComponent?.queryItems?.first { $0.name == "outputMint" }?.value
let from = urlComponent?.queryItems?.first { $0.name == "from" }?.value
let to = urlComponent?.queryItems?.first { $0.name == "to" }?.value

self.routeToSwap(nc: vc, source: .tapToken, inputMint: inputMint, outputMint: outputMint)
if from == nil, to == nil {
return
}

self.routeToSwap(
nc: vc,
source: .tapToken,
inputToken: from,
outputToken: to
)
}
.store(in: &subscriptions)

Expand Down Expand Up @@ -202,17 +211,17 @@ final class TabBarCoordinator: Coordinator<Void> {
nc: UINavigationController,
hidesBottomBarWhenPushed: Bool = true,
source: JupiterSwapSource,
inputMint: String? = nil,
outputMint: String? = nil
inputToken: String? = nil,
outputToken: String? = nil
) {
let swapCoordinator = JupiterSwapCoordinator(
navigationController: nc,
params: JupiterSwapParameters(
dismissAfterCompletion: source != .tapMain,
openKeyboardOnStart: source != .tapMain,
source: source,
inputMint: inputMint,
outputMint: outputMint,
inputToken: inputToken,
outputToken: outputToken,
hideTabBar: hidesBottomBarWhenPushed
)
)
Expand Down

0 comments on commit 9b1bdba

Please sign in to comment.