Skip to content

Commit 270e559

Browse files
committed
improve protocol stats synchronization
1 parent ac2d93b commit 270e559

File tree

1 file changed

+91
-41
lines changed

1 file changed

+91
-41
lines changed

src/mainnet/processors/protocol/protocol.ts

Lines changed: 91 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export const protocolProcessor = defineProcessor({
3030
processor.includeAllBlocks({ from: 15000000 })
3131
},
3232
process: async (ctx: Context) => {
33+
// Stage 1: Normal processing of all tokens
3334
const oethProduct = otokenProducts.find((p) => p.product === 'OETH')!
3435
const oethDetails = await getOTokenDetails(ctx, oethProduct)
3536
const oethCacheByDate: Record<string, ProtocolDailyStatDetail> = {}
@@ -46,6 +47,21 @@ export const protocolProcessor = defineProcessor({
4647
// Save product details
4748
await ctx.store.upsert(details)
4849

50+
// Stage 2: Recalculate OETH if super tokens were updated
51+
const superTokenUpdatedDates = uniq([
52+
...otherOTokenDetailsArrays.flat().filter(d => d.product === 'superOETHb' || d.product === 'superOETHp').map(d => d.date)
53+
])
54+
55+
if (superTokenUpdatedDates.length > 0) {
56+
const updatedOethDetails = await recalculateOETHForDates(ctx, oethProduct, superTokenUpdatedDates)
57+
if (updatedOethDetails.length > 0) {
58+
await ctx.store.upsert(updatedOethDetails)
59+
// Update details array to include the recalculated OETH data
60+
details = details.filter(d => !(d.product === 'OETH' && superTokenUpdatedDates.includes(d.date)))
61+
details.push(...updatedOethDetails)
62+
}
63+
}
64+
4965
// Merge all product details into daily statistics
5066
const dailyStats = []
5167

@@ -161,6 +177,8 @@ const getOTokenDetails = async (
161177
lastDates.push(lastSuperOETHp?.date ?? startDate)
162178
}
163179

180+
// For OETH variants, use the minimum date to ensure all interdependent records are recalculated
181+
// when any one of them gets updated. This ensures OETH inherits correct TVL from superOETHb/superOETHp
164182
const oldestDate = lastDates.reduce((min, d) => (d < min ? d : min), lastDates[0])
165183

166184
const status = await ctx.store.findOne(ProcessingStatus, { where: { id: processorId } })
@@ -177,49 +195,10 @@ const getOTokenDetails = async (
177195
return details
178196
}
179197
const detail = await getProtocolDailyStatDetail(ctx, date, product)
180-
const eth = (value: bigint) => (value * otokenDailyStat.rateETH) / BigInt(10 ** 18)
181-
detail.rateUSD = otokenDailyStat.rateUSD
182-
detail.earningTvl = eth(otokenDailyStat.rebasingSupply)
183-
detail.tvl = eth(otokenDailyStat.totalSupply - (otokenDailyStat.amoSupply ?? 0n))
184-
detail.supply = eth(otokenDailyStat.totalSupply)
185-
detail.yield = eth(otokenDailyStat.yield + otokenDailyStat.fees)
186-
detail.revenue = eth(otokenDailyStat.fees)
187-
detail.apy = otokenDailyStat.apy
188-
detail.inheritedTvl = 0n
189-
detail.inheritedYield = 0n
190-
detail.inheritedRevenue = 0n
191-
detail.bridgedTvl = 0n
192-
193-
const getLatestStrategyBalance = async (ctx: Context, strategy: string, date: string) => {
194-
return await ctx.store.findOne(StrategyBalance, {
195-
where: {
196-
strategy,
197-
timestamp: LessThanOrEqual(dayjs.utc(date).endOf('day').toDate()),
198-
},
199-
order: {
200-
timestamp: 'desc',
201-
},
202-
})
203-
}
198+
calculateOTokenBasicStats(detail, otokenDailyStat)
204199

205200
if (detail.product === 'OETH') {
206-
// Pull superOETHp and superOETHb overlapping details.
207-
const superOETHbWrappedOETH = await getLatestStrategyBalance(
208-
ctx,
209-
baseAddresses.superOETHb.strategies.bridgedWOETH,
210-
date,
211-
)
212-
const superOETHpWrappedOETH = await getLatestStrategyBalance(
213-
ctx,
214-
plumeAddresses.superOETHp.strategies.bridgedWOETH,
215-
date,
216-
)
217-
218-
detail.inheritedTvl = (superOETHbWrappedOETH?.balanceETH ?? 0n) + (superOETHpWrappedOETH?.balanceETH ?? 0n)
219-
detail.inheritedYield = detail.earningTvl !== 0n ? (detail.yield * detail.inheritedTvl) / detail.earningTvl : 0n
220-
detail.inheritedRevenue =
221-
detail.earningTvl !== 0n ? (detail.revenue * detail.inheritedTvl) / detail.earningTvl : 0n
222-
detail.bridgedTvl = (superOETHbWrappedOETH?.balanceETH ?? 0n) + (superOETHpWrappedOETH?.balanceETH ?? 0n)
201+
await calculateOETHInheritedStats(ctx, detail, date)
223202
} else if (detail.product === 'superOETHb') {
224203
// Prefer in-memory OETH data computed earlier in this run to avoid races
225204
const detailOETH = opts?.oethCacheByDate?.[date] ?? (await getProtocolDailyStatDetail(ctx, date, 'OETH'))
@@ -294,3 +273,74 @@ const getArmDetails = async (
294273
}
295274
return details
296275
}
276+
277+
const getLatestStrategyBalance = async (ctx: Context, strategy: string, date: string) => {
278+
return await ctx.store.findOne(StrategyBalance, {
279+
where: {
280+
strategy,
281+
timestamp: LessThanOrEqual(dayjs.utc(date).endOf('day').toDate()),
282+
},
283+
order: {
284+
timestamp: 'desc',
285+
},
286+
})
287+
}
288+
289+
const calculateOTokenBasicStats = (detail: ProtocolDailyStatDetail, otokenDailyStat: OTokenDailyStat) => {
290+
const eth = (value: bigint) => (value * otokenDailyStat.rateETH) / BigInt(10 ** 18)
291+
292+
detail.rateUSD = otokenDailyStat.rateUSD
293+
detail.earningTvl = eth(otokenDailyStat.rebasingSupply)
294+
detail.tvl = eth(otokenDailyStat.totalSupply - (otokenDailyStat.amoSupply ?? 0n))
295+
detail.supply = eth(otokenDailyStat.totalSupply)
296+
detail.yield = eth(otokenDailyStat.yield + otokenDailyStat.fees)
297+
detail.revenue = eth(otokenDailyStat.fees)
298+
detail.apy = otokenDailyStat.apy
299+
detail.inheritedTvl = 0n
300+
detail.inheritedYield = 0n
301+
detail.inheritedRevenue = 0n
302+
detail.bridgedTvl = 0n
303+
}
304+
305+
const calculateOETHInheritedStats = async (ctx: Context, detail: ProtocolDailyStatDetail, date: string) => {
306+
const superOETHbWrappedOETH = await getLatestStrategyBalance(
307+
ctx,
308+
baseAddresses.superOETHb.strategies.bridgedWOETH,
309+
date,
310+
)
311+
const superOETHpWrappedOETH = await getLatestStrategyBalance(
312+
ctx,
313+
plumeAddresses.superOETHp.strategies.bridgedWOETH,
314+
date,
315+
)
316+
317+
detail.inheritedTvl = (superOETHbWrappedOETH?.balanceETH ?? 0n) + (superOETHpWrappedOETH?.balanceETH ?? 0n)
318+
detail.inheritedYield = detail.earningTvl !== 0n ? (detail.yield * detail.inheritedTvl) / detail.earningTvl : 0n
319+
detail.inheritedRevenue = detail.earningTvl !== 0n ? (detail.revenue * detail.inheritedTvl) / detail.earningTvl : 0n
320+
detail.bridgedTvl = (superOETHbWrappedOETH?.balanceETH ?? 0n) + (superOETHpWrappedOETH?.balanceETH ?? 0n)
321+
}
322+
323+
const recalculateOETHForDates = async (
324+
ctx: Context,
325+
oethProduct: { processorId: string; product: ProductName; otokenAddress: string },
326+
dates: string[],
327+
) => {
328+
const details: ProtocolDailyStatDetail[] = []
329+
330+
const dailyStats = await ctx.store.find(OTokenDailyStat, {
331+
where: { date: In(dates), otoken: oethProduct.otokenAddress }
332+
})
333+
334+
for (const date of dates) {
335+
const otokenDailyStat = dailyStats.find((d) => d.date === date)
336+
if (!otokenDailyStat) continue
337+
338+
const detail = await getProtocolDailyStatDetail(ctx, date, oethProduct.product)
339+
calculateOTokenBasicStats(detail, otokenDailyStat)
340+
await calculateOETHInheritedStats(ctx, detail, date)
341+
342+
details.push(detail)
343+
}
344+
345+
return details
346+
}

0 commit comments

Comments
 (0)