Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

multi: dex bug fixes #678

Merged
merged 1 commit into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/crypto-power/cryptopower
go 1.21

require (
decred.org/dcrdex v0.6.3
decred.org/dcrdex v1.0.0
decred.org/dcrwallet/v4 v4.1.3
gioui.org v0.7.1
git.wow.st/gmp/jni v0.0.0-20210610011705-34026c7e22d0
Expand Down Expand Up @@ -225,4 +225,4 @@ require (
replace github.com/lib/pq => github.com/lib/pq v1.10.4

// https://github.com/ukane-philemon/dcrdex/tree/btc-node
replace decred.org/dcrdex v0.6.3 => github.com/ukane-philemon/dcrdex v0.0.0-20240906090529-912997266ecf
replace decred.org/dcrdex v1.0.0 => github.com/ukane-philemon/dcrdex v0.0.0-20240906090529-912997266ecf
4 changes: 4 additions & 0 deletions ui/cryptomaterial/dropdown.go
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,10 @@ func (d *DropDown) collapsedAndExpandedLayout(gtx C) D {
func (d *DropDown) expandedLayout(gtx C) D {
m := op.Record(gtx.Ops)
gtx.Constraints.Min.Y = gtx.Constraints.Max.Y
// This allows the dropdown to over lap other elements on the screen and not
// limit the dropdown items to the height of its parent
// (gtx.Constraints.Max.Y).
gtx.Constraints.Max.Y = inf
d.updateDropdownWidth(gtx, true)
d.updateDropdownBackground(true)
d.ExpandedLayoutInset.Layout(gtx, func(gtx C) D {
Expand Down
2 changes: 1 addition & 1 deletion ui/page/dcrdex/dcrdex_page.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func (pg *DEXPage) prepareInitialPage() {
}

if showOnBoardingPage {
pg.Display(NewDEXOnboarding(pg.Load, ""))
pg.Display(NewDEXOnboarding(pg.Load, "", nil))
} else {
pg.Display(NewDEXMarketPage(pg.Load, ""))
}
Expand Down
74 changes: 43 additions & 31 deletions ui/page/dcrdex/dex_onboarding_page.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,13 @@ type DEXOnboarding struct {
bondServer *bondServerInfo

// Sub Step Add Server
wantCustomServer bool
serverURLEditor cryptomaterial.Editor
serverCertEditor cryptomaterial.Editor
goBackToChooseServer *cryptomaterial.Clickable
wantCustomServer bool
serverURLEditor cryptomaterial.Editor
serverCertEditor cryptomaterial.Editor
// goBackToChooseServerOrMarketPage redirects a user back to the choose
// server screen if dex has not initialized and no previously registered dex
// server is found, else the user is redirect to the markets page.
goBackToChooseServerOrMarketPage *cryptomaterial.Clickable
// TODO: add a file selector to choose server cert.

// Step Post Bond
Expand All @@ -140,37 +143,39 @@ type DEXOnboarding struct {
goBackBtn cryptomaterial.Button
nextBtn cryptomaterial.Button

materialLoader material.LoaderStyle
isLoading bool
existingDEXServer string
materialLoader material.LoaderStyle
isLoading bool
existingDEXServer string
redirectToMarketPageFn func()

bondFeeCache map[uint32]uint64
}

// NewDEXOnboarding creates a new DEX onboarding pages. Specify
// existingDEXServer to use the DEX onboarding flow to allow user post bonds for
// a particular server.
func NewDEXOnboarding(l *load.Load, existingDEXServer string) *DEXOnboarding {
func NewDEXOnboarding(l *load.Load, existingDEXServer string, redirectToMarketPageFn func()) *DEXOnboarding {
th := l.Theme
pg := &DEXOnboarding{
Load: l,
GenericPageModal: app.NewGenericPageModal(DEXOnboardingPageID),
scrollContainer: &widget.List{List: layout.List{Axis: layout.Vertical, Alignment: layout.Middle}},
passwordEditor: newPasswordEditor(th, values.String(values.StrNewPassword)),
confirmPasswordEditor: newPasswordEditor(th, values.String(values.StrConfirmPassword)),
seedEditor: newTextEditor(l.Theme, values.String(values.StrOptionalRestorationSeed), values.String(values.StrOptionalRestorationSeed), true),
addServerBtn: th.NewClickable(false),
bondServer: &bondServerInfo{},
serverURLEditor: newTextEditor(th, values.String(values.StrServerURL), values.String(values.StrInputURL), false),
serverCertEditor: newTextEditor(th, values.String(values.StrCertificateOPtional), values.String(values.StrInputCertificate), true),
goBackToChooseServer: th.NewClickable(false),
bondStrengthEditor: newTextEditor(th, values.String(values.StrBondStrength), "1", false),
bondStrengthMoreInfo: th.NewClickable(false),
goBackBtn: th.Button(values.String(values.StrBack)),
nextBtn: th.Button(values.String(values.StrNext)),
materialLoader: material.Loader(th.Base),
existingDEXServer: existingDEXServer,
bondFeeCache: make(map[uint32]uint64),
Load: l,
GenericPageModal: app.NewGenericPageModal(DEXOnboardingPageID),
scrollContainer: &widget.List{List: layout.List{Axis: layout.Vertical, Alignment: layout.Middle}},
passwordEditor: newPasswordEditor(th, values.String(values.StrNewPassword)),
confirmPasswordEditor: newPasswordEditor(th, values.String(values.StrConfirmPassword)),
seedEditor: newTextEditor(l.Theme, values.String(values.StrOptionalRestorationSeed), values.String(values.StrOptionalRestorationSeed), true),
addServerBtn: th.NewClickable(false),
bondServer: &bondServerInfo{},
serverURLEditor: newTextEditor(th, values.String(values.StrServerURL), values.String(values.StrInputURL), false),
serverCertEditor: newTextEditor(th, values.String(values.StrCertificateOPtional), values.String(values.StrInputCertificate), true),
goBackToChooseServerOrMarketPage: th.NewClickable(false),
bondStrengthEditor: newTextEditor(th, values.String(values.StrBondStrength), "1", false),
bondStrengthMoreInfo: th.NewClickable(false),
goBackBtn: th.Button(values.String(values.StrBack)),
nextBtn: th.Button(values.String(values.StrNext)),
materialLoader: material.Loader(th.Base),
existingDEXServer: existingDEXServer,
bondFeeCache: make(map[uint32]uint64),
redirectToMarketPageFn: redirectToMarketPageFn,
}

pg.onBoardingSteps = map[onboardingStep]dexOnboardingStep{
Expand Down Expand Up @@ -260,7 +265,6 @@ func (pg *DEXOnboarding) OnNavigatedFrom() {
// to be eventually drawn on screen.
// Part of the load.Page interface.
func (pg *DEXOnboarding) Layout(gtx C) D {
pg.handleEditorEvents(gtx)
if !pg.AssetsManager.DEXCInitialized() {
pg.ParentNavigator().CloseCurrentPage()
return D{}
Expand Down Expand Up @@ -463,15 +467,15 @@ func (pg *DEXOnboarding) subStepAddServer(gtx C) D {
Alignment: layout.Middle,
}.Layout(gtx,
layout.Rigid(func(gtx C) D {
if !pg.wantCustomServer {
if !pg.wantCustomServer && pg.redirectToMarketPageFn == nil {
return D{}
}

return cryptomaterial.LinearLayout{
Width: cryptomaterial.WrapContent,
Height: cryptomaterial.WrapContent,
Orientation: layout.Horizontal,
Clickable: pg.goBackToChooseServer,
Clickable: pg.goBackToChooseServerOrMarketPage,
}.Layout(gtx,
layout.Rigid(func(gtx C) D {
return pg.Theme.Icons.NavigationArrowBack.Layout(gtx, pg.Theme.Color.Gray1)
Expand Down Expand Up @@ -1047,6 +1051,8 @@ func (pg *DEXOnboarding) handleEditorEvents(gtx C) {
pg.isLoading = false
}()
}

pg.ParentWindow().Reload()
}
}

Expand All @@ -1064,9 +1070,13 @@ func (pg *DEXOnboarding) HandleUserInteractions(gtx C) {
pg.serverCertEditor.Editor.SetText("")
}

if pg.goBackToChooseServer.Clicked(gtx) {
if pg.goBackToChooseServerOrMarketPage.Clicked(gtx) {
pg.wantCustomServer = false
pg.currentStep = onboardingChooseServer
if pg.redirectToMarketPageFn != nil {
pg.redirectToMarketPageFn()
} else {
pg.currentStep = onboardingChooseServer
}
pg.serverURLEditor.SetError("")
pg.serverCertEditor.SetError("")
}
Expand Down Expand Up @@ -1105,6 +1115,8 @@ func (pg *DEXOnboarding) HandleUserInteractions(gtx C) {
if pg.bondSourceAccountSelector != nil {
pg.bondSourceAccountSelector.Handle(gtx)
}

pg.handleEditorEvents(gtx)
}

func (pg *DEXOnboarding) setAddServerStep() {
Expand Down
72 changes: 58 additions & 14 deletions ui/page/dcrdex/market.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,16 @@ type DEXMarketPage struct {
toggleBuyAndSellBtn *cryptomaterial.SegmentedControl
orderTypesDropdown *cryptomaterial.DropDown

priceEditor cryptomaterial.Editor
priceEditor cryptomaterial.Editor
// TODO: Remove switchLotsOrAmount and related checks for amounts input on
// lotsOrAmountEditor. It seems we prefer users learning how to trade with
// lots since it's more straight forward. If we intend to allow users
// provide an amount and convert to lots for them before this todo is done,
// we can just just display this switch instead of the lot size.
switchLotsOrAmount *cryptomaterial.Switch
lotsOrAmountEditor cryptomaterial.Editor
totalEditor cryptomaterial.Editor
lotsInfoBtn *cryptomaterial.Clickable

maxBuyOrSellStr string
orderFeeEstimateStr string
Expand Down Expand Up @@ -155,6 +161,7 @@ func NewDEXMarketPage(l *load.Load, selectServer string) *DEXMarketPage {
priceEditor: newTextEditor(l.Theme, values.String(values.StrPrice), "", false),
switchLotsOrAmount: l.Theme.Switch(),
lotsOrAmountEditor: newTextEditor(l.Theme, values.String(values.StrLots), "", false),
lotsInfoBtn: th.NewClickable(false),
totalEditor: newTextEditor(th, values.String(values.StrTotal), "", false),
maxBuyOrSellStr: "---",
orderFeeEstimateStr: "------",
Expand Down Expand Up @@ -779,10 +786,10 @@ func (pg *DEXMarketPage) orderForm(gtx C) D {
} else {
if sell { // Show base asset available balance.
tradeDirection = values.String(values.StrSell)
availableAssetBal, baseOrQuoteAssetSym = pg.availableWalletAccountBalanceString(false)
availableAssetBal, baseOrQuoteAssetSym = pg.availableWalletAccountBalance(false)
} else {
tradeDirection = values.String(values.StrBuy)
availableAssetBal, baseOrQuoteAssetSym = pg.availableWalletAccountBalanceString(true)
availableAssetBal, baseOrQuoteAssetSym = pg.availableWalletAccountBalance(true)
}
}

Expand Down Expand Up @@ -820,15 +827,28 @@ func (pg *DEXMarketPage) orderForm(gtx C) D {
layout.Rigid(func(gtx C) D {
return layout.Inset{Bottom: dp5}.Layout(gtx, func(gtx C) D {
var labelText string
var lotSize string
if pg.orderWithLots() {
labelText = fmt.Sprintf("%s (%s)", values.String(values.StrLots), lotsOrAmountSubtext)
if mkt := pg.selectedMarketInfo(); mkt != nil {
lotSize = values.StringF(values.StrLotSizeFmt, fmt.Sprintf("%s %s", trimmedConventionalAmtString(mkt.MsgRateToConventional(mkt.LotSize)), convertAssetIDToAssetType(pg.selectedMarketOrderBook.base)))
}
} else {
labelText = fmt.Sprintf("%s (%s)", values.String(values.StrAmount), lotsOrAmountSubtext)
}
return layout.Flex{Axis: horizontal}.Layout(gtx,
layout.Rigid(pg.semiBoldLabelText(labelText).Layout),
layout.Rigid(func(gtx C) D {
return layout.Inset{Top: dp5, Left: dp2}.Layout(gtx, func(gtx C) D {
return pg.lotsInfoBtn.Layout(gtx, pg.Theme.Icons.InfoAction.Layout16dp)
})
}),
layout.Flexed(1, func(gtx C) D {
return layout.E.Layout(gtx, pg.switchLotsOrAmount.Layout)
if lotSize == "" {
return D{}
}

return layout.E.Layout(gtx, pg.Theme.Label(values.TextSize14, lotSize).Layout)
}),
)
})
Expand Down Expand Up @@ -998,10 +1018,9 @@ func trimZeros(s string) string {
return strings.TrimSuffix(strings.TrimRight(s, "0"), ".")
}

// availableWalletAccountBalanceString returns the balance of the DEX wallet
// account for the quote or base asset of the selected market. Returns the
// wallet's spendable balance as string.
func (pg *DEXMarketPage) availableWalletAccountBalanceString(forQuoteAsset bool) (bal float64, assetSym string) {
// availableWalletAccountBalance returns the balance of the DEX wallet account
// for the quote or base asset of the selected market.
func (pg *DEXMarketPage) availableWalletAccountBalance(forQuoteAsset bool) (bal float64, assetSym string) {
if pg.noMarketOrServerDisconnected.Load() {
return 0, ""
}
Expand Down Expand Up @@ -1348,6 +1367,8 @@ func (pg *DEXMarketPage) setBuyOrSell() {
pg.lotsOrAmountEditor.UpdateFocus(!pg.lotsOrAmountEditor.Editor.ReadOnly)
pg.totalEditor.Editor.ReadOnly = isSell
pg.totalEditor.UpdateFocus(!pg.totalEditor.Editor.ReadOnly)
pg.lotsOrAmountEditor.Editor.SetText("")
pg.totalEditor.Editor.SetText("")

if !isSell { // Buy
pg.createOrderBtn.Text = values.String(values.StrBuy)
Expand Down Expand Up @@ -1474,15 +1495,17 @@ func (pg *DEXMarketPage) HandleUserInteractions(gtx C) {
selectedServer := pg.serverSelector.Selected()
xc, err := dexc.Exchange(selectedServer)
if err != nil && xc.Auth.EffectiveTier == 0 /* need to post bond now */ {
pg.ParentNavigator().ClearStackAndDisplay(NewDEXOnboarding(pg.Load, selectedServer))
pg.ParentNavigator().ClearStackAndDisplay(NewDEXOnboarding(pg.Load, selectedServer, nil))
} else {
pg.lastSelectedDEXServer = selectedServer
pg.setServerMarkets()
}
}

if pg.addServerBtn.Clicked(gtx) {
pg.ParentNavigator().ClearStackAndDisplay(NewDEXOnboarding(pg.Load, ""))
pg.ParentNavigator().ClearStackAndDisplay(NewDEXOnboarding(pg.Load, "", func() {
pg.ParentNavigator().ClearStackAndDisplay(NewDEXMarketPage(pg.Load, ""))
}))
}

if pg.openOrdersBtn.Clicked(gtx) {
Expand All @@ -1506,6 +1529,18 @@ func (pg *DEXMarketPage) HandleUserInteractions(gtx C) {
log.Info("button click listener for full order book view is not implemented")
}

if pg.lotsInfoBtn.Clicked(gtx) {
infoModal := modal.NewCustomModal(pg.Load).
Title(values.String(values.StrLots)).
UseCustomWidget(func(gtx layout.Context) layout.Dimensions {
return pg.Theme.Body2(values.String(values.StrLotsExplanation)).Layout(gtx)
}).
SetCancelable(true).
SetContentAlignment(layout.W, layout.W, layout.Center).
SetPositiveButtonText(values.String(values.StrOk))
pg.ParentWindow().ShowModal(infoModal)
}

if pg.immediateOrderInfoBtn.Clicked(gtx) {
infoModal := modal.NewCustomModal(pg.Load).
Title(values.String(values.StrImmediateOrder)).
Expand All @@ -1520,7 +1555,7 @@ func (pg *DEXMarketPage) HandleUserInteractions(gtx C) {

// TODO: postBondBtn should open a separate page when its design is ready.
if pg.postBondBtn.Clicked(gtx) {
pg.ParentNavigator().ClearStackAndDisplay(NewDEXOnboarding(pg.Load, pg.serverSelector.Selected()))
pg.ParentNavigator().ClearStackAndDisplay(NewDEXOnboarding(pg.Load, pg.serverSelector.Selected(), nil))
}

if pg.loginBtn.Clicked(gtx) {
Expand Down Expand Up @@ -1827,9 +1862,16 @@ func anyMatchActive(matches []*core.Match) bool {
func (pg *DEXMarketPage) hasValidOrderInfo() bool {
mkt := pg.selectedMarketInfo()
_, lotsOrAmtOk := pg.orderLotsOrAmt()
_, totalOk := pg.totalOrderAmt()
orderAmt, totalOk := pg.totalOrderAmt()
// TODO: Check that their tier limit has not been exceeded by this trade.
return pg.orderPrice(mkt) > 0 && lotsOrAmtOk && totalOk
orderPriceIsOk := pg.orderPrice(mkt) > 0 && lotsOrAmtOk && totalOk
if !orderPriceIsOk {
return false
}

// Fetch wallet balance from dex and ensure wallet can fund dex order.
walletBalance, _ := pg.availableWalletAccountBalance(!pg.isSellOrder())
return orderPriceIsOk && orderAmt < walletBalance
}

func (pg *DEXMarketPage) orderLotsOrAmt() (float64, bool) {
Expand Down Expand Up @@ -1867,13 +1909,15 @@ func (pg *DEXMarketPage) calculateOrderAmount(mkt *core.Market, isSwitchLotsOrAm
if !pg.isSellOrder() {
amtStr := pg.totalEditor.Editor.Text()
if amtStr == "" {
pg.lotsOrAmountEditor.Editor.SetText("")
return false
}

// It's a buy order, user supplies how much total they want to buy and
// we calculate based on that.
totalAmt, err := strconv.ParseFloat(amtStr, 64)
if err != nil || totalAmt <= 0 {
pg.lotsOrAmountEditor.Editor.SetText("")
pg.totalEditor.SetError(values.String(values.StrInvalidAmount))
return false
}
Expand All @@ -1898,7 +1942,7 @@ func (pg *DEXMarketPage) calculateOrderAmount(mkt *core.Market, isSwitchLotsOrAm

pg.totalEditor.Editor.SetText("")
if pg.orderWithLots() {
if lots, err := strconv.ParseFloat(lotsOrAmtStr, 64); err != nil || lots <= 0 {
if lots, err := strconv.ParseFloat(lotsOrAmtStr, 64); err != nil || lots <= 0 || float64(int64(lots)) != lots {
pg.lotsOrAmountEditor.SetError(values.String(values.StrInvalidLot))
} else {
if isSwitchLotsOrAmtChanged {
Expand Down
4 changes: 3 additions & 1 deletion ui/values/localizable/en.go
Original file line number Diff line number Diff line change
Expand Up @@ -847,8 +847,10 @@ const EN = `
"24hVolume" = "24h Volume (%s)"
"24hHigh" = "24h High"
"lots" = "Lots"
"lotSizeFmt" = "1 Lot = %v"
"lotsExplanation" = "The Lot size is the minimum amount you can buy or sell in one trade. Lot sizes are chosen to minimize the on chain fees to below ~1% of each trade value. If you want to buy or sell 10 (or any number) lots, total order quantity will be -> (number of lots * lots size) denominated in the base currency."
"invalidPrice" = "Invalid price"
"invalidLot" = "Invalid lot"
"invalidLot" = "Invalid lot: Lot must be a valid non-decimal number"
"minMaxLot" = "Min Lots: %d, Max Lots: %d"
"buy" = "Buy"
"sell" = "Sell"
Expand Down
2 changes: 2 additions & 0 deletions ui/values/strings.go
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,8 @@ const (
Str24hVolume = "24hVolume"
Str24hHigh = "24hHigh"
StrLots = "lots"
StrLotSizeFmt = "lotSizeFmt"
StrLotsExplanation = "lotsExplanation"
StrInvalidLot = "invalidLot"
StrInvalidPrice = "invalidPrice"
StrBuy = "buy"
Expand Down
Loading