8000 feat: add forward APY calculation for vaults by matheus1lva · Pull Request #229 · yearn/kong · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

feat: add forward APY calculation for vaults #229

8000
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
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
1 change: 0 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ HTTP_FULLNODE_250=
HTTP_FULLNODE_8453=
HTTP_FULLNODE_42161=

YDAEMON_API=

YPRICE_ENABLED=
YPRICE_API=
Expand Down
153 changes: 147 additions & 6 deletions bun.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"devDependencies": {
"@testcontainers/postgresql": "^10.18.0",
"@testcontainers/redis": "^10.18.0",
"bun-types": "^1.2.1"
"bun-types": "^1.2.1",
"ts-node": "^10.9.2"
},
"dependencies": {
"@types/bun": "^1.2.2"
Expand Down
164 changes: 164 additions & 0 deletions packages/ingest/abis/yearn/3/vault/timeseries/fapy/hook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import { Output, OutputSchema, SnapshotSchema, StrategySchema, StrategyThing, VaultThingsWithName, VaultThingsWithNameSchema } from 'lib/types'
import { Data } from '../../../../../../extract/timeseries'
import { first, query } from '../../../../../../db'
import { computeChainAPY } from '../../../../../../forward'
import { multicall3 } from 'lib'
import { getBlock, estimateHeight } from 'lib/blocks'

export const outputLabel = 'fapy'

export default async function process(chainId: number, address: `0x${string}`, data: Data): Promise<Output[]> {
console.info('Fapy 🧮', data.outputLabel, chainId, address, (new Date(Number(data.blockTime) * 1000)).toDateString())

let blockNumber: bigint = 0n
if(data.blockTime >= BigInt(Math.floor(new Date().getTime() / 1000))) {
blockNumber = (await getBlock(chainId)).number
} else {
blockNumber = await estimateHeight(chainId, data.blockTime)
}

if(!multicall3.supportsBlock(chainId, blockNumber)) {
console.warn('🚨', 'block not supported', chainId, blockNumber)
return []
}

const vault = await first<VaultThingsWithName>(VaultThingsWithNameSchema,
`select thing.*, snapshot.snapshot->>'name' as name
from thing
join snapshot on thing.chain_id = snapshot.chain_id and thing.address = snapshot.address
where thing.chain_id = $1 AND thing.address = $2 AND thing.label = $3 AND (thing.defaults->>'yearn')::boolean = true`,
[chainId, address, 'vault']
)

if (!vault) return []

let strategies: StrategyThing[] = []

const snapshot = await first(SnapshotSchema, `
SELECT *
FROM snapshot
WHERE address = $1
`, [vault.address])

strategies = await query(StrategySchema, `
SELECT * FROM thing
WHERE chain_id = $1
AND label = $2
AND address = ANY($3)
`, [chainId, 'strategy', snapshot.hook.withdrawalQueue ?? snapshot.hook.strategies])

const strategiesWithIndicators = await Promise.all(strategies.map(async (strategy) => {
const strategySnapshot = await first(SnapshotSchema, `
SELECT *
FROM snapshot
WHERE address = $1
`, [strategy.address])

return {
...strategy,
...strategySnapshot?.snapshot,
name: strategySnapshot?.snapshot.name,
token: strategySnapshot?.snapshot.token,
symbol: strategySnapshot?.snapshot.symbol,
rewards: strategySnapshot?.snapshot.rewards,
guardian: strategySnapshot?.snapshot.guardian,
blockTime: Number(strategySnapshot?.snapshot.blockTime),
totalDebt: BigInt(snapshot?.snapshot.totalDebt),
totalIdle: BigInt(snapshot?.snapshot.totalIdle),
debtRatio: Number(snapshot?.snapshot.debtRatio),
decimals: Number(snapshot?.snapshot.decimals),
management: snapshot?.snapshot.management,
blockNumber: BigInt(snapshot?.snapshot.blockNumber),
totalAssets: BigInt(snapshot?.snapshot.totalAssets),
totalSupply: BigInt(snapshot?.snapshot.totalSupply),
depositLimit: BigInt(snapshot?.snapshot.depositLimit),
lockedProfit: BigInt(snapshot?.snapshot.lockedProfit),
managementFee: Number(snapshot?.snapshot.managementFee),
pricePerShare: BigInt(snapshot?.snapshot.pricePerShare),
expectedReturn: BigInt(snapshot?.snapshot.expectedReturn),
performanceFee: Number(snapshot?.snapshot.performanceFee),
creditAvailable: BigInt(snapshot?.snapshot.creditAvailable),
debtOutstanding: BigInt(snapshot?.snapshot.debtOutstanding),
DOMAIN_SEPARATOR: snapshot?.snapshot.DOMAIN_SEPARATOR,
emergencyShutdown: snapshot?.snapshot.emergencyShutdown,
maxAvailableShares: BigInt(snapshot?.snapshot.maxAvailableShares),
availableDepositLimit: BigInt(snapshot?.snapshot.availableDepositLimit),
lockedProfitDegradation: BigInt(snapshot?.snapshot.lockedProfitDegradation),
localKeepCRV: BigInt(strategySnapshot?.snapshot.localKeepCRV),
apiVersion: strategySnapshot?.snapshot.apiVersion
}
}))

const vaultAPY = await computeChainAPY(vault, 1, strategiesWithIndicators)

if(vaultAPY) {
return OutputSchema.array().parse([
{
chainId, address, label: data.outputLabel, component: 'vaultAPRType',
blockNumber, blockTime: data.blockTime, value: vaultAPY.type
},
{
chainId, address, label: data.outputLabel, component: 'vaultPointsWeekAgo',
blockNumber, blockTime: data.blockTime, value: vaultAPY.points?.weekAgo
},
{
chainId, address, label: data.outputLabel, component: 'vaultPointsMonthAgo',
blockNumber, blockTime: data.blockTime, value: vaultAPY.points?.monthAgo
},
{
chainId, address, label: data.outputLabel, component: 'vaultPointsInception',
blockNumber, blockTime: data.blockTime, value: vaultAPY.points?.inception
},
{
chainId, address, label: data.outputLabel, component: 'vaultPricePerShareToday',
blockNumber, blockTime: data.blockTime, value: vaultAPY.pricePerShare?.today
},
{
chainId, address, label: data.outputLabel, component: 'vaultPricePerShareWeekAgo',
blockNumber, blockTime: data.blockTime, value: vaultAPY.pricePerShare?.weekAgo
},
{
chainId, address, label: data.outputLabel, component: 'vaultPricePerShareMonthAgo',
blockNumber, blockTime: data.blockTime, value: vaultAPY.pricePerShare?.monthAgo
},
{
chainId, address, label: data.outputLabel, component: 'vaultFeesPerformance',
blockNumber, blockTime: data.blockTime, value: vaultAPY.fees?.performance
},
{
chainId, address, label: data.outputLabel, component: 'vaultFeesManagement',
blockNumber, blockTime: data.blockTime, value: vaultAPY.fees?.management
},
{
chainId, address, label: data.outputLabel, component: 'forwardAPRType',
blockNumber, blockTime: data.blockTime, value: vaultAPY.forwardAPY?.type
},
{
chainId, address, label: data.outputLabel, component: 'forwardNetAPY',
blockNumber, blockTime: data.blockTime, value: Number(vaultAPY.forwardAPY?.netAPY)
}, {
chainId, address, label: data.outputLabel, component: 'forwardBoost',
blockNumber, blockTime: data.blockTime, value: Number(vaultAPY.forwardAPY?.boost)
}, {
chainId, address, label: data.outputLabel, component: 'poolAPY',
blockNumber, blockTime: data.blockTime, value: Number(vaultAPY.forwardAPY?.poolAPY)
}, {
chainId, address, label: data.outputLabel, component: 'boostedAPR',
blockNumber, blockTime: data.blockTime, value: Number(vaultAPY.forwardAPY?.boostedAPR)
}, {
chainId, address, label: data.outputLabel, component: 'baseAPR',
blockNumber, blockTime: data.blockTime, value: Number(vaultAPY.forwardAPY?.baseAPR)
}, {
chainId, address, label: data.outputLabel, component: 'rewardsAPY',
blockNumber, blockTime: data.blockTime, value: Number(vaultAPY.forwardAPY?.rewardsAPY)
}, {
chainId, address, label: data.outputLabel, component: 'cvxAPR',
blockNumber, blockTime: data.blockTime, value: Number(vaultAPY.forwardAPY?.cvxAPR)
}, {
chainId, address, label: data.outputLabel, component: 'keepCRV',
blockNumber, blockTime: data.blockTime, value: Number(vaultAPY.forwardAPY?.keepCRV)
}])
}

return []
}
56 changes: 56 additions & 0 deletions packages/ingest/debug-fapy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Load environment variables first
import 'lib/global'
import path from 'path'
import dotenv from 'dotenv'

const envPath = path.join(__dirname, '../..', '.env')
dotenv.config({ path: envPath })

// Other imports after environment is loaded
import { rpcs } from 'lib/rpcs'
import process, { outputLabel } from './abis/yearn/3/vault/timeseries/fapy/hook'
import { Data } from './extract/timeseries'
import { cache } from 'lib'
import 'lib/global'

// Vault data provided by the user
const VAULT_ADDRESS = '0xf165a634296800812B8B0607a75DeDdcD4D3cC88' as `0x${string}`
const CHAIN_ID = 1 // Ethereum mainnet

// Create sample data
const sampleData: Data = {
10000 abiPath: 'yearn/3/vault',
chainId: CHAIN_ID,
address: VAULT_ADDRESS,
outputLabel,
blockTime: BigInt(Math.floor(Date.now() / 1000) - 3600)
}

// Enable more detailed logging
console.debug = console.log

// Add debugging points
const run = async () => {
await rpcs.up()
await cache.up()
console.log('Starting debugger for fapy hook')
console.log('Input parameters:', {
chainId: CHAIN_ID,
address: VAULT_ADDRESS,
blockTime: new Date(Number(sampleData.blockTime) * 1000).toISOString()
})

try {
const result = await process(CHAIN_ID, VAULT_ADDRESS, sampleData)
console.log('Result:', result)
return []
} catch (error) {
console.error('Error during processing:', error)
if (error instanceof Error) {
console.error('Error stack:', error.stack)
}
return
}
}

run()
2 changes: 1 addition & 1 deletion packages/ingest/extract/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default class Extract implements Processor {
[mq.job.extract.waveydb.name]: new WaveyDbExtractor(),
[mq.job.extract.snapshot.name]: new SnapshotExtractor(),
[mq.job.extract.timeseries.name]: new TimeseriesExtractor(),
[mq.job.extract.manuals.name]: new ManualsExtractor()
[mq.job.extract.manuals.name]: new ManualsExtractor(),
}

async up() {
Expand Down
4 changes: 2 additions & 2 deletions packages/ingest/fanout/timeseries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default class TimeseriesFanout {
const outputLabel = hook.module.outputLabel

const from = startBlock !== undefined
? startBlock
? startBlock
: math.max(inceptBlock, defaultStartBlockNumber, multicall3Activation)
const to = endBlock !== undefined ? endBlock : await getBlockNumber(chainId)
const start = endOfDay(await getBlockTime(chainId, from))
Expand All @@ -31,7 +31,7 @@ export default class TimeseriesFanout {
SELECT DISTINCT block_time
FROM output
WHERE chain_id = $1 AND address = $2 5804 AND label = $3
ORDER BY block_time ASC`,
ORDER BY block_time ASC`,
[chainId, address, outputLabel]))
.rows.map(row => BigInt(row.block_time))

Expand Down
Loading
0