Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ fun MainScreen(
AssetsAction.Receive -> navigator.openReceive()
AssetsAction.Buy -> navigator.openBuy()
AssetsAction.Swap -> navigator.openSwap()
AssetsAction.Portfolio -> navigator.openPortfolioChart()
AssetsAction.Perpetuals -> navigator.openPerpetuals()
is AssetsAction.OpenPerpetualDetails -> navigator.openPerpetualDetails(action.assetId)
is AssetsAction.OpenAsset -> navigator.openAsset(action.assetId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import com.gemwallet.android.ui.navigation.routes.SupportRoute
import com.gemwallet.android.ui.navigation.routes.SwapPairRoute
import com.gemwallet.android.ui.navigation.routes.SwapRoute
import com.gemwallet.android.ui.navigation.routes.SwapSelectRoute
import com.gemwallet.android.ui.navigation.routes.PortfolioChartRoute
import com.gemwallet.android.ui.navigation.routes.TransactionDetailsRoute
import com.gemwallet.android.ui.navigation.routes.WalletDetailsRoute
import com.gemwallet.android.ui.navigation.routes.WalletImageRoute
Expand All @@ -77,6 +78,7 @@ import com.gemwallet.android.ui.navigation.routes.WalletsRoute
import com.gemwallet.android.ext.toIdentifier
import com.wallet.core.primitives.AssetId
import com.wallet.core.primitives.NFTAssetId
import com.wallet.core.primitives.PortfolioType
import com.wallet.core.primitives.TransactionId
import com.wallet.core.primitives.WalletId
import com.wallet.core.primitives.WalletType
Expand Down Expand Up @@ -189,6 +191,7 @@ class WalletNavigator(
fun openAddAsset() = push(AddAssetRoute)
fun openAsset(assetId: AssetId) = push(AssetRoute(assetId))
fun openAssetChart(assetId: AssetId) = push(AssetChartRoute(assetId))
fun openPortfolioChart(type: PortfolioType = PortfolioType.Wallet) = push(PortfolioChartRoute(type))
fun openTransaction(transactionId: TransactionId) = push(TransactionDetailsRoute(transactionId))
fun openBridgeConnections() = push(BridgeConnectionsRoute)
fun openBridgeConnectionDetails(connectionId: String) = push(BridgeConnectionDetailsRoute(connectionId))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import com.gemwallet.android.ui.navigation.routes.confirm
import com.gemwallet.android.ui.navigation.routes.fiatScreen
import com.gemwallet.android.ui.navigation.routes.nftCollection
import com.gemwallet.android.ui.navigation.routes.perpetualScreen
import com.gemwallet.android.ui.navigation.routes.portfolioChartScreen
import com.gemwallet.android.ui.navigation.routes.receiveScreen
import com.gemwallet.android.ui.navigation.routes.recipientInput
import com.gemwallet.android.ui.navigation.routes.referral
Expand All @@ -56,6 +57,7 @@ import com.gemwallet.android.ui.navigation.routes.transactionDetailsScreen
import com.gemwallet.android.ui.navigation.routes.walletScreen
import com.gemwallet.android.ui.navigation.routes.walletSearchScreen
import com.gemwallet.android.ui.navigation.routes.walletsScreen
import com.wallet.core.primitives.PortfolioType
import com.wallet.core.primitives.WalletId

@OptIn(ExperimentalComposeUiApi::class)
Expand Down Expand Up @@ -131,6 +133,10 @@ fun WalletNavGraph(
onCancel = onCancel,
)

portfolioChartScreen(
onCancel = onCancel,
)

swap(
navigator = navigator,
onConfirm = navigator::openConfirm,
Expand Down Expand Up @@ -290,6 +296,7 @@ fun WalletNavGraph(

perpetualScreen(
onOpenPerpetualDetails = navigator::openPerpetualDetails,
onOpenPortfolio = { navigator.openPortfolioChart(PortfolioType.Perpetuals) },
amountAction = AmountTransactionAction(navigator::openAmount),
confirmAction = ConfirmTransactionAction(navigator::openConfirm),
onCancel = onCancel,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ data class PerpetualPositionRoute(val assetId: AssetId) : NavKey
fun EntryProviderScope<NavKey>.perpetualScreen(
onCancel: () -> Unit,
onOpenPerpetualDetails: AssetIdAction,
onOpenPortfolio: () -> Unit,
amountAction: AmountTransactionAction,
confirmAction: ConfirmTransactionAction,
onTransaction: (TransactionId) -> Unit,
) {
entry<PerpetualRoute> {
PerpetualMarketNavScreen(
onOpenPerpetualDetails = onOpenPerpetualDetails,
onOpenPortfolio = onOpenPortfolio,
amountAction = amountAction,
onCancel = onCancel,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.gemwallet.android.ui.navigation.routes

import androidx.navigation3.runtime.EntryProviderScope
import androidx.navigation3.runtime.NavKey
import com.gemwallet.android.features.asset.presents.chart.PortfolioChartScene
import com.gemwallet.android.ui.models.navigation.RouteArgument
import com.gemwallet.android.ui.navigation.routeArguments
import com.wallet.core.primitives.PortfolioType
import kotlinx.serialization.Serializable

@Serializable
data class PortfolioChartRoute(val type: PortfolioType = PortfolioType.Wallet) : NavKey

fun EntryProviderScope<NavKey>.portfolioChartScreen(
onCancel: () -> Unit,
) {
entry<PortfolioChartRoute>(
metadata = { key -> routeArguments(RouteArgument.Type to key.type) },
) {
PortfolioChartScene(onCancel = onCancel)
}
}
Original file line number Diff line number Diff line change
@@ -1,29 +1,38 @@
package com.gemwallet.android.blockchain.gemstone

import com.gemwallet.android.domains.asset.toDTO
import com.gemwallet.android.ext.secondsToMillis
import com.gemwallet.android.ext.toAssetId
import com.gemwallet.android.ext.toPerpetualId
import com.wallet.core.primitives.ChartCandleStick
import com.wallet.core.primitives.ChartCandleUpdate
import com.wallet.core.primitives.ChartDateValue
import com.wallet.core.primitives.Perpetual
import com.wallet.core.primitives.PerpetualAccountSummary
import com.wallet.core.primitives.PerpetualBalance
import com.wallet.core.primitives.PerpetualData
import com.wallet.core.primitives.PerpetualDirection
import com.wallet.core.primitives.PerpetualMarginType
import com.wallet.core.primitives.PerpetualMarketData
import com.wallet.core.primitives.PerpetualMetadata
import com.wallet.core.primitives.PerpetualOrderType
import com.wallet.core.primitives.PerpetualPortfolio
import com.wallet.core.primitives.PerpetualPortfolioTimeframeData
import com.wallet.core.primitives.PerpetualPosition
import com.wallet.core.primitives.PerpetualPositionsSummary
import com.wallet.core.primitives.PerpetualProvider
import com.wallet.core.primitives.PerpetualTriggerOrder
import uniffi.gemstone.GemChartCandleStick
import uniffi.gemstone.GemChartCandleUpdate
import uniffi.gemstone.GemChartDateValue
import uniffi.gemstone.GemPerpetualAccountSummary
import uniffi.gemstone.GemPerpetualBalance
import uniffi.gemstone.GemPerpetualData
import uniffi.gemstone.GemPerpetualMarginType
import uniffi.gemstone.GemPerpetualMarketData
import uniffi.gemstone.GemPerpetualOrderType
import uniffi.gemstone.GemPerpetualPortfolio
import uniffi.gemstone.GemPerpetualPortfolioTimeframeData
import uniffi.gemstone.GemPerpetualPosition
import uniffi.gemstone.GemPerpetualPositionsSummary
import uniffi.gemstone.GemPerpetualTriggerOrder
Expand Down Expand Up @@ -126,6 +135,40 @@ fun GemChartCandleStick.toDTO(): ChartCandleStick {
)
}

private fun GemChartDateValue.toDTO(): ChartDateValue {
return ChartDateValue(
date = date.secondsToMillis(),
value = value,
)
}

private fun GemPerpetualAccountSummary.toDTO(): PerpetualAccountSummary {
return PerpetualAccountSummary(
accountValue = accountValue,
accountLeverage = accountLeverage,
marginUsage = marginUsage,
unrealizedPnl = unrealizedPnl,
)
}

private fun GemPerpetualPortfolioTimeframeData.toDTO(): PerpetualPortfolioTimeframeData {
return PerpetualPortfolioTimeframeData(
accountValueHistory = accountValueHistory.map { it.toDTO() },
pnlHistory = pnlHistory.map { it.toDTO() },
volume = volume,
)
}

fun GemPerpetualPortfolio.toDTO(): PerpetualPortfolio {
return PerpetualPortfolio(
day = day?.toDTO(),
week = week?.toDTO(),
month = month?.toDTO(),
allTime = allTime?.toDTO(),
accountSummary = accountSummary?.toDTO(),
)
}

private fun GemPerpetualTriggerOrder.toDTO(): PerpetualTriggerOrder {
return PerpetualTriggerOrder(
price = price,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.wallet.core.primitives.Chain
import com.wallet.core.primitives.ChartCandleStick
import com.wallet.core.primitives.ChartPeriod
import com.wallet.core.primitives.PerpetualData
import com.wallet.core.primitives.PerpetualPortfolio
import com.wallet.core.primitives.PerpetualPositionsSummary
import uniffi.gemstone.GemGateway

Expand Down Expand Up @@ -32,4 +33,7 @@ class PerpetualService(
suspend fun getCandleSticks(chain: Chain = Chain.HyperCore, symbol: String, period: ChartPeriod): List<ChartCandleStick> {
return gateway.getPerpetualCandlesticks(chain.string, symbol, period.string).map { it.toDTO() }
}

suspend fun getPortfolio(chain: Chain = Chain.HyperCore, address: String): PerpetualPortfolio =
gateway.getPerpetualPortfolio(chain.string, address).toDTO()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package com.gemwallet.android.data.coordinators.asset

import com.gemwallet.android.application.assets.coordinators.GetPortfolioData
import com.gemwallet.android.application.assets.coordinators.walletChartPeriods
import com.gemwallet.android.blockchain.services.PerpetualService
import com.gemwallet.android.data.repositories.assets.AssetsRepository
import com.gemwallet.android.data.repositories.session.SessionRepository
import com.gemwallet.android.data.services.gemapi.GemDeviceApiClient
import com.gemwallet.android.ext.hyperliquidAccount
import com.gemwallet.android.ext.secondsToMillis
import com.gemwallet.android.model.getTotalAmount
import com.wallet.core.primitives.ChartDateValue
import com.wallet.core.primitives.ChartPeriod
import com.wallet.core.primitives.Currency
import com.wallet.core.primitives.PerpetualPortfolio
import com.wallet.core.primitives.PerpetualPortfolioTimeframeData
import com.wallet.core.primitives.PortfolioAsset
import com.wallet.core.primitives.PortfolioAssetsRequest
import com.wallet.core.primitives.PortfolioChartData
import com.wallet.core.primitives.PortfolioChartType
import com.wallet.core.primitives.PortfolioData
import com.wallet.core.primitives.PortfolioMarginUsage
import com.wallet.core.primitives.PortfolioStatistic
import com.wallet.core.primitives.PortfolioType
import kotlinx.coroutines.flow.firstOrNull
import java.math.BigInteger

class GetPortfolioDataImpl(
private val gemDeviceApiClient: GemDeviceApiClient,
private val assetsRepository: AssetsRepository,
private val perpetualService: PerpetualService,
private val sessionRepository: SessionRepository,
) : GetPortfolioData {

override suspend fun getPortfolioData(
type: PortfolioType,
period: ChartPeriod,
currency: Currency,
): PortfolioData = when (type) {
PortfolioType.Wallet -> getWalletData(period, currency)
PortfolioType.Perpetuals -> getPerpetualData(period)
}

private suspend fun getWalletData(period: ChartPeriod, currency: Currency): PortfolioData {
val rate = checkNotNull(assetsRepository.getCurrencyRate(currency).firstOrNull()?.rate) {
"Missing currency rate for ${currency.string}"
}
val assets = assetsRepository.getAssetsInfo().firstOrNull().orEmpty()
.mapNotNull { assetInfo ->
val total = assetInfo.balance.balance.getTotalAmount()
if (total <= BigInteger.ZERO) return@mapNotNull null
PortfolioAsset(assetId = assetInfo.asset.id, value = total.toString())
}
val portfolio = gemDeviceApiClient.getPortfolioAssets(
period = period.string,
request = PortfolioAssetsRequest(assets = assets),
)
val values = portfolio.values
.sortedBy { it.timestamp }
.map { ChartDateValue(date = it.timestamp.toLong().secondsToMillis(), value = it.value * rate) }
val statistics = listOfNotNull(
portfolio.allTimeHigh?.let { PortfolioStatistic.AllTimeHigh(it) },
portfolio.allTimeLow?.let { PortfolioStatistic.AllTimeLow(it) },
)
return PortfolioData(
charts = listOf(PortfolioChartData(chartType = PortfolioChartType.Value, values = values)),
statistics = statistics,
availablePeriods = walletChartPeriods,
)
}

private suspend fun getPerpetualData(period: ChartPeriod): PortfolioData {
val address = checkNotNull(sessionRepository.session().value?.wallet?.hyperliquidAccount?.address) {
"Perpetual account is not available"
}
val portfolio = perpetualService.getPortfolio(address = address)
val timeframe = portfolio.timeframeData(period)

val charts = listOf(
PortfolioChartData(chartType = PortfolioChartType.Pnl, values = timeframe?.pnlHistory.orEmpty()),
PortfolioChartData(
chartType = PortfolioChartType.Value,
values = timeframe?.accountValueHistory.orEmpty().dropWhile { it.value == 0.0 },
),
)
val statistics = buildList {
portfolio.accountSummary?.let { summary ->
add(PortfolioStatistic.UnrealizedPnl(summary.unrealizedPnl))
add(PortfolioStatistic.AccountLeverage(summary.accountLeverage))
add(PortfolioStatistic.MarginUsage(PortfolioMarginUsage(accountValue = summary.accountValue, usage = summary.marginUsage)))
}
portfolio.allTime?.let { allTime ->
allTime.pnlHistory.lastOrNull()?.let { add(PortfolioStatistic.AllTimePnl(it.value)) }
add(PortfolioStatistic.Volume(allTime.volume))
}
}
return PortfolioData(charts = charts, statistics = statistics, availablePeriods = portfolio.availablePeriods())
}
}

private fun PerpetualPortfolio.availablePeriods(): List<ChartPeriod> = listOfNotNull(
day?.let { ChartPeriod.Day },
week?.let { ChartPeriod.Week },
month?.let { ChartPeriod.Month },
allTime?.let { ChartPeriod.Year },
allTime?.let { ChartPeriod.All },
)

private fun PerpetualPortfolio.timeframeData(period: ChartPeriod): PerpetualPortfolioTimeframeData? = when (period) {
ChartPeriod.Hour, ChartPeriod.Day -> day
ChartPeriod.Week -> week
ChartPeriod.Month -> month
ChartPeriod.Year, ChartPeriod.All -> allTime
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import com.gemwallet.android.application.assets.coordinators.GetChartPeriod
import com.gemwallet.android.application.assets.coordinators.GetHideBalancesState
import com.gemwallet.android.application.assets.coordinators.GetImportInProgress
import com.gemwallet.android.application.assets.coordinators.GetSearchLists
import com.gemwallet.android.application.assets.coordinators.GetPortfolioData
import com.gemwallet.android.application.assets.coordinators.GetShowWelcomeBanner
import com.gemwallet.android.application.assets.coordinators.GetWalletSummary
import com.gemwallet.android.application.assets.coordinators.EnsureWalletAssets
Expand All @@ -27,6 +28,7 @@ import com.gemwallet.android.application.assets.coordinators.SyncAssets
import com.gemwallet.android.application.assets.coordinators.ToggleAssetPin
import com.gemwallet.android.application.assets.coordinators.ToggleHideBalances
import com.gemwallet.android.application.wallet_import.coordinators.GetImportWalletState
import com.gemwallet.android.blockchain.services.PerpetualService
import com.gemwallet.android.cases.banners.HasMultiSign
import com.gemwallet.android.cases.tokens.SyncAssetPrices
import com.gemwallet.android.data.coordinators.asset.EnableAssetImpl
Expand All @@ -42,6 +44,7 @@ import com.gemwallet.android.data.coordinators.asset.GetChartPeriodImpl
import com.gemwallet.android.data.coordinators.asset.GetHideBalancesStateImpl
import com.gemwallet.android.data.coordinators.asset.GetImportInProgressImpl
import com.gemwallet.android.data.coordinators.asset.GetSearchListsImpl
import com.gemwallet.android.data.coordinators.asset.GetPortfolioDataImpl
import com.gemwallet.android.data.coordinators.asset.GetShowWelcomeBannerImpl
import com.gemwallet.android.data.coordinators.asset.GetWalletSummaryImpl
import com.gemwallet.android.data.coordinators.asset.DeviceAssetsSyncService
Expand All @@ -62,6 +65,7 @@ import com.gemwallet.android.data.repositories.perpetual.PerpetualRepository
import com.gemwallet.android.data.repositories.session.SessionRepository
import com.gemwallet.android.data.repositories.stream.StreamSubscriptionService
import com.gemwallet.android.data.services.gemapi.GemApiClient
import com.gemwallet.android.data.services.gemapi.GemDeviceApiClient
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
Expand Down Expand Up @@ -155,6 +159,20 @@ object AssetModule {
assetsRepository = assetsRepository,
)

@Provides
@Singleton
fun provideGetPortfolioData(
gemDeviceApiClient: GemDeviceApiClient,
assetsRepository: AssetsRepository,
perpetualService: PerpetualService,
sessionRepository: SessionRepository,
): GetPortfolioData = GetPortfolioDataImpl(
gemDeviceApiClient = gemDeviceApiClient,
assetsRepository = assetsRepository,
perpetualService = perpetualService,
sessionRepository = sessionRepository,
)

@Provides
@Singleton
fun providePrefetchAssets(
Expand Down
Loading
Loading