From bdd841b6327e1988a34ceb217f9c55ab75d914cf Mon Sep 17 00:00:00 2001
From: TZGyn
Date: Mon, 25 May 2026 21:57:44 +0800
Subject: [PATCH] Add gas price display (Gwei, native, USD) under native
balance per network
---
apps/desktop/src/bun/index.ts | 45 +++++++++-
.../src/lib/components/EvmDashboard.svelte | 87 ++++++++++++++++---
apps/desktop/src/lib/rpc-schema.ts | 1 +
3 files changed, 116 insertions(+), 17 deletions(-)
diff --git a/apps/desktop/src/bun/index.ts b/apps/desktop/src/bun/index.ts
index 81a8e6a..70bd3ff 100644
--- a/apps/desktop/src/bun/index.ts
+++ b/apps/desktop/src/bun/index.ts
@@ -21,6 +21,7 @@ import { db } from './lib/db'
import { join } from 'path'
import { getENV } from './lib/get-env'
import { getClient } from './lib/viem'
+import { formatGwei } from 'viem'
import {
isBinaryInstalled,
downloadBinary,
@@ -502,13 +503,20 @@ const rpc = BrowserView.defineRPC({
if (symbols && symbols.length > 0) {
const url = `https://api.g.alchemy.com/prices/v1/${key}/tokens/by-symbol?${symbols.map((s) => `symbols=${encodeURIComponent(s)}`).join('&')}`
- const res = await fetch(url, { signal: AbortSignal.timeout(10000) })
+ const res = await fetch(url, {
+ signal: AbortSignal.timeout(10000),
+ })
if (res.ok) {
const body = await res.json()
const data = (body.data ?? []) as any[]
for (const d of data) {
for (const p of d.prices ?? []) {
- results.push({ symbol: d.symbol, currency: p.currency, value: p.value, lastUpdatedAt: p.lastUpdatedAt })
+ results.push({
+ symbol: d.symbol,
+ currency: p.currency,
+ value: p.value,
+ lastUpdatedAt: p.lastUpdatedAt,
+ })
}
}
}
@@ -527,7 +535,14 @@ const rpc = BrowserView.defineRPC({
const data = (body.data ?? []) as any[]
for (const d of data) {
for (const p of d.prices ?? []) {
- results.push({ symbol: d.symbol, currency: p.currency, value: p.value, lastUpdatedAt: p.lastUpdatedAt, network: d.network, address: d.address })
+ results.push({
+ symbol: d.symbol,
+ currency: p.currency,
+ value: p.value,
+ lastUpdatedAt: p.lastUpdatedAt,
+ network: d.network,
+ address: d.address,
+ })
}
}
}
@@ -540,6 +555,30 @@ const rpc = BrowserView.defineRPC({
return []
}
},
+ fetchGasPrice: async ({ chainid }) => {
+ const key = await Bun.secrets.get({
+ service: 'koins',
+ name: 'alchemy_key',
+ })
+ if (!key) {
+ console.log('[gas] no key')
+ return null
+ }
+ try {
+ const client = getClient(chainid, key)
+ const gasPrice = await client.getGasPrice()
+ console.log(
+ '[gas] price for chain',
+ chainid,
+ ':',
+ gasPrice.toString(),
+ )
+ return formatGwei(gasPrice)
+ } catch (error) {
+ console.log('[gas] error:', error)
+ return null
+ }
+ },
fetchTransactionDetails: async ({ hash, chainid, address }) => {
const key = await Bun.secrets.get({
service: 'koins',
diff --git a/apps/desktop/src/lib/components/EvmDashboard.svelte b/apps/desktop/src/lib/components/EvmDashboard.svelte
index ba5c253..af734a9 100644
--- a/apps/desktop/src/lib/components/EvmDashboard.svelte
+++ b/apps/desktop/src/lib/components/EvmDashboard.svelte
@@ -92,7 +92,10 @@
value: string
currency: string
} | null>(null)
- let tokenPrices = $state>({})
+ let tokenPrices = $state<
+ Record
+ >({})
+ let gasPrice = $state(null)
$effect(() => {
const syms: string[] = w.symbol ? [w.symbol] : []
@@ -107,16 +110,30 @@
}
if (syms.length > 0 || addrs.length > 0) {
electrobun.rpc?.request
- .fetchTokenPrices({ symbols: syms.length > 0 ? syms : undefined, addresses: addrs.length > 0 ? addrs : undefined })
+ .fetchTokenPrices({
+ symbols: syms.length > 0 ? syms : undefined,
+ addresses: addrs.length > 0 ? addrs : undefined,
+ })
.then((prices) => {
- const usd = prices.find((p) => p.currency === 'usd' && p.symbol === w.symbol && !p.address)
+ const usd = prices.find(
+ (p) =>
+ p.currency === 'usd' &&
+ p.symbol === w.symbol &&
+ !p.address,
+ )
nativePrice = usd
? { value: usd.value, currency: usd.currency }
: null
- const map: Record = {}
+ const map: Record<
+ string,
+ { value: string; currency: string }
+ > = {}
for (const p of prices) {
if (p.currency === 'usd' && p.address) {
- map[p.address.toLowerCase()] = { value: p.value, currency: p.currency }
+ map[p.address.toLowerCase()] = {
+ value: p.value,
+ currency: p.currency,
+ }
}
}
tokenPrices = map
@@ -124,6 +141,14 @@
}
})
+ $effect(() => {
+ if (w.chainid) {
+ electrobun.rpc?.request
+ .fetchGasPrice({ chainid: w.chainid })
+ .then((gp) => (gasPrice = gp))
+ }
+ })
+
$effect(() => {
if (!initStarted) {
initStarted = true
@@ -386,16 +411,37 @@
{w.symbol}
{#if nativePrice}
- {@const balVal = Number(w.balance) * Number(nativePrice.value)}
+ {@const balVal =
+ Number(w.balance) * Number(nativePrice.value)}
- ${balVal.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} @ ${Number(nativePrice.value).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}/{w.symbol}
+ ${balVal.toLocaleString(undefined, {
+ minimumFractionDigits: 2,
+ maximumFractionDigits: 2,
+ })} @ ${Number(nativePrice.value).toLocaleString(
+ undefined,
+ {
+ minimumFractionDigits: 2,
+ maximumFractionDigits: 2,
+ },
+ )}/{w.symbol}
+
+ {/if}
+ {#if gasPrice}
+ {@const gasNative = Number(gasPrice) * 21000 / 1e9}
+
+ Gas: {Number(gasPrice).toFixed(1)} Gwei
+ ({gasNative.toLocaleString(undefined, { minimumFractionDigits: 6, maximumFractionDigits: 6 })} {w.symbol})
+ {#if nativePrice}
+ ${(gasNative * Number(nativePrice.value)).toLocaleString(undefined, { minimumFractionDigits: 4, maximumFractionDigits: 4 })}
+ {/if}
{/if}
Tokens
{#each w.tokenBalances as t}
-
+

{#if t.contractAddress && tokenPrices[t.contractAddress.toLowerCase()]}
- {@const val = tokenPrices[t.contractAddress.toLowerCase()]}
- {@const balVal = Number(t.balance) * Number(val.value)}
-
- ${balVal.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} @ ${Number(val.value).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}/{t.symbol}
+ {@const val =
+ tokenPrices[t.contractAddress.toLowerCase()]}
+ {@const balVal =
+ Number(t.balance) * Number(val.value)}
+
+ ${balVal.toLocaleString(undefined, {
+ minimumFractionDigits: 2,
+ maximumFractionDigits: 2,
+ })} @ ${Number(val.value).toLocaleString(
+ undefined,
+ {
+ minimumFractionDigits: 2,
+ maximumFractionDigits: 2,
+ },
+ )}/{t.symbol}
{/if}
{/each}
@@ -485,10 +543,11 @@
})}
class="flex w-full cursor-pointer items-start gap-2 rounded-md bg-muted px-3 py-2 text-xs text-left hover:bg-muted/80 transition-colors">
{#if txAction(tx, w.address) === 'Swap'}
diff --git a/apps/desktop/src/lib/rpc-schema.ts b/apps/desktop/src/lib/rpc-schema.ts
index 9f6a886..668f154 100644
--- a/apps/desktop/src/lib/rpc-schema.ts
+++ b/apps/desktop/src/lib/rpc-schema.ts
@@ -169,6 +169,7 @@ export type RPC = {
evmGetSeed: { params: { vaultKey: string }; response: string }
evmDeleteWallet: { params: { id: string }; response: void }
fetchTokenPrices: { params: { symbols?: string[]; addresses?: { network: string; address: string }[] }; response: TokenPriceEntry[] }
+ fetchGasPrice: { params: { chainid: string }; response: string | null }
}
messages: {}
}