Skip to content

Commit a7f640a

Browse files
authored
fix: coin denomination (#4695)
1 parent fc062ea commit a7f640a

File tree

20 files changed

+407
-56
lines changed

20 files changed

+407
-56
lines changed

src/components/ProtocolActivity/ProtocolActivityContext.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, {
22
createContext,
33
PropsWithChildren,
44
useContext,
5+
useEffect,
56
useMemo,
67
useState,
78
} from 'react'
@@ -17,6 +18,8 @@ const ProtocolActivityContext = createContext<
1718
ProtocolActivityContextType | undefined
1819
>(undefined)
1920

21+
const STORAGE_KEY = 'protocolActivityPanelOpen'
22+
2023
export const useProtocolActivity = () => {
2124
const context = useContext(ProtocolActivityContext)
2225
if (!context) {
@@ -30,7 +33,17 @@ export const useProtocolActivity = () => {
3033
export const ProtocolActivityProvider: React.FC<PropsWithChildren> = ({
3134
children,
3235
}) => {
33-
const [isOpen, setIsOpen] = useState(true)
36+
// Initialize state from localStorage, default to true if not set
37+
const [isOpen, setIsOpen] = useState(() => {
38+
if (typeof window === 'undefined') return true
39+
const stored = localStorage.getItem(STORAGE_KEY)
40+
return stored !== null ? stored === 'true' : true
41+
})
42+
43+
// Persist state changes to localStorage
44+
useEffect(() => {
45+
localStorage.setItem(STORAGE_KEY, String(isOpen))
46+
}, [isOpen])
3447

3548
const value = useMemo(
3649
() => ({

src/components/ProtocolActivity/ProtocolActivityElement.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ export interface ProtocolActivityElementEvent {
1414
projectId?: number
1515
projectName?: string | null
1616
projectHandle?: string | null
17+
projectLogoUri?: string | null
18+
projectToken?: string | null
19+
projectCurrency?: string | null
20+
projectDecimals?: number | null
1721
}
1822

1923
export function ProtocolActivityElement({
@@ -39,6 +43,7 @@ export function ProtocolActivityElement({
3943
className="h-12 w-12"
4044
projectId={event.projectId}
4145
name={displayName}
46+
uri={event.projectLogoUri ?? undefined}
4247
pv={'5'}
4348
/>
4449
</div>

src/components/ProtocolActivity/ProtocolActivityPanel.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export function ProtocolActivityPanel() {
1212
'sticky top-0 self-start bg-white transition-all duration-300 dark:bg-slate-900',
1313
'border-l-2 border-smoke-300 shadow-2xl dark:border-slate-700',
1414
'h-screen flex flex-col',
15-
isOpen ? 'w-[460px]' : 'w-0',
15+
isOpen ? 'w-[400px]' : 'w-0',
1616
)}
1717
>
1818
{isOpen && (

src/components/ProtocolActivity/utils/translateEventDataToProtocolPresenter.tsx

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,53 @@
11
import { BigNumber } from '@ethersproject/bignumber'
22
import { AmountInCurrency } from 'components/currency/AmountInCurrency'
3+
import { USDC_ADDRESSES } from 'juice-sdk-core'
4+
import { ETH_TOKEN_ADDRESS } from 'constants/juiceboxTokens'
35
import { AnyEvent } from 'packages/v4v5/views/V4V5ProjectDashboard/V4V5ProjectTabs/V4V5ActivityPanel/utils/transformEventsData'
46
import { formatActivityAmount } from 'utils/format/formatActivityAmount'
57

8+
// Build currency mapping from SDK constants
9+
// Maps token addresses (lowercase) to their symbols
10+
const CURRENCY_SYMBOLS: Record<string, string> = {
11+
// Add ETH token address
12+
[ETH_TOKEN_ADDRESS.toLowerCase()]: 'ETH',
13+
// Add USDC addresses for all supported chains
14+
...Object.values(USDC_ADDRESSES).reduce((acc, address) => {
15+
acc[address.toLowerCase()] = 'USDC'
16+
return acc
17+
}, {} as Record<string, string>),
18+
}
19+
20+
/**
21+
* Get currency symbol from currency address (hex string)
22+
*/
23+
function getCurrencySymbol(currency?: string | null): string {
24+
if (!currency) return 'ETH'
25+
// Normalize to lowercase for lookup
26+
const symbol = CURRENCY_SYMBOLS[currency.toLowerCase()]
27+
return symbol || 'ETH'
28+
}
29+
630
/**
731
* Translate event data to protocol activity presenter with formatted amounts
832
*/
933
export function translateEventDataToProtocolPresenter(event: AnyEvent) {
34+
// Use projectToken (the actual token address) for currency symbol lookup
35+
const currencySymbol = getCurrencySymbol(event.projectToken)
36+
// Use project decimals (e.g., 6 for USDC, 18 for ETH)
37+
const decimals = event.projectDecimals ?? 18
38+
1039
switch (event.type) {
1140
case 'payEvent':
1241
return {
1342
event,
1443
header: 'Paid',
15-
subject: `${formatActivityAmount(event.amount.value)} ETH`,
44+
subject: `${formatActivityAmount(event.amount.value, decimals)} ${currencySymbol}`,
1645
}
1746
case 'addToBalanceEvent':
1847
return {
1948
event,
2049
header: 'Added to balance',
21-
subject: `${formatActivityAmount(event.amount.value)} ETH`,
50+
subject: `${formatActivityAmount(event.amount.value, decimals)} ${currencySymbol}`,
2251
}
2352
case 'manualMintTokensEvent':
2453
return {
@@ -30,7 +59,7 @@ export function translateEventDataToProtocolPresenter(event: AnyEvent) {
3059
return {
3160
event,
3261
header: 'Cashed out',
33-
subject: `${formatActivityAmount(event.reclaimAmount.value)} ETH`,
62+
subject: `${formatActivityAmount(event.reclaimAmount.value, decimals)} ${currencySymbol}`,
3463
}
3564
case 'deployedERC20Event':
3665
return {
@@ -48,7 +77,7 @@ export function translateEventDataToProtocolPresenter(event: AnyEvent) {
4877
return {
4978
event,
5079
header: 'Send payouts',
51-
subject: `${formatActivityAmount(event.amount.value)} ETH`,
80+
subject: `${formatActivityAmount(event.amount.value, decimals)} ${currencySymbol}`,
5281
}
5382
case 'distributeReservedTokensEvent':
5483
return {
@@ -66,13 +95,13 @@ export function translateEventDataToProtocolPresenter(event: AnyEvent) {
6695
return {
6796
event,
6897
header: 'Send to payout split',
69-
subject: `${formatActivityAmount(event.amount.value)} ETH`,
98+
subject: `${formatActivityAmount(event.amount.value, decimals)} ${currencySymbol}`,
7099
}
71100
case 'useAllowanceEvent':
72101
return {
73102
event,
74103
header: 'Used allowance',
75-
subject: `${formatActivityAmount(event.amount.value)} ETH`,
104+
subject: `${formatActivityAmount(event.amount.value, decimals)} ${currencySymbol}`,
76105
}
77106
case 'manualBurnEvent':
78107
return {

src/components/VolumeChart/components/TimelineChart.tsx

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { t } from '@lingui/macro'
22
import CurrencySymbol from 'components/currency/CurrencySymbol'
33
import { ThemeContext } from 'contexts/Theme/ThemeContext'
44
import { useTrendingProjects } from 'hooks/useTrendingProjects'
5+
import { USDC_ADDRESSES } from 'juice-sdk-core'
56
import tailwind from 'lib/tailwind'
67
import moment from 'moment'
78
import { CSSProperties, useContext, useMemo } from 'react'
@@ -25,24 +26,51 @@ import {
2526
ProjectTimelineRange,
2627
ProjectTimelineView,
2728
} from '../types'
29+
import { ETH_TOKEN_ADDRESS } from 'constants/juiceboxTokens'
2830

2931
const now = Date.now().valueOf()
3032

33+
// Build currency mapping from SDK constants
34+
const CURRENCY_SYMBOLS: Record<string, string> = {
35+
[ETH_TOKEN_ADDRESS.toLowerCase()]: 'ETH',
36+
...Object.values(USDC_ADDRESSES).reduce((acc, address) => {
37+
acc[address.toLowerCase()] = 'USDC'
38+
return acc
39+
}, {} as Record<string, string>),
40+
}
41+
42+
/**
43+
* Get currency symbol from currency address (hex string)
44+
*/
45+
function getCurrencySymbol(currency?: string | null): string {
46+
if (!currency) return 'ETH'
47+
// Normalize to lowercase for lookup
48+
const symbol = CURRENCY_SYMBOLS[currency.toLowerCase()]
49+
return symbol || 'ETH'
50+
}
51+
3152
export default function TimelineChart({
3253
points,
3354
view,
3455
range,
3556
height,
57+
projectToken,
58+
projectDecimals,
3659
}: {
3760
points: ProjectTimelinePoint[] | undefined
3861
view: ProjectTimelineView
3962
range: ProjectTimelineRange
4063
height: CSSProperties['height']
64+
projectToken?: string
65+
projectDecimals?: number
4166
}) {
4267
const { themeOption } = useContext(ThemeContext)
4368

4469
const defaultYDomain = useTimelineYDomain(points?.map(p => p[view]))
4570

71+
// Determine currency symbol based on project token
72+
const currencySymbol = getCurrencySymbol(projectToken)
73+
4674
const { data: trendingProjects } = useTrendingProjects(1)
4775
const highTrendingScore = trendingProjects?.length
4876
? wadToFloat(trendingProjects[0].trendingScore)
@@ -166,7 +194,11 @@ export default function TimelineChart({
166194
fill={color}
167195
transform={`translate(${props.x + 4},${props.y + 4})`}
168196
>
169-
<CurrencySymbol currency="ETH" />
197+
{currencySymbol === 'ETH' ? (
198+
<CurrencySymbol currency="ETH" />
199+
) : (
200+
`${currencySymbol} `
201+
)}
170202
{formattedValue}
171203
</text>
172204
</g>
@@ -241,7 +273,11 @@ export default function TimelineChart({
241273
</div>
242274
{view !== 'trendingScore' && (
243275
<div className="font-medium">
244-
<CurrencySymbol currency="ETH" />
276+
{currencySymbol === 'ETH' ? (
277+
<CurrencySymbol currency="ETH" />
278+
) : (
279+
`${currencySymbol} `
280+
)}
245281
{amount.toFixed(amount > 10 ? 1 : amount > 1 ? 2 : 4)}
246282
</div>
247283
)}

src/components/VolumeChart/index.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ export default function VolumeChart({
1919
version,
2020
lockedView,
2121
hideViewSelector,
22+
projectToken,
23+
projectDecimals,
2224
}: {
2325
height: CSSProperties['height']
2426
createdAt: number | undefined
@@ -27,6 +29,8 @@ export default function VolumeChart({
2729
version?: number
2830
lockedView?: ProjectTimelineView
2931
hideViewSelector?: boolean
32+
projectToken?: string
33+
projectDecimals?: number
3034
}) {
3135
const [timelineView, setTimelineView] =
3236
useState<ProjectTimelineView>(lockedView || 'volume')
@@ -42,7 +46,12 @@ export default function VolumeChart({
4246

4347
// V4/V5: Use new database-based hook (only for V4 projects)
4448
const shouldUseV4Hook = pv === PV_V4
45-
const { points: v4v5Points, loading: v4v5Loading } = useV4V5ProjectTimeline({
49+
const {
50+
points: v4v5Points,
51+
loading: v4v5Loading,
52+
projectToken: v4v5Token,
53+
projectDecimals: v4v5Decimals,
54+
} = useV4V5ProjectTimeline({
4655
projectId: shouldUseV4Hook ? projectId : 0,
4756
range,
4857
version: version || 4,
@@ -51,6 +60,10 @@ export default function VolumeChart({
5160
const points = shouldUseV4Hook ? v4v5Points : v1v2v3Points
5261
const loading = shouldUseV4Hook ? v4v5Loading : legacyLoading
5362

63+
// Use token/decimals from hook if not provided via props
64+
const token = projectToken ?? v4v5Token
65+
const decimals = projectDecimals ?? v4v5Decimals
66+
5467
// Use locked view if provided, otherwise use state
5568
const currentView = lockedView || timelineView
5669

@@ -73,6 +86,8 @@ export default function VolumeChart({
7386
view={currentView}
7487
range={range}
7588
height={height}
89+
projectToken={token}
90+
projectDecimals={decimals}
7691
/>
7792
{!points?.length && (
7893
<div className="absolute top-0 left-0 right-0 bottom-0 flex items-center justify-center">

src/packages/v4v5/graphql/queries/activityEvents.graphql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ query ActivityEvents(
2323
project {
2424
name
2525
handle
26+
logoUri
27+
token
28+
currency
29+
decimals
2630
}
2731
payEvent {
2832
id

src/packages/v4v5/graphql/queries/project.graphql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ query Project($chainId: Float!, $projectId: Float!, $version: Float!) {
1818
suckerGroupId
1919
createdAt
2020
isRevnet
21+
token
22+
currency
23+
decimals
2124
permissionHolders {
2225
items {
2326
operator

src/packages/v4v5/hooks/useV4V5ProjectTimeline.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,17 @@ import { useMemo } from 'react'
55
import { wadToFloat } from 'utils/format/formatNumber'
66
import { ProjectTimelinePoint, ProjectTimelineRange } from 'components/VolumeChart/types'
77

8+
/**
9+
* Convert volume/balance value from token decimals to float
10+
* Volume in Bendystraw is stored in the token's native decimals (6 for USDC, 18 for ETH)
11+
*/
12+
function formatVolumeValue(value: string | bigint, decimals: number = 18): number {
13+
const num = typeof value === 'string' ? BigInt(value) : value
14+
const divisor = BigInt(10) ** BigInt(decimals)
15+
// Convert to float with proper decimal places
16+
return Number(num) / Number(divisor)
17+
}
18+
819
export function useV4V5ProjectTimeline({
920
projectId,
1021
range,
@@ -89,21 +100,24 @@ export function useV4V5ProjectTimeline({
89100
if (!timestamps || !project?.project) return
90101
if (queryResult === undefined) return // Still loading
91102

103+
// Get project decimals for proper volume/balance conversion
104+
const decimals = project.project.decimals ? Number(project.project.decimals) : 18
105+
92106
const previous = queryResult.previous.items.length
93107
? queryResult.previous.items[0]
94108
: undefined
95109

96110
const rangeItems = queryResult.range.items.map(item => ({
97111
timestamp: item.timestamp,
98-
volume: wadToFloat(item.volume),
99-
balance: wadToFloat(item.balance),
100-
trendingScore: wadToFloat(item.trendingScore),
112+
volume: formatVolumeValue(item.volume, decimals),
113+
balance: formatVolumeValue(item.balance, decimals),
114+
trendingScore: wadToFloat(item.trendingScore), // Trending score is always in wad
101115
}))
102116

103117
// Base values to use when no data exists
104118
const baseValues = previous ? {
105-
volume: wadToFloat(previous.volume),
106-
balance: wadToFloat(previous.balance),
119+
volume: formatVolumeValue(previous.volume, decimals),
120+
balance: formatVolumeValue(previous.balance, decimals),
107121
trendingScore: wadToFloat(previous.trendingScore),
108122
} : {
109123
volume: 0,
@@ -153,5 +167,7 @@ export function useV4V5ProjectTimeline({
153167
return {
154168
points,
155169
loading: isLoadingProject || isLoadingTimeline,
170+
projectToken: project?.project?.token ?? undefined,
171+
projectDecimals: project?.project?.decimals ? Number(project.project.decimals) : undefined,
156172
}
157173
}

0 commit comments

Comments
 (0)