@@ -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