Skip to content

Commit

Permalink
Add core logic for event fetching
Browse files Browse the repository at this point in the history
  • Loading branch information
surbhit14 committed Oct 17, 2024
1 parent ef089da commit 2d7eefb
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 7 deletions.
126 changes: 125 additions & 1 deletion packages/api/src/routes/operators/operatorController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { WithAdditionalDataQuerySchema } from '../../schema/zod/schemas/withAddi
import { SortByQuerySchema } from '../../schema/zod/schemas/sortByQuery'
import { SearchByTextQuerySchema } from '../../schema/zod/schemas/searchByTextQuery'
import { WithRewardsQuerySchema } from '../../schema/zod/schemas/withRewardsQuery'
import { OperatorEventQuerySchema } from '../../schema/zod/schemas/operatorevents'
import { OperatorEventQuerySchema } from '../../schema/zod/schemas/operatorEvents'
import { handleAndReturnErrorResponse } from '../../schema/errors'
import { fetchRewardTokenPrices, fetchStrategyTokenPrices } from '../../utils/tokenPrices'
import {
Expand Down Expand Up @@ -394,6 +394,71 @@ export async function getOperatorRewards(req: Request, res: Response) {
}
}

/**
* Function for route /operators/:address/events
* Fetches and returns a list of events for a specific operator with optional filters
*
* @param req
* @param res
*/
export async function getOperatorEvents(req: Request, res: Response) {
const result = OperatorEventQuerySchema.and(PaginationQuerySchema).safeParse(req.query)
if (!result.success) {
return handleAndReturnErrorResponse(req, res, result.error)
}

try {
const { type, stakerAddress, strategyAddress, txHash, startAt, skip, take } = result.data
const { address } = req.params

const baseFilterQuery = {
operator: address,
...(stakerAddress && { staker: stakerAddress }),
...(strategyAddress && { strategy: strategyAddress }),
...(txHash && { transactionHash: txHash }),
...(startAt ? { blockTime: { gte: new Date(startAt) } } : {})
}

let eventRecords: EventRecord[] = []
let eventCount = 0

const eventTypesToFetch = type
? [type]
: strategyAddress
? ['shares increased', 'shares decreased']
: ['shares increased', 'shares decreased', 'delegation', 'undelegation']

const fetchEventsForTypes = async (types: string[]) => {
const results = await Promise.all(
types.map((eventType) => fetchAndMapEvents(eventType, baseFilterQuery, skip, take))
)
return results
}

const results = await fetchEventsForTypes(eventTypesToFetch)

eventRecords = results.flatMap((result) => result.eventRecords)
eventRecords = eventRecords
.sort((a, b) => (b.blockNumber > a.blockNumber ? 1 : -1))
.slice(0, take)

eventCount = results.reduce((acc, result) => acc + result.eventCount, 0)

const response = {
data: eventRecords,
meta: {
total: eventCount,
skip,
take
}
}

res.send(response)
} catch (error) {
handleAndReturnErrorResponse(req, res, error)
}
}

/**
* Function for route /operators/:address/invalidate-metadata
* Protected route to invalidate the metadata of a given Operator
Expand Down Expand Up @@ -640,3 +705,62 @@ export function isSpecialToken(tokenAddress: string): boolean {

return specialTokens.includes(tokenAddress.toLowerCase())
}

/**
* Utility function to fetch and map event records from the database.
*
* @param eventType
* @param baseFilterQuery
* @param skip
* @param take
* @returns
*/
async function fetchAndMapEvents(
eventType: string,
baseFilterQuery: any,
skip: number,
take: number
): Promise<{ eventRecords: EventRecord[]; eventCount: number }> {
const modelName = (() => {
switch (eventType) {
case 'shares increased':
return 'eventLogs_OperatorSharesIncreased'
case 'shares decreased':
return 'eventLogs_OperatorSharesDecreased'
case 'delegation':
return 'eventLogs_StakerDelegated'
case 'undelegation':
return 'eventLogs_StakerUndelegated'
default:
throw new Error(`Unknown event type: ${eventType}`)
}
})()

const model = prisma[modelName] as any

const eventCount = await model.count({
where: baseFilterQuery
})

const eventRecords = await model.findMany({
where: baseFilterQuery,
skip,
take,
orderBy: { blockNumber: 'desc' }
})

return {
eventRecords: eventRecords.map((event) => ({
type: eventType,
tx: event.transactionHash,
blockNumber: event.blockNumber,
blockTime: event.blockTime,
args: {
staker: event.staker,
strategy: event.strategy,
shares: event.shares
}
})),
eventCount
}
}
9 changes: 3 additions & 6 deletions packages/api/src/schema/zod/schemas/operatorEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,14 @@ export const OperatorEventQuerySchema = z
})
.refine(
(data) => {
if (
(data.type === 'delegation' || data.type === 'undelegation') &&
(data.strategyAddress || data.stakerAddress)
) {
if ((data.type === 'delegation' || data.type === 'undelegation') && data.strategyAddress) {
return false
}
return true
},
{
message:
'Neither strategyAddress nor stakerAddress should be provided for delegation or undelegation event types.',
path: ['strategyAddress', 'stakerAddress']
'strategyAddress filter is not supported for delegation or undelegation event types.',
path: ['strategyAddress']
}
)

0 comments on commit 2d7eefb

Please sign in to comment.