Skip to content
Merged
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
57 changes: 50 additions & 7 deletions packages/ingest/abis/yearn/3/vault/snapshot/hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { mq } from 'lib'
import { estimateCreationBlock } from 'lib/blocks'
import { priced } from 'lib/math'
import { snakeToCamelCols } from 'lib/strings'
import { EvmAddressSchema, ThingSchema, TokenMetaSchema, VaultMetaSchema, zhexstring } from 'lib/types'
import { EstimatedAprSchema, EvmAddressSchema, ThingSchema, TokenMetaSchema, VaultMetaSchema, zhexstring } from 'lib/types'
import { parseAbi, toEventSelector, zeroAddress } from 'viem'
import { z } from 'zod'
import db, { getSparkline } from '../../../../../db'
Expand All @@ -22,6 +22,7 @@ export const CompositionSchema = z.object({
status: z.enum(['active', 'inactive', 'unallocated']),
latestReportApr: z.number().nullish(),
performance: z.object({
estimated: EstimatedAprSchema.nullish(),
oracle: z.object({
apr: z.number().nullish(),
apy: z.number().nullish()
Expand Down Expand Up @@ -94,7 +95,8 @@ export default async function process(chainId: number, address: `0x${string}`, d
const allocator = await projectDebtAllocator(chainId, address)

const debts = await extractDebts(chainId, address, strategies, allocator)
const composition = await extractComposition(chainId, address, strategies, debts)
const estimatedApr = await getLatestEstimatedAprV3(chainId, address)
const composition = await extractComposition(chainId, address, strategies, debts, estimatedApr?.type)
const fees = await extractFeesBps(chainId, address, snapshot)
const risk = await getRiskScore(chainId, address)
const meta = await getVaultMeta(chainId, address)
Expand Down Expand Up @@ -123,7 +125,6 @@ export default async function process(chainId: number, address: `0x${string}`, d

const apy = await getLatestApy(chainId, address)
const [oracleApr, oracleApy] = await getLatestOracleApr(chainId, address)
const estimatedApr = await getLatestEstimatedAprV3(chainId, address)

// Query DB for staking pool associated with this vault
const stakingPool = await db.query(`
Expand Down Expand Up @@ -345,20 +346,22 @@ export async function extractDebts(chainId: number, vault: `0x${string}`, strate
async function fetchStrategySnapshots(chainId: number, strategies: `0x${string}`[]) {
if (strategies.length === 0) return []

const lowerStrategies = strategies.map(s => s.toLowerCase())
const result = await db.query(`
SELECT
address,
snapshot->>'name' as name,
hook->'performance' as performance,
hook->'lastReportDetail'->'apr'->>'net' as "latestReportApr"
FROM snapshot
WHERE chain_id = $1 AND address = ANY($2)
`, [chainId, strategies])
WHERE chain_id = $1 AND LOWER(address) = ANY($2)
`, [chainId, lowerStrategies])

return z.object({
address: zhexstring,
name: z.string().nullish(),
performance: z.object({
estimated: EstimatedAprSchema.nullish(),
oracle: z.object({
apr: z.number().nullish(),
apy: z.number().nullish()
Expand All @@ -374,11 +377,42 @@ async function fetchStrategySnapshots(chainId: number, strategies: `0x${string}`
}).array().parse(result.rows)
}

async function fetchStrategyPerformance(
chainId: number,
strategies: `0x${string}`[],
estimatedAprLabel?: string
) {
const entries = await Promise.all(strategies.map(async strategy => {
const [oracleApr, oracleApy] = await getLatestOracleApr(chainId, strategy.toLowerCase())
const apy = await getLatestApy(chainId, strategy.toLowerCase())
const estimated = await getLatestEstimatedAprV3(chainId, strategy, estimatedAprLabel)

const performance = {
...(estimated ? { estimated } : {}),
oracle: {
apr: oracleApr,
apy: oracleApy
},
historical: {
net: apy?.net ?? null,
weeklyNet: apy?.weeklyNet ?? null,
monthlyNet: apy?.monthlyNet ?? null,
inceptionNet: apy?.inceptionNet ?? null
}
}

return { strategy: strategy.toLowerCase(), performance }
}))

return new Map(entries.map(entry => [entry.strategy, entry.performance]))
}

export async function extractComposition(
chainId: number,
vault: `0x${string}`,
strategies: `0x${string}`[],
debts: Awaited<ReturnType<typeof extractDebts>>
debts: Awaited<ReturnType<typeof extractDebts>>,
estimatedAprLabel?: string
) {
// Fetch vault snapshot data for queue context
const vaultSnapshot = await db.query(`
Expand All @@ -397,6 +431,7 @@ export async function extractComposition(

// Batch-fetch strategy snapshots for name and APR
const strategySnapshots = await fetchStrategySnapshots(chainId, strategies)
const strategyPerformanceMap = await fetchStrategyPerformance(chainId, strategies, estimatedAprLabel)

const composition: z.infer<typeof CompositionSchema>[] = []

Expand All @@ -414,6 +449,14 @@ export async function extractComposition(
// Parse latestReportApr
const latestReportApr = snapshot?.latestReportApr ? parseFloat(snapshot.latestReportApr) : null

const strategyPerformance = strategyPerformanceMap.get(strategy.toLowerCase())
const performance = (snapshot?.performance || strategyPerformance)
? {
...(snapshot?.performance ?? {}),
...(strategyPerformance ?? {})
}
: undefined

// Compute status based on debt and queue membership
let status: 'active' | 'inactive' | 'unallocated'
if (debt && debt.currentDebt > 0n) {
Expand All @@ -428,7 +471,7 @@ export async function extractComposition(
address: strategy,
name,
status,
performance: snapshot?.performance ?? undefined,
performance,
latestReportApr,
activation: debt?.activation ?? 0n,
lastReport: debt?.lastReport ?? 0n,
Expand Down
59 changes: 38 additions & 21 deletions packages/ingest/helpers/apy-apr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,40 @@ import { EstimatedAprSchema } from 'lib/types'
import { z } from 'zod'
import db, { firstRow } from '../db'

export async function getLatestEstimatedAprV3(chainId: number, address: string) {
const result = await db.query(`
SELECT label, component, value
FROM output
WHERE block_time = (
SELECT block_time FROM output
WHERE chain_id = $1
AND address = $2
AND label LIKE '%-estimated-apr'
AND block_time > NOW() - INTERVAL '7 days'
ORDER BY block_time DESC
LIMIT 1
)
AND chain_id = $1
AND address = $2
AND label LIKE '%-estimated-apr'
`, [chainId, address])
export async function getLatestEstimatedAprV3(chainId: number, address: string, label?: string) {
const result = label
? await db.query(`
SELECT label, component, value
FROM output
WHERE block_time = (
SELECT block_time FROM output
WHERE chain_id = $1
AND LOWER(address) = LOWER($2)
AND label = $3
AND block_time > NOW() - INTERVAL '7 days'
ORDER BY block_time DESC
LIMIT 1
)
AND chain_id = $1
AND LOWER(address) = LOWER($2)
AND label = $3
`, [chainId, address, label])
: await db.query(`
SELECT label, component, value
FROM output
WHERE block_time = (
SELECT block_time FROM output
WHERE chain_id = $1
AND LOWER(address) = LOWER($2)
AND label LIKE '%-estimated-apr'
AND block_time > NOW() - INTERVAL '7 days'
ORDER BY block_time DESC
LIMIT 1
)
AND chain_id = $1
AND LOWER(address) = LOWER($2)
AND label LIKE '%-estimated-apr'
`, [chainId, address])

if (!result.rows.length) return undefined

Expand Down Expand Up @@ -115,11 +132,11 @@ export async function getLatestApy(chainId: number, address: string) {
WHERE block_time = (
SELECT MAX(block_time) FROM output
WHERE chain_id = $1
AND address = $2
AND LOWER(address) = LOWER($2)
AND label = 'apy-bwd-delta-pps'
)
AND chain_id = $1
AND address = $2
AND LOWER(address) = LOWER($2)
AND label = 'apy-bwd-delta-pps'
GROUP BY chain_id, address, label, block_number, block_time;
`, [chainId, address])
Expand Down Expand Up @@ -157,11 +174,11 @@ export async function getLatestOracleApr(chainId: number, address: string): Prom
WHERE block_time = (
SELECT MAX(block_time) FROM output
WHERE chain_id = $1
AND address = $2
AND LOWER(address) = LOWER($2)
AND label = 'apr-oracle'
)
AND chain_id = $1
AND address = $2
AND LOWER(address) = LOWER($2)
AND label = 'apr-oracle'
GROUP BY chain_id, address, label, block_number, block_time;
`, [chainId, address])
Expand Down
Loading