Add gas price display (Gwei, native, USD) under native balance per network
This commit is contained in:
@@ -21,6 +21,7 @@ import { db } from './lib/db'
|
|||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { getENV } from './lib/get-env'
|
import { getENV } from './lib/get-env'
|
||||||
import { getClient } from './lib/viem'
|
import { getClient } from './lib/viem'
|
||||||
|
import { formatGwei } from 'viem'
|
||||||
import {
|
import {
|
||||||
isBinaryInstalled,
|
isBinaryInstalled,
|
||||||
downloadBinary,
|
downloadBinary,
|
||||||
@@ -502,13 +503,20 @@ const rpc = BrowserView.defineRPC<RPC>({
|
|||||||
|
|
||||||
if (symbols && symbols.length > 0) {
|
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 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) {
|
if (res.ok) {
|
||||||
const body = await res.json()
|
const body = await res.json()
|
||||||
const data = (body.data ?? []) as any[]
|
const data = (body.data ?? []) as any[]
|
||||||
for (const d of data) {
|
for (const d of data) {
|
||||||
for (const p of d.prices ?? []) {
|
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<RPC>({
|
|||||||
const data = (body.data ?? []) as any[]
|
const data = (body.data ?? []) as any[]
|
||||||
for (const d of data) {
|
for (const d of data) {
|
||||||
for (const p of d.prices ?? []) {
|
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<RPC>({
|
|||||||
return []
|
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 }) => {
|
fetchTransactionDetails: async ({ hash, chainid, address }) => {
|
||||||
const key = await Bun.secrets.get({
|
const key = await Bun.secrets.get({
|
||||||
service: 'koins',
|
service: 'koins',
|
||||||
|
|||||||
@@ -92,7 +92,10 @@
|
|||||||
value: string
|
value: string
|
||||||
currency: string
|
currency: string
|
||||||
} | null>(null)
|
} | null>(null)
|
||||||
let tokenPrices = $state<Record<string, { value: string; currency: string }>>({})
|
let tokenPrices = $state<
|
||||||
|
Record<string, { value: string; currency: string }>
|
||||||
|
>({})
|
||||||
|
let gasPrice = $state<string | null>(null)
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
const syms: string[] = w.symbol ? [w.symbol] : []
|
const syms: string[] = w.symbol ? [w.symbol] : []
|
||||||
@@ -107,16 +110,30 @@
|
|||||||
}
|
}
|
||||||
if (syms.length > 0 || addrs.length > 0) {
|
if (syms.length > 0 || addrs.length > 0) {
|
||||||
electrobun.rpc?.request
|
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) => {
|
.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
|
nativePrice = usd
|
||||||
? { value: usd.value, currency: usd.currency }
|
? { value: usd.value, currency: usd.currency }
|
||||||
: null
|
: null
|
||||||
const map: Record<string, { value: string; currency: string }> = {}
|
const map: Record<
|
||||||
|
string,
|
||||||
|
{ value: string; currency: string }
|
||||||
|
> = {}
|
||||||
for (const p of prices) {
|
for (const p of prices) {
|
||||||
if (p.currency === 'usd' && p.address) {
|
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
|
tokenPrices = map
|
||||||
@@ -124,6 +141,14 @@
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
if (w.chainid) {
|
||||||
|
electrobun.rpc?.request
|
||||||
|
.fetchGasPrice({ chainid: w.chainid })
|
||||||
|
.then((gp) => (gasPrice = gp))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (!initStarted) {
|
if (!initStarted) {
|
||||||
initStarted = true
|
initStarted = true
|
||||||
@@ -386,16 +411,37 @@
|
|||||||
{w.symbol}
|
{w.symbol}
|
||||||
</p>
|
</p>
|
||||||
{#if nativePrice}
|
{#if nativePrice}
|
||||||
{@const balVal = Number(w.balance) * Number(nativePrice.value)}
|
{@const balVal =
|
||||||
|
Number(w.balance) * Number(nativePrice.value)}
|
||||||
<p class="font-mono text-xs text-muted-foreground">
|
<p class="font-mono text-xs text-muted-foreground">
|
||||||
${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}
|
||||||
|
</p>
|
||||||
|
{/if}
|
||||||
|
{#if gasPrice}
|
||||||
|
{@const gasNative = Number(gasPrice) * 21000 / 1e9}
|
||||||
|
<p class="font-mono text-xs text-muted-foreground mt-1">
|
||||||
|
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}
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4 space-y-1">
|
<div class="mb-4 space-y-1">
|
||||||
<p class="font-medium text-xs">Tokens</p>
|
<p class="font-medium text-xs">Tokens</p>
|
||||||
{#each w.tokenBalances as t}
|
{#each w.tokenBalances as t}
|
||||||
<div class="flex items-center gap-1.5 font-mono text-sm">
|
<div
|
||||||
|
class="flex items-center gap-1.5 font-mono text-sm">
|
||||||
<img
|
<img
|
||||||
src={t.logo}
|
src={t.logo}
|
||||||
alt=""
|
alt=""
|
||||||
@@ -409,10 +455,22 @@
|
|||||||
{t.symbol}
|
{t.symbol}
|
||||||
</div>
|
</div>
|
||||||
{#if t.contractAddress && tokenPrices[t.contractAddress.toLowerCase()]}
|
{#if t.contractAddress && tokenPrices[t.contractAddress.toLowerCase()]}
|
||||||
{@const val = tokenPrices[t.contractAddress.toLowerCase()]}
|
{@const val =
|
||||||
{@const balVal = Number(t.balance) * Number(val.value)}
|
tokenPrices[t.contractAddress.toLowerCase()]}
|
||||||
<p class="font-mono text-xs text-muted-foreground -mt-0.5 ml-6">
|
{@const balVal =
|
||||||
${balVal.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} @ ${Number(val.value).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}/{t.symbol}
|
Number(t.balance) * Number(val.value)}
|
||||||
|
<p
|
||||||
|
class="font-mono text-xs text-muted-foreground -mt-0.5 ml-6">
|
||||||
|
${balVal.toLocaleString(undefined, {
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
maximumFractionDigits: 2,
|
||||||
|
})} @ ${Number(val.value).toLocaleString(
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
maximumFractionDigits: 2,
|
||||||
|
},
|
||||||
|
)}/{t.symbol}
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
@@ -485,7 +543,8 @@
|
|||||||
})}
|
})}
|
||||||
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">
|
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">
|
||||||
<div
|
<div
|
||||||
class="shrink-0 mt-0.5 {txAction(tx, w.address) === 'Swap'
|
class="shrink-0 mt-0.5 {txAction(tx, w.address) ===
|
||||||
|
'Swap'
|
||||||
? 'text-yellow-500'
|
? 'text-yellow-500'
|
||||||
: tx.from.toLowerCase() ===
|
: tx.from.toLowerCase() ===
|
||||||
w.address.toLowerCase()
|
w.address.toLowerCase()
|
||||||
|
|||||||
@@ -169,6 +169,7 @@ export type RPC = {
|
|||||||
evmGetSeed: { params: { vaultKey: string }; response: string }
|
evmGetSeed: { params: { vaultKey: string }; response: string }
|
||||||
evmDeleteWallet: { params: { id: string }; response: void }
|
evmDeleteWallet: { params: { id: string }; response: void }
|
||||||
fetchTokenPrices: { params: { symbols?: string[]; addresses?: { network: string; address: string }[] }; response: TokenPriceEntry[] }
|
fetchTokenPrices: { params: { symbols?: string[]; addresses?: { network: string; address: string }[] }; response: TokenPriceEntry[] }
|
||||||
|
fetchGasPrice: { params: { chainid: string }; response: string | null }
|
||||||
}
|
}
|
||||||
messages: {}
|
messages: {}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user