Skip to content

Commit e4c6be9

Browse files
committed
fix: update incentive timing logic, incentive data queries and epoch context structure
1 parent 32803d8 commit e4c6be9

File tree

1 file changed

+37
-86
lines changed

1 file changed

+37
-86
lines changed

src/adaptors/hermes-v2/index.ts

Lines changed: 37 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ const ADDRESSES = {
3636
// Epoch timing
3737
const WEEK_SECONDS = 7 * 24 * 60 * 60;
3838
const YEAR_IN_SECONDS = 31_536_000;
39-
const INCENTIVE_OFFSET = 12 * 60 * 60; // Incentives start 12 hours after activePeriod
4039

4140
// ============================================================================
4241
// EPOCH CONTEXT
@@ -46,7 +45,8 @@ interface EpochContext {
4645
currentBlock: number;
4746
timestamp: number | null;
4847
epochStartTime: number; // from getIncentiveStartTime()
49-
epochStartBlock: number; // from getEpochStartBlock()
48+
epochEndTime: number; // from getIncentiveStartTime() + WEEK_SECONDS
49+
epochStartBlock: number;
5050
}
5151

5252
// ============================================================================
@@ -55,56 +55,11 @@ interface EpochContext {
5555

5656
/**
5757
* Get incentive epoch start time for a given timestamp/block
58-
* This is activePeriod + 12 hours (incentives are start at 12:00 UTC)
5958
*/
60-
const getIncentiveStartTime = async (timestamp = null, block = null) => {
61-
try {
62-
const result = await sdk.api.abi.call({
63-
target: ADDRESSES.BASE_V2_MINTER,
64-
abi: 'function activePeriod() external view returns (uint256)',
65-
chain: CHAIN,
66-
block,
67-
});
68-
return parseInt(result.output) + INCENTIVE_OFFSET;
69-
} catch (e) {
70-
// Fallback: compute manually using provided timestamp or current time
71-
const referenceTime = timestamp || Math.floor(Date.now() / 1000);
72-
return (
73-
Math.floor(referenceTime / WEEK_SECONDS) * WEEK_SECONDS + INCENTIVE_OFFSET
74-
);
75-
}
76-
};
77-
78-
/**
79-
* Get the block number when the current epoch started by querying the most recent Mint event
80-
*/
81-
const getEpochStartBlock = async (currentBlock) => {
82-
try {
83-
const mintTopic = ethers.utils.id('Mint(uint256,uint256,uint256,uint256)');
84-
85-
// Look back ~2 weeks and find the most recent Mint event
86-
const fromBlock = currentBlock - 5000000;
87-
88-
const logs = await sdk.api.util.getLogs({
89-
target: ADDRESSES.BASE_V2_MINTER,
90-
topic: '',
91-
toBlock: currentBlock,
92-
fromBlock: fromBlock > 0 ? fromBlock : 1,
93-
keys: [],
94-
chain: CHAIN,
95-
topics: [mintTopic],
96-
});
97-
98-
if (logs.output && logs.output.length > 0) {
99-
// Return the block number of the most recent Mint event
100-
return logs.output[logs.output.length - 1].blockNumber;
101-
}
59+
const getIncentiveStartTime = async (timestamp = null) => {
60+
const referenceTime = timestamp || Math.floor(Date.now() / 1000);
10261

103-
// Fallback: use ~1 week lookback
104-
return currentBlock - 2400000;
105-
} catch (e) {
106-
return currentBlock - 2400000;
107-
}
62+
return Math.trunc((referenceTime - 43200) / 604800) * 604800 + 43200;
10863
};
10964

11065
/**
@@ -114,11 +69,21 @@ const getEpochContext = async (
11469
timestamp: number | null,
11570
currentBlock: number
11671
): Promise<EpochContext> => {
117-
const [epochStartTime, epochStartBlock] = await Promise.all([
118-
getIncentiveStartTime(timestamp, currentBlock),
119-
getEpochStartBlock(currentBlock),
120-
]);
121-
return { currentBlock, timestamp, epochStartTime, epochStartBlock };
72+
const epochStartTime = await getIncentiveStartTime(timestamp);
73+
const [epochStartBlock] = await utils.getBlocksByTime(
74+
[epochStartTime],
75+
CHAIN
76+
);
77+
78+
const epochEndTime = epochStartTime + WEEK_SECONDS;
79+
80+
return {
81+
currentBlock,
82+
timestamp,
83+
epochStartTime,
84+
epochEndTime,
85+
epochStartBlock,
86+
};
12287
};
12388

12489
/**
@@ -506,44 +471,29 @@ const getGaugeAllocation = async (gaugeAddress, block = null) => {
506471
* Get incentive data from QueueRewards events on FlywheelGaugeRewards emitted at epoch start
507472
*/
508473
const getIncentiveFromEvents = async (
509-
gaugeAddress: string,
474+
incentiveId: string,
510475
epochContext: EpochContext
511476
) => {
512477
try {
513-
// QueueRewards event topic
514-
const queueRewardsTopic = ethers.utils.id('QueueRewards(address,uint256)');
515-
516-
// Filter by gauge address
517-
const gaugeAddressPadded = ethers.utils
518-
.hexZeroPad(gaugeAddress, 32)
519-
.toLowerCase();
520-
521-
const logs = await sdk.api.util.getLogs({
522-
target: ADDRESSES.FLYWHEEL_GAUGE_REWARDS,
523-
topic: '',
524-
toBlock: epochContext.currentBlock,
525-
fromBlock: epochContext.epochStartBlock,
526-
keys: [],
478+
const result = await sdk.api.abi.call({
479+
target: ADDRESSES.UNISWAP_V3_STAKER,
480+
abi: 'function incentives(bytes32 incentiveId) external view returns (uint256 totalRewardUnclaimed, uint160 totalSecondsClaimedX128, uint96 numberOfStakes)',
481+
params: [incentiveId],
527482
chain: CHAIN,
528-
topics: [queueRewardsTopic, gaugeAddressPadded],
483+
block: epochContext.epochStartBlock,
529484
});
530485

531-
if (!logs.output || logs.output.length === 0) {
532-
return null;
533-
}
534-
535-
// Get most recent QueueRewards event for this gauge
536-
const latestLog = logs.output[logs.output.length - 1];
537-
538-
// rewardAmount is in topics[2] (indexed parameter)
539-
const rewardBigInt = BigInt(latestLog.topics[2]);
540-
const reward = parseFloat(
541-
ethers.utils.formatUnits(rewardBigInt.toString(), 18)
486+
const totalRewardUnclaimed = BigInt(
487+
result.output.totalRewardUnclaimed || 0
542488
);
543489

544490
// Use epoch timing from context
545491
const endTime = epochContext.epochStartTime + WEEK_SECONDS;
546492

493+
const reward = parseFloat(
494+
ethers.utils.formatUnits(totalRewardUnclaimed.toString(), 18)
495+
);
496+
547497
return { startTime: epochContext.epochStartTime, endTime, reward };
548498
} catch (e) {
549499
console.error('Error getting incentive from events:', e.message);
@@ -811,7 +761,8 @@ const getPools = async (timestamp = null) => {
811761

812762
// Fetch all prices
813763
const tokens = Array.from(tokenSet);
814-
const prices = (await utils.getPrices(tokens, CHAIN, timestamp)).pricesByAddress;
764+
const prices = (await utils.getPrices(tokens, CHAIN, timestamp))
765+
.pricesByAddress;
815766

816767
// Extract HERMES price
817768
const hermesPrice = prices[ADDRESSES.HERMES.toLowerCase()] || 0;
@@ -838,7 +789,7 @@ const getPools = async (timestamp = null) => {
838789

839790
// Get incentive data from QueueRewards events
840791
const incentiveData = await getIncentiveFromEvents(
841-
gaugeAddress,
792+
incentiveId,
842793
epochContext
843794
);
844795

@@ -879,8 +830,8 @@ const getPools = async (timestamp = null) => {
879830
incentiveData.reward,
880831
hermesPrice,
881832
activeLiquidityUSD,
882-
incentiveData.startTime,
883-
incentiveData.endTime,
833+
epochContext.epochStartTime,
834+
epochContext.epochEndTime,
884835
timestamp
885836
);
886837
}

0 commit comments

Comments
 (0)