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

Implement total USD asset balance #131

Merged
merged 3 commits into from
Oct 2, 2023
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
1 change: 1 addition & 0 deletions libwallet/assets/wallet/wallet_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ const (
KnownDexServersConfigKey = "known_dex_servers"
LanguagePreferenceKey = "app_language"
DarkModeConfigKey = "dark_mode"
HideTotalBalanceConfigKey = "hideTotalUSDBalance"

PassphraseTypePin int32 = 0
PassphraseTypePass int32 = 1
Expand Down
12 changes: 12 additions & 0 deletions libwallet/assets_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,18 @@ func (mgr *AssetsManager) ClearExchangeConfig() {
mgr.db.DeleteWalletConfigValue(sharedW.ExchangeSourceDstnTypeConfigKey)
}

// IsTotalBalanceVisible checks if the total balance visibility is set.
func (mgr *AssetsManager) IsTotalBalanceVisible() bool {
var data bool
mgr.db.ReadWalletConfigValue(sharedW.HideTotalBalanceConfigKey, &data)
return data
}

// SetTotalBalanceVisibility sets the transaction notifications for the wallet.
func (mgr *AssetsManager) SetTotalBalanceVisibility(data bool) {
mgr.db.SaveWalletConfigValue(sharedW.HideTotalBalanceConfigKey, data)
}

func genKey(prefix, identifier interface{}) string {
return fmt.Sprintf("%v-%v", prefix, identifier)
}
2 changes: 1 addition & 1 deletion ui/modal/info_modal_layouts.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ func sourceModalInfo(th *cryptomaterial.Theme) []layout.Widget {
}

func totalValueInfo(th *cryptomaterial.Theme) []layout.Widget {
text := values.StringF(values.StrTotalValueMsg, `<br />`)
text := values.StringF(values.StrTotalValueMsg, `<br />`, `<br />`)
return []layout.Widget{
renderers.RenderHTML(text, th).Layout,
}
Expand Down
73 changes: 73 additions & 0 deletions ui/page/components/components.go
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,79 @@ func CalculateTotalWalletsBalance(l *load.Load) (*CummulativeWalletsBalance, err
return cumm, nil
}

func calculateTotalAssetsBalance(l *load.Load) (map[libutils.AssetType]int64, error) {
wallets := l.WL.AssetsManager.AllWallets()
assetsTotalBalance := make(map[libutils.AssetType]int64)

for _, wal := range wallets {
if wal.IsWatchingOnlyWallet() {
continue
}

accountsResult, err := wal.GetAccountsRaw()
if err != nil {
return nil, err
}

for _, account := range accountsResult.Accounts {
assetsTotalBalance[wal.GetAssetType()] += account.Balance.Total.ToInt()
}
}

return assetsTotalBalance, nil
}

func CalculateTotalAssetsBalance(l *load.Load) (map[libutils.AssetType]sharedW.AssetAmount, error) {
balances, err := calculateTotalAssetsBalance(l)
if err != nil {
return nil, err
}

assetsTotalBalance := make(map[libutils.AssetType]sharedW.AssetAmount)
for assetType, balance := range balances {
switch assetType {
case libutils.BTCWalletAsset:
assetsTotalBalance[assetType] = l.WL.AssetsManager.AllBTCWallets()[0].ToAmount(balance)
case libutils.DCRWalletAsset:
assetsTotalBalance[assetType] = l.WL.AssetsManager.AllDCRWallets()[0].ToAmount(balance)
case libutils.LTCWalletAsset:
assetsTotalBalance[assetType] = l.WL.AssetsManager.AllLTCWallets()[0].ToAmount(balance)
default:
return nil, fmt.Errorf("Unsupported asset type: %s", assetType)
}
}

return assetsTotalBalance, nil
}

func CalculateAssetsUSDBalance(l *load.Load, assetsTotalBalance map[libutils.AssetType]sharedW.AssetAmount) (map[libutils.AssetType]float64, error) {
preferredExchange := l.WL.AssetsManager.GetCurrencyConversionExchange()

usdBalance := func(bal sharedW.AssetAmount, market string) (float64, error) {
rate, err := l.WL.AssetsManager.ExternalService.GetTicker(preferredExchange, market)
if err != nil {
return 0, err
}

return bal.MulF64(rate.LastTradePrice).ToCoin(), nil
}

assetsTotalUSDBalance := make(map[libutils.AssetType]float64)
for assetType, balance := range assetsTotalBalance {
marketValue, exist := values.AssetExchangeMarketValue[assetType]
if !exist {
return nil, fmt.Errorf("Unsupported asset type: %s", assetType)
}
usdBal, err := usdBalance(balance, marketValue)
if err != nil {
return nil, err
}
assetsTotalUSDBalance[assetType] = usdBal
}

return assetsTotalUSDBalance, nil
}

// SecondsToDays takes time in seconds and returns its string equivalent in the format ddhhmm.
func SecondsToDays(totalTimeLeft int64) string {
q, r := divMod(totalTimeLeft, 24*60*60)
Expand Down
51 changes: 37 additions & 14 deletions ui/page/root/home_page.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/crypto-power/cryptopower/ui/page/components"
"github.com/crypto-power/cryptopower/ui/page/send"
"github.com/crypto-power/cryptopower/ui/page/settings"
"github.com/crypto-power/cryptopower/ui/utils"
"github.com/crypto-power/cryptopower/ui/values"
)

Expand All @@ -31,7 +32,6 @@ type HomePage struct {
ctxCancel context.CancelFunc
drawerNav components.NavDrawer

totalUSDValueSwitch *cryptomaterial.Switch
navigationTab *cryptomaterial.Tab
appLevelSettingsButton *cryptomaterial.Clickable
appNotificationButton *cryptomaterial.Clickable
Expand All @@ -41,6 +41,7 @@ type HomePage struct {

// page state variables
isBalanceHidden bool
totalBalanceUSD string
}

var navigationTabTitles = []string{
Expand All @@ -64,8 +65,6 @@ func NewHomePage(l *load.Load) *HomePage {
_, hp.infoButton = components.SubpageHeaderButtons(l)
hp.infoButton.Size = values.MarginPadding15

hp.totalUSDValueSwitch = hp.Theme.Switch()

hp.drawerNav = components.NavDrawer{
Load: hp.Load,
CurrentPage: hp.CurrentPageID(),
Expand Down Expand Up @@ -102,11 +101,13 @@ func (hp *HomePage) ID() string {
func (hp *HomePage) OnNavigatedTo() {
hp.ctx, hp.ctxCancel = context.WithCancel(context.TODO())

go hp.CalculateAssetsUSDBalance()

if hp.CurrentPage() == nil {
hp.Display(NewOverviewPage(hp.Load))
}

hp.totalUSDValueSwitch.SetChecked(true)
hp.isBalanceHidden = hp.WL.AssetsManager.IsTotalBalanceVisible()
}

// OnDarkModeChanged is triggered whenever the dark mode setting is changed
Expand Down Expand Up @@ -185,13 +186,8 @@ func (hp *HomePage) HandleUserInteractions() {
}

for hp.hideBalanceButton.Clicked() {
// TODO use assetManager config settings
hp.isBalanceHidden = !hp.isBalanceHidden
}

if hp.totalUSDValueSwitch.Changed() {
// TODO use assetManager config settings
hp.totalUSDValueSwitch.SetChecked(hp.totalUSDValueSwitch.IsChecked())
hp.WL.AssetsManager.SetTotalBalanceVisibility(hp.isBalanceHidden)
}
}

Expand Down Expand Up @@ -219,6 +215,10 @@ func (hp *HomePage) HandleKeyPress(evt *key.Event) {
}
}

func (hp *HomePage) OnCurrencyChanged() {
go hp.CalculateAssetsUSDBalance()
}

// OnNavigatedFrom is called when the page is about to be removed from
// the displayed window. This method should ideally be used to disable
// features that are irrelevant when the page is NOT displayed.
Expand Down Expand Up @@ -327,7 +327,7 @@ func (hp *HomePage) totalBalanceLayout(gtx C) D {
}

func (hp *HomePage) balanceLayout(gtx C) D {
if hp.totalUSDValueSwitch.IsChecked() {
if components.IsFetchExchangeRateAPIAllowed(hp.WL) && hp.totalBalanceUSD != "" {
return layout.Flex{}.Layout(gtx,
layout.Rigid(hp.LayoutUSDBalance),
layout.Rigid(func(gtx C) D {
Expand All @@ -348,10 +348,10 @@ func (hp *HomePage) balanceLayout(gtx C) D {

// TODO: use real values
func (hp *HomePage) LayoutUSDBalance(gtx C) D {
lblText := hp.Theme.Label(values.TextSize30, "$0.00")
lblText := hp.Theme.Label(values.TextSize30, hp.totalBalanceUSD)

if hp.isBalanceHidden {
lblText = hp.Theme.Label(values.TextSize24, "********")
lblText = hp.Theme.Label(values.TextSize24, "******")
}
inset := layout.Inset{Right: values.MarginPadding8}
return inset.Layout(gtx, lblText.Layout)
Expand All @@ -374,7 +374,6 @@ func (hp *HomePage) totalBalanceTextAndIconButtonLayout(gtx C) D {
Right: values.MarginPadding10,
}.Layout(gtx, hp.infoButton.Layout)
}),
layout.Rigid(hp.totalUSDValueSwitch.Layout),
)
}

Expand Down Expand Up @@ -405,3 +404,27 @@ func (hp *HomePage) notificationSettingsLayout(gtx C) D {
}),
)
}

func (hp *HomePage) CalculateAssetsUSDBalance() {
if components.IsFetchExchangeRateAPIAllowed(hp.WL) {
assetsBalance, err := components.CalculateTotalAssetsBalance(hp.Load)
if err != nil {
log.Error(err)
return
}

assetsTotalUSDBalance, err := components.CalculateAssetsUSDBalance(hp.Load, assetsBalance)
if err != nil {
log.Error(err)
return
}

var totalBalance float64
for _, balance := range assetsTotalUSDBalance {
totalBalance += balance
}

hp.totalBalanceUSD = utils.FormatUSDBalance(hp.Printer, totalBalance)
hp.ParentWindow().Reload()
}
}
66 changes: 18 additions & 48 deletions ui/page/root/overview_page.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,8 @@ func (pg *OverviewPage) ID() string {
func (pg *OverviewPage) OnNavigatedTo() {
pg.ctx, pg.ctxCancel = context.WithCancel(context.TODO())

pg.updateSliders()
go pg.fetchExchangeRate()
pg.updateAssetsSliders()
go pg.updateAssetsUSDBalance()

pg.proposalItems = components.LoadProposals(pg.Load, libwallet.ProposalCategoryAll, 0, 3, true)
pg.orders = components.LoadOrders(pg.Load, 0, 3, true)
Expand Down Expand Up @@ -196,7 +196,7 @@ func (pg *OverviewPage) OnNavigatedFrom() {
}

func (pg *OverviewPage) OnCurrencyChanged() {
go pg.fetchExchangeRate()
go pg.updateAssetsUSDBalance()
}

// Layout draws the page UI components into the provided layout context
Expand Down Expand Up @@ -689,51 +689,26 @@ func (pg *OverviewPage) recentProposal(gtx C) D {
})
}

func (pg *OverviewPage) calculateTotalAssetBalance() (map[libutils.AssetType]int64, error) {
wallets := pg.WL.AssetsManager.AllWallets()
assetsTotalBalance := make(map[libutils.AssetType]int64)

for _, wal := range wallets {
if wal.IsWatchingOnlyWallet() {
continue
}

accountsResult, err := wal.GetAccountsRaw()
func (pg *OverviewPage) updateAssetsUSDBalance() {
if components.IsFetchExchangeRateAPIAllowed(pg.WL) {
assetsTotalUSDBalance, err := components.CalculateAssetsUSDBalance(pg.Load, pg.assetsTotalBalance)
if err != nil {
return nil, err
}

for _, account := range accountsResult.Accounts {
assetsTotalBalance[wal.GetAssetType()] += account.Balance.Total.ToInt()
log.Error(err)
return
}
}

return assetsTotalBalance, nil
}

func (pg *OverviewPage) fetchExchangeRate() {
if components.IsFetchExchangeRateAPIAllowed(pg.WL) {
preferredExchange := pg.WL.AssetsManager.GetCurrencyConversionExchange()

usdBalance := func(bal sharedW.AssetAmount, market string) string {
rate, err := pg.WL.AssetsManager.ExternalService.GetTicker(preferredExchange, market)
if err != nil {
log.Error(err)
return "$--"
}

balanceInUSD := bal.MulF64(rate.LastTradePrice).ToCoin()
return utils.FormatUSDBalance(pg.Printer, balanceInUSD)
toUSDString := func(balance float64) string {
return utils.FormatUSDBalance(pg.Printer, balance)
}

for assetType, balance := range pg.assetsTotalBalance {
for assetType, balance := range assetsTotalUSDBalance {
switch assetType {
case libutils.DCRWalletAsset:
pg.dcr.totalBalanceUSD = usdBalance(balance, values.DCRUSDTMarket)
pg.dcr.totalBalanceUSD = toUSDString(balance)
case libutils.BTCWalletAsset:
pg.btc.totalBalanceUSD = usdBalance(balance, values.BTCUSDTMarket)
pg.btc.totalBalanceUSD = toUSDString(balance)
case libutils.LTCWalletAsset:
pg.ltc.totalBalanceUSD = usdBalance(balance, values.LTCUSDTMarket)
pg.ltc.totalBalanceUSD = toUSDString(balance)
default:
log.Errorf("Unsupported asset type: %s", assetType)
return
Expand All @@ -745,12 +720,13 @@ func (pg *OverviewPage) fetchExchangeRate() {
}
}

func (pg *OverviewPage) updateSliders() {
assetItems, err := pg.calculateTotalAssetBalance()
func (pg *OverviewPage) updateAssetsSliders() {
assetsBalance, err := components.CalculateTotalAssetsBalance(pg.Load)
if err != nil {
log.Error(err)
return
}
pg.assetsTotalBalance = assetsBalance

sliderItem := func(totalBalance sharedW.AssetAmount, assetFullName string, icon, bkgImage *cryptomaterial.Image) *assetBalanceSliderItem {
return &assetBalanceSliderItem{
Expand All @@ -762,22 +738,16 @@ func (pg *OverviewPage) updateSliders() {
}
}

for assetType, totalBalance := range assetItems {
for assetType, balance := range assetsBalance {
assetFullName := strings.ToUpper(assetType.ToFull())

switch assetType {
case libutils.BTCWalletAsset:
balance := pg.WL.AssetsManager.AllBTCWallets()[0].ToAmount(totalBalance)
pg.btc = sliderItem(balance, assetFullName, pg.Theme.Icons.BTCGroupIcon, pg.Theme.Icons.BTCBackground)
pg.assetsTotalBalance[assetType] = balance
case libutils.DCRWalletAsset:
balance := pg.WL.AssetsManager.AllDCRWallets()[0].ToAmount(totalBalance)
pg.dcr = sliderItem(balance, assetFullName, pg.Theme.Icons.DCRGroupIcon, pg.Theme.Icons.DCRBackground)
pg.assetsTotalBalance[assetType] = balance
case libutils.LTCWalletAsset:
balance := pg.WL.AssetsManager.AllLTCWallets()[0].ToAmount(totalBalance)
pg.ltc = sliderItem(balance, assetFullName, pg.Theme.Icons.LTCGroupIcon, pg.Theme.Icons.LTCBackground)
pg.assetsTotalBalance[assetType] = balance
default:
log.Errorf("Unsupported asset type: %s", assetType)
return
Expand Down
9 changes: 9 additions & 0 deletions ui/values/arrays.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package values

import "github.com/crypto-power/cryptopower/libwallet/utils"

const (
DefaultExchangeValue = "none"
DCRUSDTMarket = "DCR-USDT"
Expand All @@ -8,3 +10,10 @@ const (
BittrexExchange = "bittrex"
BinanceExchange = "binance"
)

// initialize an asset market value map
var AssetExchangeMarketValue = map[utils.AssetType]string{
utils.DCRWalletAsset: DCRUSDTMarket,
utils.BTCWalletAsset: BTCUSDTMarket,
utils.LTCWalletAsset: LTCUSDTMarket,
}
Loading
Loading