Skip to content

Commit fa002b9

Browse files
committed
refactor inflows chart
1 parent 46a7741 commit fa002b9

File tree

1 file changed

+150
-121
lines changed

1 file changed

+150
-121
lines changed

src/containers/ProtocolOverview/utils.tsx

Lines changed: 150 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,156 @@ export const formatTvlsByChain = ({ historicalChainTvls, extraTvlsEnabled }) =>
5555
return result
5656
}
5757

58+
function buildInflows({ chainTvls, extraTvlsEnabled, tokensUnique, datesToDelete }) {
59+
const usdInflows: Record<string, number> = {}
60+
const tokenInflows: Record<string, any> = {}
61+
const tokensUniqueSet = new Set(tokensUnique)
62+
63+
let zeroUsdInfows = 0
64+
let zeroTokenInfows = 0
65+
66+
for (const section in chainTvls) {
67+
const name = section.toLowerCase()
68+
69+
// skip sum of keys like ethereum-staking, arbitrum-vesting
70+
if (name.includes('-')) continue
71+
72+
const isEnabled = name in extraTvlsEnabled ? extraTvlsEnabled[name] : true
73+
if (!isEnabled) continue
74+
75+
// Index per-date maps
76+
const tokensInUsdByDate: Record<string, Record<string, number>> = {}
77+
const tokensByDate: Record<string, Record<string, number>> = {}
78+
const dateSet = new Set<string>()
79+
80+
for (const { date, tokens } of chainTvls[section]?.tokensInUsd ?? []) {
81+
tokensInUsdByDate[date] = tokens
82+
dateSet.add(String(date))
83+
}
84+
for (const { date, tokens } of chainTvls[section]?.tokens ?? []) {
85+
tokensByDate[date] = tokens
86+
dateSet.add(String(date))
87+
}
88+
89+
const dates: string[] = Array.from(dateSet).sort((a, b) => Number(a) - Number(b))
90+
91+
for (let i = 1; i < dates.length; i++) {
92+
const currentDate = dates[i]
93+
const prevDate = dates[i - 1]
94+
95+
const currentTokens = tokensByDate[currentDate] || {}
96+
const oldTokens = tokensByDate[prevDate] || {}
97+
const currentUsdTokens = tokensInUsdByDate[currentDate] || {}
98+
const oldUsdTokens = tokensInUsdByDate[prevDate] || {}
99+
100+
// Build price map using larger totalUSD preference
101+
const priceMap: Record<string, { value: number; totalUSD: number }> = {}
102+
for (const token in currentUsdTokens) {
103+
const amount = currentTokens[token]
104+
const usd = currentUsdTokens[token]
105+
if (amount && amount > 0) {
106+
priceMap[token] = { value: usd / amount, totalUSD: usd }
107+
}
108+
}
109+
for (const token in oldUsdTokens) {
110+
const amount = oldTokens[token]
111+
const usd = oldUsdTokens[token]
112+
if (amount && amount > 0) {
113+
if (!priceMap[token] || priceMap[token].totalUSD < usd) {
114+
priceMap[token] = { value: usd / amount, totalUSD: usd }
115+
}
116+
}
117+
}
118+
119+
let dayDifference = 0
120+
let tokenDayDifference: Record<string, number> = {}
121+
122+
// Iterate all tokens that changed
123+
const allTokens = new Set([...Object.keys(currentTokens), ...Object.keys(oldTokens)])
124+
125+
for (const token of allTokens) {
126+
const currentAmount = currentTokens[token] || 0
127+
const prevAmount = oldTokens[token] || 0
128+
const diff = currentAmount - prevAmount
129+
if (diff === 0) continue
130+
131+
let priceInfo = priceMap[token]
132+
if (!priceInfo) {
133+
// Fallback price from whichever side has info
134+
if (currentAmount > 0 && currentUsdTokens[token] != null) {
135+
priceInfo = { value: currentUsdTokens[token] / currentAmount, totalUSD: currentUsdTokens[token] }
136+
} else if (prevAmount > 0 && oldUsdTokens[token] != null) {
137+
priceInfo = { value: oldUsdTokens[token] / prevAmount, totalUSD: oldUsdTokens[token] }
138+
}
139+
}
140+
141+
if (!priceInfo || !isFinite(priceInfo.value)) continue
142+
143+
const diffUsd = diff * priceInfo.value
144+
// Ignore outliers similar to computeInflowsData
145+
if (Math.abs(diffUsd) > 2 * (priceInfo.totalUSD || 0)) continue
146+
147+
dayDifference += diffUsd
148+
149+
// Track token inflows only for tokens in tokensUnique
150+
if (tokensUniqueSet.has(token)) {
151+
tokenDayDifference[token] = (tokenDayDifference[token] || 0) + diffUsd
152+
}
153+
}
154+
155+
if (dayDifference === 0) {
156+
zeroUsdInfows++
157+
}
158+
159+
let hasTokenDifference = false
160+
for (const _ in tokenDayDifference) {
161+
hasTokenDifference = true
162+
break
163+
}
164+
if (!hasTokenDifference) {
165+
zeroTokenInfows++
166+
}
167+
168+
usdInflows[currentDate] = (usdInflows[currentDate] || 0) + dayDifference
169+
170+
if (!tokenInflows[currentDate]) {
171+
tokenInflows[currentDate] = { date: Number(currentDate) }
172+
}
173+
// Merge existing values accumulated from other chains before writing
174+
for (const token in tokenInflows[currentDate]) {
175+
if (token !== 'date') {
176+
tokenDayDifference[token] = (tokenDayDifference[token] || 0) + tokenInflows[currentDate][token]
177+
}
178+
}
179+
for (const token in tokenDayDifference) {
180+
tokenInflows[currentDate][token] = tokenDayDifference[token]
181+
}
182+
}
183+
}
184+
185+
for (const date of datesToDelete) {
186+
delete usdInflows[date]
187+
delete tokenInflows[date]
188+
}
189+
190+
const usdFlows: Array<[string, number]> = []
191+
for (const date in usdInflows) {
192+
usdFlows.push([date, usdInflows[date]])
193+
}
194+
usdFlows.sort((a, b) => Number(a[0]) - Number(b[0]))
195+
196+
const tokenFlows = []
197+
for (const date in tokenInflows) {
198+
tokenFlows.push(tokenInflows[date])
199+
}
200+
tokenFlows.sort((a, b) => a.date - b.date)
201+
202+
return {
203+
usdInflows: (zeroUsdInfows === usdFlows.length ? null : usdFlows) as Array<[string, number]> | null,
204+
tokenInflows: zeroTokenInfows === tokenFlows.length ? null : tokenFlows
205+
}
206+
}
207+
58208
// build unique tokens based on top 10 tokens in usd value on each day
59209
// also includes tokens with significant flows (outflows/inflows > $100M)
60210
function getUniqueTokens({ chainTvls, extraTvlsEnabled }) {
@@ -219,127 +369,6 @@ function getUniqueTokens({ chainTvls, extraTvlsEnabled }) {
219369
return Array.from(tokenSet)
220370
}
221371

222-
function buildInflows({ chainTvls, extraTvlsEnabled, tokensUnique, datesToDelete }) {
223-
const usdInflows = {}
224-
const tokenInflows = {}
225-
226-
const tokensUniqueSet = new Set(tokensUnique)
227-
228-
let zeroUsdInfows = 0
229-
let zeroTokenInfows = 0
230-
231-
for (const section in chainTvls) {
232-
const name = section.toLowerCase()
233-
234-
// skip sum of keys like ethereum-staking, arbitrum-vesting
235-
if (!name.includes('-')) {
236-
// sum key with staking, ethereum, arbitrum etc
237-
const isEnabled = name in extraTvlsEnabled ? extraTvlsEnabled[name] : true
238-
if (isEnabled) {
239-
const tokensInUsd: Record<string, Record<string, number>> = {}
240-
const tokensData: Record<string, Record<string, number>> = {}
241-
242-
const tokensInUsdArray = chainTvls[section]?.tokensInUsd ?? []
243-
for (const { date, tokens } of tokensInUsdArray) {
244-
tokensInUsd[date] = tokens
245-
}
246-
247-
const tokensArray = chainTvls[section]?.tokens ?? []
248-
for (const { date, tokens } of tokensArray) {
249-
tokensData[date] = tokens
250-
}
251-
252-
const tokens = tokensData
253-
254-
let prevDate = null
255-
256-
for (const date in tokensInUsd) {
257-
let dayDifference = 0
258-
let tokenDayDifference = {}
259-
260-
for (const token in tokensInUsd[date]) {
261-
const price = tokens[date]?.[token]
262-
? Number((tokensInUsd[date][token] / tokens[date][token]).toFixed(4))
263-
: null
264-
265-
const diff =
266-
tokens[date]?.[token] && prevDate && tokens[prevDate]?.[token]
267-
? tokens[date][token] - tokens[prevDate][token]
268-
: null
269-
270-
const diffUsd = price && diff ? price * diff : null
271-
272-
if (diffUsd && !Number.isNaN(diffUsd) && isFinite(price)) {
273-
// Show only top 10 inflow tokens of the day, add remaining inlfows under "Others" category
274-
if (tokensUniqueSet.has(token)) {
275-
tokenDayDifference[token] = (tokenDayDifference[token] || 0) + diffUsd
276-
} else {
277-
tokenDayDifference['Others'] = (tokenDayDifference['Others'] || 0) + diffUsd
278-
}
279-
280-
dayDifference += diffUsd
281-
}
282-
}
283-
284-
if (dayDifference === 0) {
285-
zeroUsdInfows++
286-
}
287-
288-
let hasTokenDifference = false
289-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
290-
for (const _ in tokenDayDifference) {
291-
hasTokenDifference = true
292-
break
293-
}
294-
if (!hasTokenDifference) {
295-
zeroTokenInfows++
296-
}
297-
298-
usdInflows[date] = (usdInflows[date] || 0) + dayDifference
299-
300-
if (!tokenInflows[date]) {
301-
tokenInflows[date] = { date }
302-
}
303-
304-
for (const token in tokenInflows[date]) {
305-
if (token !== 'date') {
306-
tokenDayDifference[token] = (tokenDayDifference[token] || 0) + tokenInflows[date][token]
307-
}
308-
}
309-
310-
for (const token in tokenDayDifference) {
311-
tokenInflows[date][token] = tokenDayDifference[token]
312-
}
313-
314-
prevDate = date
315-
}
316-
}
317-
}
318-
}
319-
320-
for (const date of datesToDelete) {
321-
delete usdInflows[date]
322-
delete tokenInflows[date]
323-
}
324-
325-
const usdFlows: Array<[string, number]> = []
326-
for (const date in usdInflows) {
327-
usdFlows.push([date, usdInflows[date]])
328-
}
329-
usdFlows.sort((a, b) => Number(a[0]) - Number(b[0]))
330-
331-
const tokenFlows = []
332-
for (const date in tokenInflows) {
333-
tokenFlows.push(tokenInflows[date])
334-
}
335-
tokenFlows.sort((a, b) => a.date - b.date)
336-
337-
return {
338-
usdInflows: (zeroUsdInfows === usdFlows.length ? null : usdFlows) as Array<[string, number]> | null,
339-
tokenInflows: zeroTokenInfows === tokenFlows.length ? null : tokenFlows
340-
}
341-
}
342-
343372
function storeTokensBreakdown({ date, tokens, tokensUnique, directory }) {
344373
const tokensOfTheDay = {}
345374
// filters tokens that have no name or their value is near zero

0 commit comments

Comments
 (0)