Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions packages/api/src/routes/metrics/metricController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2676,3 +2676,48 @@ async function calculateMetricsForHistoricalRecord(
function extractTvlValue(tvl: number | TvlWithChange): number {
return (tvl as TvlWithChange).tvl || (tvl as number)
}

/**
* Function to handle the request for historical strategy shares data
*
* @param req
* @param res
* @returns
*/
export async function getHistoricalStrategyShares(req: Request, res: Response) {
const { strategyAddress } = req.params

try {
if (!strategyAddress) {
return res.status(400).json({ error: 'Strategy address is required' })
}

const prismaClient = getPrismaClient()

const historicalShares = await prismaClient.strategySharesDaily.findMany({
where: {
strategyAddress: strategyAddress.toLowerCase()
},
orderBy: {
timestamp: 'asc'
},
select: {
strategyAddress: true,
tokenAddress: true,
sharesToUnderlying: true,
timestamp: true
}
})

return res.status(200).json({
data: historicalShares.map((share) => ({
strategyAddress: share.strategyAddress,
tokenAddress: share.tokenAddress,
exchangeRate: share.sharesToUnderlying,
timestamp: share.timestamp
}))
})
} catch (error) {
return res.status(500).json({ error: 'Failed to fetch historical strategy shares data' })
}
}
9 changes: 8 additions & 1 deletion packages/api/src/routes/metrics/metricRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ import {
getTotalWithdrawals,
getTotalDeposits,
getRestakingRatio,
getDeploymentRatio
getDeploymentRatio,
getHistoricalStrategyShares
} from './metricController'

import routeCache from 'route-cache'
Expand Down Expand Up @@ -97,4 +98,10 @@ router.get('/restaking-ratio', routeCache.cacheSeconds(120), getRestakingRatio)

router.get('/deployment-ratio', routeCache.cacheSeconds(120), getDeploymentRatio)

router.get(
'/historical/strategy-shares/:strategyAddress',
routeCache.cacheSeconds(120),
getHistoricalStrategyShares
)

export default router
13 changes: 13 additions & 0 deletions packages/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -874,6 +874,19 @@ model EthPricesDaily {
@@index([timestamp])
}

model StrategySharesDaily {
id Int @id @default(autoincrement())

strategyAddress String
tokenAddress String
sharesToUnderlying String
timestamp DateTime

@@unique([strategyAddress, timestamp])
@@index([strategyAddress])
@@index([timestamp])
}

// Misc

model Evm_BlockData {
Expand Down
2 changes: 2 additions & 0 deletions packages/seeder/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { seedMetricsRestaking } from './metrics/seedMetricsRestaking'
import { seedStrategies } from './seedStrategies'
import { seedRestakedStrategies } from './seedAvsRestakedStrategies'
import { seedEthPricesDaily } from './seedEthPricesDaily'
import { seedStrategySharesDaily } from './seedStrategySharesDaily'
import { seedMetricsEigenPods } from './metrics/seedMetricsEigenPods'
import { seedMetricsTvl } from './metrics/seedMetricsTvl'
import { monitorAvsMetrics } from './monitors/avsMetrics'
Expand Down Expand Up @@ -271,6 +272,7 @@ async function seedEigenDailyData(retryCount = 0) {
await seedStrategies()
await seedRestakedStrategies()
await seedEthPricesDaily()
await seedStrategySharesDaily()

if (!process.env.FLAG_SEEDER_DISABLE_HISTORICAL_DATA) {
await seedMetricsDeposit()
Expand Down
113 changes: 113 additions & 0 deletions packages/seeder/src/seedStrategySharesDaily.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import 'dotenv/config'

import type prisma from '@prisma/client'
import { strategyAbi } from './data/abi/strategy'
import { getPrismaClient } from './utils/prismaClient'
import { bulkUpdateDbTransactions } from './utils/seeder'
import { getViemClient } from './utils/viemClient'

export async function seedStrategySharesDaily() {
const prismaClient = getPrismaClient()
const viemClient = getViemClient()

console.time('Done in')

const startAt = await getLastUpdate()
const endAt = setToStartOfDay(new Date())

const strategySharesDailyList: Omit<prisma.StrategySharesDaily, 'id'>[] = []

// Bail early if there is no time diff to sync
if (endAt.getTime() - startAt.getTime() <= 0) {
console.log(
`[In Sync] [Data] Strategy Shares Daily from: ${startAt.getTime()} to: ${endAt.getTime()}`
)
return
}

try {
// Get all strategies from the database
const strategies = await prismaClient.strategies.findMany()

// For each day between startAt and endAt, capture strategy shares
const currentDate = new Date(startAt)
while (currentDate < endAt) {
const dayTimestamp = setToStartOfDay(new Date(currentDate))

// For each strategy, get the current sharesToUnderlying rate
for (const strategy of strategies) {
const strategyAddress = strategy.address.toLowerCase()
let sharesToUnderlying = strategy.sharesToUnderlying || '1000000000000000000' // Default 1e18

try {
// Read current exchange rate from strategy contract
const rate = (await viemClient.readContract({
address: strategyAddress as `0x${string}`,
abi: strategyAbi,
functionName: 'sharesToUnderlyingView',
args: [BigInt(1e18)]
})) as bigint

sharesToUnderlying = rate.toString()
} catch (error) {
// Use fallback from database if contract call fails
console.warn(`Failed to read sharesToUnderlying for strategy ${strategyAddress}:`, error)
}

strategySharesDailyList.push({
strategyAddress,
tokenAddress: strategy.underlyingToken.toLowerCase(),
sharesToUnderlying,
timestamp: dayTimestamp
})
}

// Move to next day
currentDate.setDate(currentDate.getDate() + 1)
}

// Save to database
if (strategySharesDailyList.length > 0) {
const dbTransactions: any[] = []

dbTransactions.push(
prismaClient.strategySharesDaily.createMany({
data: strategySharesDailyList,
skipDuplicates: true
})
)

await bulkUpdateDbTransactions(
dbTransactions,
`[Data] Strategy Shares Daily size: ${strategySharesDailyList.length}`
)
}
} catch (error) {
console.log('Error seeding Strategy Shares Daily: ', error)
}

console.timeEnd('Done in')
}

async function getLastUpdate() {
const prismaClient = getPrismaClient()
const minimumStartAt = setToStartOfDay(new Date())
minimumStartAt.setMonth(minimumStartAt.getMonth() - 1)
minimumStartAt.setDate(minimumStartAt.getDate() + 1)

const latestRecord = await prismaClient.strategySharesDaily.findFirst({
select: { timestamp: true },
orderBy: { timestamp: 'desc' }
})

return (latestRecord && latestRecord.timestamp.getTime() <= minimumStartAt.getTime()) ||
!latestRecord
? minimumStartAt
: latestRecord.timestamp
}

function setToStartOfDay(date: Date) {
const dailyDate = new Date(date)
dailyDate.setUTCHours(0, 0, 0, 0)
return dailyDate
}