Skip to content

Commit f6ab526

Browse files
committed
pool booster
1 parent 1945ce1 commit f6ab526

File tree

5 files changed

+100
-21
lines changed

5 files changed

+100
-21
lines changed

.cursorrules

+43-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,43 @@
1-
- We are a subsquid project which extracts data from EVM blockchains.
2-
- We reference `README.md` for instructions on how to use and contribute to our project.
3-
- When saving entities, use `.insert()` or `.upsert()`.
1+
# Origin Protocol Squid Project Rules
2+
3+
## Database Operations
4+
5+
- Use `.insert()` or `.upsert()` for saving entities
6+
- Batch database operations by collecting entities in maps and saving at the end of context processing
7+
- When fetching and modifying entities from the database, store them in a map for later upserting
8+
9+
## GraphQL Schema
10+
11+
- Order fields with indexed fields first
12+
- Include clear comments for complex ID formats
13+
- Use descriptive names for entities prefixed with their context (e.g., PoolBooster\*)
14+
- Only add @index directive when the field needs to be queried
15+
16+
## Event Processing
17+
18+
- Create separate filter for each event type
19+
- Use descriptive filter names that match the event (e.g., feeCollectedFilter)
20+
- Decode events using their respective ABI definitions
21+
- Handle each event in its own conditional block
22+
- Create helper functions for repeated operations (e.g., getCampaign)
23+
24+
## Code Organization
25+
26+
- Group related event handlers together
27+
- Keep processor setup clean and organized
28+
- Use clear variable names that indicate their purpose
29+
- Use maps to collect entities during processing
30+
- Add TODO comments for future improvements or validations
31+
32+
## Error Handling
33+
34+
- Validate entity existence before updates
35+
- Use optional chaining and null checks where appropriate
36+
- Add appropriate error logging
37+
38+
## Performance
39+
40+
- Minimize database operations by batching
41+
- Use maps for in-memory entity tracking
42+
- Only fetch data that's needed
43+
- Reuse entities from memory when possible

src/main-base.ts

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { base } from 'viem/chains'
33

44
import { defineSquidProcessor, run } from '@originprotocol/squid-utils'
55
import * as exchangeRatesPostProcessor from '@shared/post-processors/exchange-rates'
6+
import { createCurvePoolBoosterProcessor } from '@templates/otoken/curve-pool-booster'
67
import { processStatus } from '@templates/processor-status'
78

89
import { baseERC20s, baseStrategies, bridgedWoethStrategy, superOETHb } from './base'
@@ -20,6 +21,7 @@ export const processor = defineSquidProcessor({
2021
...aerodromeProcessors,
2122
bridgedWoethStrategy,
2223
exchangeRatesProcessor,
24+
createCurvePoolBoosterProcessor({ from: 26255636 }),
2325
],
2426
postProcessors: [exchangeRatesPostProcessor, processStatus('base')],
2527
validators: [validate],

src/main-mainnet.ts

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import * as exchangeRates from '@shared/post-processors/exchange-rates'
88
import { createESTracker } from '@templates/exponential-staking'
99
import { createFRRSProcessor } from '@templates/fixed-rate-rewards-source'
1010
import { createGovernanceProcessor } from '@templates/governance'
11+
import { createCurvePoolBoosterProcessor } from '@templates/otoken/curve-pool-booster'
1112
import { processStatus } from '@templates/processor-status'
1213
import { createTransactionProcessor } from '@templates/transactions'
1314
import {
@@ -48,6 +49,7 @@ export const processor = defineSquidProcessor({
4849
...originArmProcessors,
4950
// Defender Relayer for Lido ARM
5051
createTransactionProcessor({ from: 18744591, address: ['0x39878253374355dbcc15c86458f084fb6f2d6de7'] }),
52+
createCurvePoolBoosterProcessor({ from: 21000000 }),
5153
],
5254
postProcessors: [exchangeRates, dailyStats, processStatus('mainnet'), protocolProcessor],
5355
validators: [validate],

src/main-sonic.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@ import { sonic } from 'viem/chains'
55

66
import { defineSquidProcessor, run } from '@originprotocol/squid-utils'
77
import * as exchangeRatesPostProcessor from '@shared/post-processors/exchange-rates'
8+
import { createCurvePoolBoosterProcessor } from '@templates/otoken/curve-pool-booster'
89
import { processStatus } from '@templates/processor-status'
910

1011
import * as validate from './sonic/validate'
1112

1213
export const processor = defineSquidProcessor({
1314
chainId: sonic.id,
1415
stateSchema: 'sonic-processor',
15-
processors: [...OS, sonicStrategies],
16+
processors: [...OS, sonicStrategies, createCurvePoolBoosterProcessor({ from: 7436660 })],
1617
postProcessors: [exchangeRatesPostProcessor, processStatus('sonic')],
1718
validators: [validate],
1819
})

src/templates/otoken/curve-pool-booster.ts

+51-17
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { PoolBoosterCampaign, PoolBoosterFeeCollected, PoolBoosterTokensRescued
33
import { Context, blockFrequencyUpdater, defineProcessor, logFilter } from '@originprotocol/squid-utils'
44
import { EvmBatchProcessor } from '@subsquid/evm-processor'
55

6-
export const createCurvePoolBoosterProcessor = (params: { otokenAddress: string; from: number }) => {
6+
export const createCurvePoolBoosterProcessor = (params: { from: number }) => {
77
const frequencyUpdate = blockFrequencyUpdater({ from: params.from })
88

99
const feeCollectedFilter = logFilter({
@@ -26,21 +26,41 @@ export const createCurvePoolBoosterProcessor = (params: { otokenAddress: string;
2626
topic0: [curvePoolBoosterAbi.events.TokensRescued.topic],
2727
range: { from: params.from },
2828
})
29+
const rewardPerVoteUpdatedFilter = logFilter({
30+
topic0: [curvePoolBoosterAbi.events.RewardPerVoteUpdated.topic],
31+
range: { from: params.from },
32+
})
33+
const totalRewardAmountUpdatedFilter = logFilter({
34+
topic0: [curvePoolBoosterAbi.events.TotalRewardAmountUpdated.topic],
35+
range: { from: params.from },
36+
})
2937

3038
return defineProcessor({
3139
name: `curve-pool-booster`,
40+
from: params.from,
3241
setup: (processor: EvmBatchProcessor) => {
3342
processor.addLog(campaignCreatedFilter.value)
3443
processor.addLog(campaignIdUpdatedFilter.value)
3544
processor.addLog(campaignClosedFilter.value)
3645
processor.addLog(feeCollectedFilter.value)
3746
processor.addLog(tokensRescuedFilter.value)
47+
processor.addLog(rewardPerVoteUpdatedFilter.value)
48+
processor.addLog(totalRewardAmountUpdatedFilter.value)
3849
},
3950
process: async (ctx: Context) => {
4051
const campaigns = new Map<string, PoolBoosterCampaign>()
4152
const feesCollected = new Map<string, PoolBoosterFeeCollected>()
4253
const tokensRescued = new Map<string, PoolBoosterTokensRescued>()
4354

55+
const getCampaign = async (address: string): Promise<PoolBoosterCampaign | undefined> => {
56+
const id = `${ctx.chain.id}-${address}`
57+
let campaign = campaigns.get(id) || (await ctx.store.get(PoolBoosterCampaign, id))
58+
if (campaign) {
59+
campaigns.set(id, campaign)
60+
}
61+
return campaign
62+
}
63+
4464
for (const block of ctx.blocksWithContent) {
4565
for (const log of block.logs) {
4666
// TODO: Validate campaign feeCollector is in our whitelist.
@@ -55,34 +75,48 @@ export const createCurvePoolBoosterProcessor = (params: { otokenAddress: string;
5575
rewardToken: data.rewardToken,
5676
maxRewardPerVote: data.maxRewardPerVote,
5777
totalRewardAmount: data.totalRewardAmount,
78+
closed: false,
5879
})
5980
campaigns.set(id, campaign)
6081
} else if (campaignIdUpdatedFilter.matches(log)) {
6182
const data = curvePoolBoosterAbi.events.CampaignIdUpdated.decode(log)
62-
const id = `${ctx.chain.id}-${log.address}`
63-
let campaign = campaigns.get(id) || (await ctx.store.get(PoolBoosterCampaign, id))
83+
const campaign = await getCampaign(log.address)
6484
if (campaign) {
6585
campaign.campaignId = data.newId
6686
}
6787
} else if (campaignClosedFilter.matches(log)) {
68-
const id = `${ctx.chain.id}-${log.address}`
69-
let campaign = campaigns.get(id) || (await ctx.store.get(PoolBoosterCampaign, id))
88+
const campaign = await getCampaign(log.address)
7089
if (campaign) {
7190
campaign.closed = true
7291
}
92+
} else if (rewardPerVoteUpdatedFilter.matches(log)) {
93+
const data = curvePoolBoosterAbi.events.RewardPerVoteUpdated.decode(log)
94+
const campaign = await getCampaign(log.address)
95+
if (campaign) {
96+
campaign.maxRewardPerVote = data.newMaxRewardPerVote
97+
}
98+
} else if (totalRewardAmountUpdatedFilter.matches(log)) {
99+
const data = curvePoolBoosterAbi.events.TotalRewardAmountUpdated.decode(log)
100+
const campaign = await getCampaign(log.address)
101+
if (campaign) {
102+
campaign.totalRewardAmount += data.extraTotalRewardAmount
103+
}
73104
} else if (feeCollectedFilter.matches(log)) {
74-
const data = curvePoolBoosterAbi.events.FeeCollected.decode(log)
75-
const feeCollected = new PoolBoosterFeeCollected({
76-
id: `${ctx.chain.id}-${log.id}`,
77-
chainId: ctx.chain.id,
78-
address: log.address,
79-
feeCollector: data.feeCollector,
80-
feeAmount: data.feeAmount,
81-
timestamp: new Date(block.header.timestamp),
82-
blockNumber: block.header.height,
83-
txHash: log.transactionHash,
84-
})
85-
feesCollected.set(feeCollected.id, feeCollected)
105+
const campaign = await getCampaign(log.address)
106+
if (campaign) {
107+
const data = curvePoolBoosterAbi.events.FeeCollected.decode(log)
108+
const feeCollected = new PoolBoosterFeeCollected({
109+
id: `${ctx.chain.id}-${log.id}`,
110+
chainId: ctx.chain.id,
111+
address: log.address,
112+
feeCollector: data.feeCollector,
113+
feeAmount: data.feeAmount,
114+
timestamp: new Date(block.header.timestamp),
115+
blockNumber: block.header.height,
116+
txHash: log.transactionHash,
117+
})
118+
feesCollected.set(feeCollected.id, feeCollected)
119+
}
86120
} else if (tokensRescuedFilter.matches(log)) {
87121
const data = curvePoolBoosterAbi.events.TokensRescued.decode(log)
88122
const tokensRescuedEvent = new PoolBoosterTokensRescued({

0 commit comments

Comments
 (0)