diff --git a/ponder.config.ts b/ponder.config.ts index a6b13c6..87fd16a 100644 --- a/ponder.config.ts +++ b/ponder.config.ts @@ -27,6 +27,16 @@ export default createConfig({ startBlock: FACTORY[baseSepolia.id].startBlock, interval: (60 * 60) / 2, // 1 hour }, + PriceSnapshotUpdate: { + network: 'baseSepolia', + startBlock: FACTORY[baseSepolia.id].startBlock + 1, // plus one to get snapshot after price update + interval: (60 * 60 * 24) / 2, // 1 day + }, + APRSnapshotUpdate: { + network: 'baseSepolia', + startBlock: FACTORY[baseSepolia.id].startBlock + 1, // plus one to get snapshot after price update + interval: (60 * 60 * 24) / 2, // 1 day + }, }, contracts: { Factory: { diff --git a/relations.schema.ts b/relations.schema.ts index afbcd05..6f3c953 100644 --- a/relations.schema.ts +++ b/relations.schema.ts @@ -9,6 +9,7 @@ import { liquidateBorrow, marketEntered, marketExited, + priceSnapshot, protocol, pToken, pTokenEMode, @@ -100,6 +101,8 @@ export const pTokenRelations = relations(pToken, ({ one, many }) => ({ }), userBalances: many(userBalance), many: many(pTokenEMode), + priceSnapshots: many(priceSnapshot), + aprSnapshots: many(priceSnapshot), })); export const marketEnteredRelations = relations(marketEntered, ({ one }) => ({ @@ -347,3 +350,17 @@ export const userEModeRelations = relations(userEMode, ({ one }) => ({ references: [protocol.id], }), })); + +export const priceSnapshotRelations = relations(priceSnapshot, ({ one }) => ({ + pToken: one(pToken, { + fields: [priceSnapshot.pTokenId], + references: [pToken.id], + }), +})); + +export const aprSnapshotRelations = relations(priceSnapshot, ({ one }) => ({ + pToken: one(pToken, { + fields: [priceSnapshot.pTokenId], + references: [pToken.id], + }), +})); diff --git a/src/currentPriceUpdate.ts b/src/block.ts similarity index 57% rename from src/currentPriceUpdate.ts rename to src/block.ts index d23fa07..4f9565e 100644 --- a/src/currentPriceUpdate.ts +++ b/src/block.ts @@ -1,8 +1,9 @@ import { ponder } from 'ponder:registry'; -import { protocol, pToken } from 'ponder:schema'; +import { aprSnapshot, priceSnapshot, protocol, pToken } from 'ponder:schema'; import { readMultiplePTokenPricesInfo } from './utils/multicalls'; import { formatEther } from 'viem'; import { MathSol } from './utils/math'; +import { bigint, timestamp } from 'ponder'; ponder.on('CurrentPriceUpdate:block', async ({ context, event }) => { // for some reason while using merge to do 1 SQL it return an error. @@ -45,3 +46,32 @@ ponder.on('CurrentPriceUpdate:block', async ({ context, event }) => { ) ); }); + +ponder.on('PriceSnapshotUpdate:block', async ({ context, event }) => { + const pTokens = await context.db.sql.select().from(pToken); + + const snapshotValues = pTokens.map(ptoken => ({ + id: `${ptoken.id}-${event.block.number}`, + pTokenId: ptoken.id, + price: ptoken.currentUnderlyingPrice, + timestamp: event.block.timestamp, + chainId: BigInt(context.network.chainId), + })); + + await context.db.insert(priceSnapshot).values(snapshotValues); +}); + +ponder.on('APRSnapshotUpdate:block', async ({ context, event }) => { + const pTokens = await context.db.sql.select().from(pToken); + + const snapshotValues = pTokens.map(ptoken => ({ + id: `${ptoken.id}-${event.block.number}`, + pTokenId: ptoken.id, + supplyRatePerSecond: ptoken.supplyRatePerSecond, + borrowRatePerSecond: ptoken.borrowRatePerSecond, + timestamp: event.block.timestamp, + chainId: BigInt(context.network.chainId), + })); + + await context.db.insert(aprSnapshot).values(snapshotValues); +}); diff --git a/src/utils/type.ts b/src/utils/type.ts index f22abc7..4d71151 100644 --- a/src/utils/type.ts +++ b/src/utils/type.ts @@ -3,7 +3,13 @@ import { config, EventNames } from 'ponder:registry'; export type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>; -export type ContractEvents = Exclude<EventNames, 'CurrentPriceUpdate:block'>; +export type BlockEventNames = + | 'CurrentPriceUpdate:block' + | 'PriceSnapshotUpdate:block' + | 'APRSnapshotUpdate:block'; -export type ContractEvent<name extends ContractEvents = ContractEvents> = - Virtual.Event<config['default'], name>; +export type ContractEventsName = Exclude<EventNames, BlockEventNames>; + +export type ContractEvent< + name extends ContractEventsName = ContractEventsName, +> = Virtual.Event<config['default'], name>; diff --git a/src/utils/underlying.ts b/src/utils/underlying.ts index 05fed52..6ae1525 100644 --- a/src/utils/underlying.ts +++ b/src/utils/underlying.ts @@ -1,9 +1,8 @@ import { Context, Event } from 'ponder:registry'; import { Address } from 'viem'; import { getAddressId } from './id'; -import { underlyingToken, pToken, protocol } from 'ponder:schema'; +import { underlyingToken } from 'ponder:schema'; import { readErc20Information } from './multicalls'; -import { OracleEngineAbi } from '../../abis/OracleEngineAbi'; export async function createIfNotExistsUnderlying( address: Address, diff --git a/tables.schema.ts b/tables.schema.ts index 8ae9a52..8982bca 100644 --- a/tables.schema.ts +++ b/tables.schema.ts @@ -258,3 +258,20 @@ export const userBalance = onchainTable('user_balance', t => ({ borrowAssets: t.bigint().notNull().default(0n), isCollateral: t.boolean().notNull().default(false), })); + +export const priceSnapshot = onchainTable('price_snapshot', t => ({ + id: t.text().primaryKey(), + chainId: t.bigint().notNull(), + pTokenId: t.text().notNull(), + timestamp: t.bigint().notNull(), + price: t.bigint().notNull(), +})); + +export const aprSnapshot = onchainTable('apr_snapshot', t => ({ + id: t.text().primaryKey(), + chainId: t.bigint().notNull(), + pTokenId: t.text().notNull(), + timestamp: t.bigint().notNull(), + borrowRatePerSecond: t.bigint().notNull(), + supplyRatePerSecond: t.bigint().notNull(), +}));