Skip to content

Commit

Permalink
isolated markets integration pt. 1 (#175)
Browse files Browse the repository at this point in the history
* fix routing

* fix project dependencies

* fix routing

* hook up abacus

* undo me

* Revert "undo me"

This reverts commit 41777e2.

* reset proj files

* clean up cell size in leverage selector

* fix up adjusted margin layout

* clean up
  • Loading branch information
mike-dydx committed Aug 21, 2024
1 parent d98f671 commit 8f4933a
Show file tree
Hide file tree
Showing 17 changed files with 191 additions and 86 deletions.
11 changes: 10 additions & 1 deletion dydx/dydx.xcworkspace/xcshareddata/swiftpm/Package.resolved
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"originHash" : "1c0055be4aba3ed4d97a2f62f05486877839310f65e2cbdd18b067bef06a04a3",
"originHash" : "975d00e29efb8d2ca017c5e61df90418ac01f7d7143e85a3f9ddb4eb982154e4",
"pins" : [
{
"identity" : "bigint",
Expand Down Expand Up @@ -46,6 +46,15 @@
"revision" : "48134b5460435cc9d048223ad7560ee2e40f3d4a"
}
},
{
"identity" : "percy-xcui-swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/percy/percy-xcui-swift",
"state" : {
"revision" : "a2e9a86dfc3f5b69ef53cbda28a0ea71098c9f77",
"version" : "1.0.0"
}
},
{
"identity" : "qrcode",
"kind" : "remoteSourceControl",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@
"destination":"AccountFailure.storyboard",
"presentation":"half"
},
"/features":{
"/settings/feature_flag_overrides":{
"destination":"dydxPresenters.dydxFeatureFlagsViewBuilder",
"presentation":"push"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"text" : "Feature Flag Overrides"
},
"link" : {
"text" : "/features"
"text" : "/settings/feature_flag_overrides"
}
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ class dydxPortfolioPositionsViewPresenter: HostedViewPresenter<dydxPortfolioPosi
} else {
item.sideText.side = .short
item.gradientType = .minus
}
}

item.leverage = dydxFormatter.shared.leverage(number: NSNumber(value: position.leverage.current?.doubleValue ?? 0))
if let leverage = position.leverage.current?.doubleValue, let maxLeverage = position.maxLeverage.current?.doubleValue, maxLeverage > 0 {
Expand All @@ -95,6 +95,27 @@ class dydxPortfolioPositionsViewPresenter: HostedViewPresenter<dydxPortfolioPosi
item.unrealizedPnl = SignedAmountViewModel(amount: position.unrealizedPnl.current?.doubleValue ?? 0, displayType: .dollar, coloringOption: .signOnly)
item.unrealizedPnlPercent = SignedAmountViewModel(amount: position.unrealizedPnlPercent.current?.doubleValue, displayType: .percent, coloringOption: .allText)

if let marginMode = position.marginMode {
item.marginMode = DataLocalizer.shared?.localize(path: "APP.GENERAL.\(marginMode.rawValue)", params: nil) ?? "--"
item.isMarginAdjustable = marginMode == .isolated
// TODO: move calculation logic to abacus
switch marginMode {
case .cross:
item.isMarginAdjustable = false
if let marginValue = position.notionalTotal.current?.doubleValue, let marginMaintenanceFraction = configs.maintenanceMarginFraction?.doubleValue {
item.marginValue = dydxFormatter.shared.dollar(number: marginValue * marginMaintenanceFraction) ?? "--"
}
case .isolated:
item.isMarginAdjustable = true
if let marginValue = position.equity.current?.doubleValue {
item.marginValue = dydxFormatter.shared.dollar(number: marginValue) ?? "--"
}
default:
assertionFailure("no margin mode")
break
}
}

if let url = asset.resources?.imageUrl {
item.logoUrl = URL(string: url)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class dydxFeatureFlagsViewBuilder: NSObject, ObjectBuilderProtocol {
let presenter = dydxFeatureFlagsViewPresenter()
let view = presenter.viewModel?.createView() ?? PlatformViewModel().createView()
let viewController = SettingsViewController(presenter: presenter, view: view, configuration: .default)
viewController.requestPath = "/settings/debug"
viewController.requestPath = "/settings/feature_flag_overrides"
return viewController as? T
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class dydxTradingRewardsViewBuilder: NSObject, ObjectBuilderProtocol {

private class dydxTradingRewardsViewController: HostingViewController<PlatformView, dydxTradingRewardsViewModel> {
override public func arrive(to request: RoutingRequest?, animated: Bool) -> Bool {
if request?.path == "/trading-rewards" {
if request?.path == "/profile/trading-rewards" {
return true
}
return false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ class dydxValidationViewPresenter: HostedViewPresenter<dydxValidationViewModel>,
viewModel?.errorType = .error
if let hyperlinkText = firstBlockingError.linkText,
let link = firstBlockingError.link {
viewModel?.hyperlinkText = firstBlockingError.linkText
viewModel?.hyperlinkText = hyperlinkText
viewModel?.validationViewDescriptionHyperlinkAction = {
Router.shared?.navigate(to: URL(string: link), completion: nil)
}
Expand All @@ -96,7 +96,7 @@ class dydxValidationViewPresenter: HostedViewPresenter<dydxValidationViewModel>,
viewModel?.hyperlinkText = firstWarning.linkText
if let hyperlinkText = firstWarning.linkText,
let link = firstWarning.link {
viewModel?.hyperlinkText = firstWarning.linkText
viewModel?.hyperlinkText = hyperlinkText
viewModel?.validationViewDescriptionHyperlinkAction = {
Router.shared?.navigate(to: URL(string: link), completion: nil)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import PlatformParticles
import RoutingKit
import ParticlesKit
import PlatformUI
import dydxStateManager
import Abacus

public class dydxMarginModeViewBuilder: NSObject, ObjectBuilderProtocol {
public func build<T>() -> T? {
Expand All @@ -34,24 +36,52 @@ private protocol dydxMarginModeViewPresenterProtocol: HostedViewPresenterProtoco
}

private class dydxMarginModeViewPresenter: HostedViewPresenter<dydxMarginModeViewModel>, dydxMarginModeViewPresenterProtocol {
private let crossItemViewModel = dydxMarginModeItemViewModel(title: DataLocalizer.localize(path: "APP.GENERAL.CROSS_MARGIN"),
detail: DataLocalizer.localize(path: "APP.GENERAL.CROSS_MARGIN_DESCRIPTION"),
isSelected: true,
selectedAction: {
AbacusStateManager.shared.trade(input: MarginMode.cross.rawValue, type: .marginmode)
Router.shared?.navigate(to: RoutingRequest(path: "/action/dismiss"), animated: true, completion: nil)
})

private let isolatedItemViewModel = dydxMarginModeItemViewModel(title: DataLocalizer.localize(path: "APP.GENERAL.ISOLATED_MARGIN"),
detail: DataLocalizer.localize(path: "APP.GENERAL.ISOLATED_MARGIN_DESCRIPTION"),
isSelected: false,
selectedAction: {
AbacusStateManager.shared.trade(input: MarginMode.isolated.rawValue, type: .marginmode)
Router.shared?.navigate(to: RoutingRequest(path: "/action/dismiss"), animated: true, completion: nil)
})

override init() {
super.init()

viewModel = dydxMarginModeViewModel()
viewModel?.market = "BTC-USD"
viewModel?.items = [
dydxMarginModeItemViewModel(title: DataLocalizer.localize(path: "APP.GENERAL.CROSS_MARGIN"),
detail: DataLocalizer.localize(path: "APP.GENERAL.CROSS_MARGIN_DESCRIPTION"),
isSelected: true,
selectedAction: {
Router.shared?.navigate(to: RoutingRequest(path: "/action/dismiss"), animated: true, completion: nil)
}),
dydxMarginModeItemViewModel(title: DataLocalizer.localize(path: "APP.GENERAL.ISOLATED_MARGIN"),
detail: DataLocalizer.localize(path: "APP.GENERAL.ISOLATED_MARGIN_DESCRIPTION"),
isSelected: false,
selectedAction: {
Router.shared?.navigate(to: RoutingRequest(path: "/action/dismiss"), animated: true, completion: nil)
})
]
viewModel?.items = [crossItemViewModel, isolatedItemViewModel]
}

override func start() {
super.start()

AbacusStateManager.shared.state.tradeInput
.compactMap(\.?.marginMode)
.sink {[weak self] mode in
self?.updateMarginMode(mode: mode)
}
.store(in: &subscriptions)
}

private func updateMarginMode(mode: MarginMode) {
switch mode {
case .cross:
crossItemViewModel.isSelected = true
isolatedItemViewModel.isSelected = false
case .isolated:
crossItemViewModel.isSelected = false
isolatedItemViewModel.isSelected = true
default:
assertionFailure("should have margin mode")
return
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,28 +53,56 @@ private class dydxTargetLeverageViewPresenter: HostedViewPresenter<dydxTargetLev

viewModel.description = DataLocalizer.localize(path: "APP.TRADE.ADJUST_TARGET_LEVERAGE_DESCRIPTION")

viewModel.leverageOptions = [
dydxTargetLeverageViewModel.LeverageTextAndValue(text: "1x", value: 1.0),
dydxTargetLeverageViewModel.LeverageTextAndValue(text: "2x", value: 2.0),
dydxTargetLeverageViewModel.LeverageTextAndValue(text: "5x", value: 5.0),
dydxTargetLeverageViewModel.LeverageTextAndValue(text: "10.0", value: 10.0),
dydxTargetLeverageViewModel.LeverageTextAndValue(text: "Max", value: 20.0)
]

self.viewModel = viewModel

self.viewModel?.optionSelectedAction = {[weak self] value in
self?.update(value: "\(value.value)")
}
self.ctaButtonPresenter.viewModel?.ctaAction = {
guard let value = viewModel.leverageInput?.value else { return }
AbacusStateManager.shared.trade(input: "\(value)", type: .targetleverage)
Router.shared?.navigate(to: RoutingRequest(path: "/action/dismiss"), animated: true, completion: nil)
}
self.viewModel?.leverageInput?.onEdited = {[weak self] value in
self?.update(value: value)
}

attachChildren(workers: childPresenters)
}

private func update(value: String?) {
let valueAsDouble = Double(value ?? "") ?? 0
viewModel?.leverageInput?.value = value
viewModel?.selectedOptionIndex = viewModel?.leverageOptions.firstIndex(where: { option in
option.value == valueAsDouble
})
if valueAsDouble > 0 {
viewModel?.ctaButton?.ctaButtonState = .enabled(DataLocalizer.localize(path: "APP.TRADE.CONFIRM_LEVERAGE"))
} else {
viewModel?.ctaButton?.ctaButtonState = .disabled(DataLocalizer.localize(path: "APP.TRADE.CONFIRM_LEVERAGE"))
}
}

override func start() {
super.start()

// TODO: Fix... tradeInput?.targetLeverage is nil for now
// TODO: Fix...? tradeInput?.targetLeverage is nil for now

Publishers.CombineLatest(AbacusStateManager.shared.state.configsAndAssetMap,
AbacusStateManager.shared.state.tradeInput)
.compactMap { $0 }
.sink { [weak self] configsAndAssetMap, tradeInput in
guard let viewModel = self?.viewModel, let marketId = tradeInput?.marketId, let market = configsAndAssetMap[marketId] else { return }
if let imf = market.configs?.initialMarginFraction?.doubleValue, imf > 0 {
let maxLeverage = 1.0 / imf
viewModel.leverageOptions = [1, 2, 5, 10]
.filter { $0 < maxLeverage }
.map { .init(text: "\($0)×", value: $0) }
viewModel.leverageOptions.append(.init(text: DataLocalizer.localize(path: "APP.GENERAL.MAX"), value: maxLeverage))
}

AbacusStateManager.shared.state.tradeInput
.sink { [weak self] tradeInput in
let value = dydxFormatter.shared.localFormatted(number: tradeInput?.targetLeverage ?? 1, digits: 1)
self?.viewModel?.leverageInput?.value = value
self?.update(value: value)
}
.store(in: &subscriptions)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import PlatformParticles
import RoutingKit
import ParticlesKit
import PlatformUI
import dydxStateManager
import Abacus

protocol dydxTradeInputMarginViewPresenterProtocol: HostedViewPresenterProtocol {
var viewModel: dydxTradeInputMarginViewModel? { get }
Expand All @@ -26,9 +28,12 @@ class dydxTradeInputMarginViewPresenter: HostedViewPresenter<dydxTradeInputMargi
override func start() {
super.start()

// TODO: Fetch from Abacus
viewModel?.marginMode = DataLocalizer.localize(path: "APP.GENERAL.ISOLATED")
viewModel?.marginLeverage = "2x"
AbacusStateManager.shared.state.tradeInput
.compactMap { $0 }
.sink {[weak self] tradeInput in
self?.update(withTradeInput: tradeInput)
}
.store(in: &subscriptions)

viewModel?.marginModeAction = {
Router.shared?.navigate(to: RoutingRequest(path: "/trade/margin_type"), animated: true, completion: nil)
Expand All @@ -37,4 +42,9 @@ class dydxTradeInputMarginViewPresenter: HostedViewPresenter<dydxTradeInputMargi
Router.shared?.navigate(to: RoutingRequest(path: "/trade/target_leverage"), animated: true, completion: nil)
}
}

private func update(withTradeInput tradeInput: Abacus.TradeInput) {
viewModel?.marginMode = DataLocalizer.localize(path: "APP.GENERAL.\(tradeInput.marginMode.rawValue)")
viewModel?.marginLeverage = "\(tradeInput.targetLeverage)×"
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"images" : [
{
"filename" : "Vector-33.pdf",
"filename" : "icon_edit.pdf",
"idiom" : "universal"
}
],
Expand Down
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ public class dydxPortfolioPositionItemViewModel: PlatformViewModel {
public var entryPrice: String?
public var unrealizedPnl: SignedAmountViewModel?
public var unrealizedPnlPercent: SignedAmountViewModel?
public var marginValue: String = "--"
public var marginMode: String = "--"
public var isMarginAdjustable: Bool = false
public var logoUrl: URL?
public var gradientType: GradientType
public var handler: Handler?
Expand Down Expand Up @@ -161,36 +164,33 @@ public class dydxPortfolioPositionItemViewModel: PlatformViewModel {

HStack {
VStack(alignment: .leading, spacing: 4) {
/*
TODO: Get from Abacus
*/
Text("$??.??")
Text(self.marginValue)
.themeFont(fontSize: .small)
.themeColor(foreground: .textPrimary)
.minimumScaleFactor(0.5)

/*
TODO: Get from Abacus
*/
Text("isolated")
Text(self.marginMode)
.themeFont(fontSize: .smaller)
.themeColor(foreground: .textTertiary)
.minimumScaleFactor(0.5)
}

Spacer()

let buttonContent = PlatformIconViewModel(type: .asset(name: "icon_edit", bundle: Bundle.dydxView),
size: CGSize(width: 12, height: 12),
templateColor: .textSecondary)
PlatformButtonViewModel(content: buttonContent,
type: PlatformButtonType.iconType) { [weak self] in
self?.handler?.onMarginEditAction?()
if self.isMarginAdjustable {

let buttonContent = PlatformIconViewModel(type: .asset(name: "icon_edit", bundle: Bundle.dydxView),
size: CGSize(width: 20, height: 20),
templateColor: .textSecondary)
PlatformButtonViewModel(content: buttonContent,
type: PlatformButtonType.iconType) { [weak self] in
self?.handler?.onMarginEditAction?()
}
.createView(parentStyle: parentStyle)
.frame(width: 32, height: 32)
.themeColor(background: .layer6)
.border(borderWidth: 1, cornerRadius: 7, borderColor: ThemeColor.SemanticColor.layer7.color)
}
.createView(parentStyle: parentStyle)
.frame(width: 32, height: 32)
.themeColor(background: .layer6)
.border(borderWidth: 1, cornerRadius: 7, borderColor: ThemeColor.SemanticColor.layer7.color)
}
}
.leftAligned()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public class dydxTargetLeverageCtaButtonViewModel: PlatformViewModel {
buttonTitle = title ?? DataLocalizer.localize(path: "APP.TRADE.CONFIRM_LEVERAGE")
state = .primary
case .disabled(let title):
buttonTitle = title ?? DataLocalizer.localize(path: "APP.TRADE.ADJUST_LEVERAGE")
buttonTitle = title ?? DataLocalizer.localize(path: "APP.TRADE.CONFIRM_LEVERAGE")
state = .disabled
case .thinking:
buttonTitle = DataLocalizer.localize(path: "APP.V4.CALCULATING")
Expand Down
Loading

0 comments on commit 8f4933a

Please sign in to comment.