From 9d36fd92850a9a8ed20bdf5a8dfbdf9c3bddf801 Mon Sep 17 00:00:00 2001 From: Corey Rice Date: Tue, 10 Dec 2024 17:48:04 -0300 Subject: [PATCH] refactor: rename reward Speed to MarketReward and add missing details --- subgraphs/isolated-pools/schema.graphql | 21 +++- .../src/mappings/rewardsDistributor.ts | 102 +++++++++++++++++- .../isolated-pools/src/operations/create.ts | 5 +- .../isolated-pools/src/operations/get.ts | 16 ++- .../src/operations/getOrCreate.ts | 22 ++-- subgraphs/isolated-pools/src/utilities/ids.ts | 2 +- subgraphs/isolated-pools/template.yaml | 14 +++ .../tests/RewardsDistributor/index.test.ts | 34 +++--- .../tests/RewardsDistributor/mocks.ts | 4 + 9 files changed, 183 insertions(+), 37 deletions(-) diff --git a/subgraphs/isolated-pools/schema.graphql b/subgraphs/isolated-pools/schema.graphql index 27611f80..8a2e514a 100644 --- a/subgraphs/isolated-pools/schema.graphql +++ b/subgraphs/isolated-pools/schema.graphql @@ -249,13 +249,14 @@ type RewardsDistributor @entity { "Address of the reward token" reward: Bytes! "Distribution rate for suppliers" - rewardSpeeds: [RewardSpeed!]! @derivedFrom(field:"rewardsDistributor") + marketRewards: [MarketReward!]! @derivedFrom(field:"rewardsDistributor") + isTimeBased: Boolean! } """ A interface for rewards distributor that distribute rewards to isolated pools """ -type RewardSpeed @entity { +type MarketReward @entity { "ID created from the reward distributor and market this speed applies to" id: Bytes! "Address of rewards distributor" @@ -263,7 +264,19 @@ type RewardSpeed @entity { "Address of the market this speed applies to" market: Market! "Distribution rate for borrowers" - borrowSpeedPerBlockMantissa: BigInt + borrowSpeedPerBlockMantissa: BigInt! "Distribution rate for suppliers" - supplySpeedPerBlockMantissa: BigInt + supplySpeedPerBlockMantissa: BigInt! + "Index of the last supply state update" + supplyStateIndex: BigInt! + "Timestamp or block number of the last supply state update" + supplyStateBlockNumberOrTimestamp: BigInt! + "Index of the last borrow state update" + borrowStateIndex: BigInt! + "Timestamp or block number of the last borrow state update" + borrowStateBlockNumberOrTimestamp: BigInt! + "Supply last rewarding block or timestamp" + supplyLastRewardingBlockTimestamp: BigInt! + "Borrow last rewarding block or timestamp" + borrowLastRewardingBlockTimestamp: BigInt! } diff --git a/subgraphs/isolated-pools/src/mappings/rewardsDistributor.ts b/subgraphs/isolated-pools/src/mappings/rewardsDistributor.ts index 6e1559a0..e39502d4 100644 --- a/subgraphs/isolated-pools/src/mappings/rewardsDistributor.ts +++ b/subgraphs/isolated-pools/src/mappings/rewardsDistributor.ts @@ -1,17 +1,113 @@ +import { BigInt } from '@graphprotocol/graph-ts'; import { + MarketInitialized, RewardTokenBorrowSpeedUpdated, RewardTokenSupplySpeedUpdated, + RewardTokenSupplyIndexUpdated, + SupplyLastRewardingBlockUpdated, + BorrowLastRewardingBlockUpdated, + SupplyLastRewardingBlockTimestampUpdated, + BorrowLastRewardingBlockTimestampUpdated, } from '../../generated/templates/RewardsDistributor/RewardsDistributor'; -import { getOrCreateRewardSpeed } from '../operations/getOrCreate'; +import { RewardsDistributor as RewardDistributorContract } from '../../generated/templates/RewardsDistributor/RewardsDistributor'; +import { zeroBigInt32 } from '../constants'; +import exponentToBigInt from '../utilities/exponentToBigInt'; +import { getRewardDistributor } from '../operations/get'; +import { getOrCreateMarketReward } from '../operations/getOrCreate'; + +export function handleMarketInitialized(event: MarketInitialized): void { + const marketReward = getOrCreateMarketReward(event.address, event.params.vToken); + const rewardsDistributor = getRewardDistributor(event.address)!; + if (marketReward.supplyStateIndex.equals(zeroBigInt32)) { + marketReward.supplyStateIndex = BigInt.fromI64(exponentToBigInt(BigInt.fromI32(36))); + } + if (marketReward.borrowStateIndex.equals(zeroBigInt32)) { + marketReward.borrowStateIndex = BigInt.fromI64(exponentToBigInt(BigInt.fromI32(36))); + } + if (rewardsDistributor.isTimeBased) { + marketReward.supplyStateBlockNumberOrTimestamp = event.block.timestamp; + marketReward.borrowStateBlockNumberOrTimestamp = event.block.timestamp; + } else { + marketReward.supplyStateBlockNumberOrTimestamp = event.block.number; + marketReward.borrowStateBlockNumberOrTimestamp = event.block.number; + marketReward.save(); + } +} export function handleRewardTokenBorrowSpeedUpdated(event: RewardTokenBorrowSpeedUpdated): void { - const rewardSpeed = getOrCreateRewardSpeed(event.address, event.params.vToken); + const rewardSpeed = getOrCreateMarketReward(event.address, event.params.vToken); rewardSpeed.borrowSpeedPerBlockMantissa = event.params.newSpeed; rewardSpeed.save(); } export function handleRewardTokenSupplySpeedUpdated(event: RewardTokenSupplySpeedUpdated): void { - const rewardSpeed = getOrCreateRewardSpeed(event.address, event.params.vToken); + const rewardSpeed = getOrCreateMarketReward(event.address, event.params.vToken); rewardSpeed.supplySpeedPerBlockMantissa = event.params.newSpeed; rewardSpeed.save(); } + +export function handleRewardTokenSupplyIndexUpdated(event: RewardTokenSupplyIndexUpdated): void { + const rewardDistributorContract = RewardDistributorContract.bind(event.address); + const rewardsDistributor = getRewardDistributor(event.address)!; + const marketReward = getOrCreateMarketReward(event.address, event.params.vToken); + if (rewardsDistributor.isTimeBased) { + const supplyState = rewardDistributorContract.rewardTokenSupplyStateTimeBased( + event.params.vToken, + ); + marketReward.supplyStateIndex = supplyState.getIndex(); + marketReward.supplyStateBlockNumberOrTimestamp = supplyState.getTimestamp(); + } else { + const supplyState = rewardDistributorContract.rewardTokenSupplyState(event.params.vToken); + marketReward.supplyStateIndex = supplyState.getIndex(); + marketReward.supplyStateBlockNumberOrTimestamp = supplyState.getBlock(); + } +} + +export function handleRewardTokenBorrowIndexUpdated(event: RewardTokenSupplyIndexUpdated): void { + const rewardDistributorContract = RewardDistributorContract.bind(event.address); + const rewardsDistributor = getRewardDistributor(event.address)!; + const marketReward = getOrCreateMarketReward(event.address, event.params.vToken); + if (rewardsDistributor.isTimeBased) { + const borrowState = rewardDistributorContract.rewardTokenBorrowStateTimeBased( + event.params.vToken, + ); + marketReward.borrowStateIndex = borrowState.getIndex(); + marketReward.borrowStateBlockNumberOrTimestamp = borrowState.getTimestamp(); + } else { + const borrowState = rewardDistributorContract.rewardTokenBorrowState(event.params.vToken); + marketReward.borrowStateIndex = borrowState.getIndex(); + marketReward.borrowStateBlockNumberOrTimestamp = borrowState.getBlock(); + } +} + +export function handleSupplyLastRewardingBlockUpdated( + event: SupplyLastRewardingBlockUpdated, +): void { + const marketReward = getOrCreateMarketReward(event.address, event.params.vToken); + marketReward.supplyLastRewardingBlockTimestamp = event.params.newBlock; + marketReward.save(); +} + +export function handleBorrowLastRewardingBlockUpdated( + event: BorrowLastRewardingBlockUpdated, +): void { + const marketReward = getOrCreateMarketReward(event.address, event.params.vToken); + marketReward.borrowLastRewardingBlockTimestamp = event.params.newBlock; + marketReward.save(); +} + +export function handleSupplyLastRewardingBlockTimestampUpdated( + event: SupplyLastRewardingBlockTimestampUpdated, +): void { + const marketReward = getOrCreateMarketReward(event.address, event.params.vToken); + marketReward.supplyLastRewardingBlockTimestamp = event.params.newTimestamp; + marketReward.save(); +} + +export function handleBorrowLastRewardingBlockTimestampUpdated( + event: BorrowLastRewardingBlockTimestampUpdated, +): void { + const marketReward = getOrCreateMarketReward(event.address, event.params.vToken); + marketReward.borrowLastRewardingBlockTimestamp = event.params.newTimestamp; + marketReward.save(); +} diff --git a/subgraphs/isolated-pools/src/operations/create.ts b/subgraphs/isolated-pools/src/operations/create.ts index 1814ad29..89e9e33c 100644 --- a/subgraphs/isolated-pools/src/operations/create.ts +++ b/subgraphs/isolated-pools/src/operations/create.ts @@ -37,7 +37,7 @@ import { vWETHLiquidStakedETHAddress, vWETHCoreAddress, } from '../constants/addresses'; -import { getOrCreateRewardSpeed } from './getOrCreate'; +import { getOrCreateMarketReward } from './getOrCreate'; import { getTokenPriceInCents, valueOrNotAvailableIntIfReverted } from '../utilities'; import { getAccountId, @@ -311,6 +311,7 @@ export function createRewardDistributor( rewardsDistributor.address = rewardsDistributorAddress; rewardsDistributor.pool = comptrollerAddress; rewardsDistributor.reward = rewardToken; + rewardsDistributor.isTimeBased = rewardDistributorContract.isTimeBased(); rewardsDistributor.save(); // we get the current speeds for all known markets at this point in time @@ -321,7 +322,7 @@ export function createRewardDistributor( for (let i = 0; i < marketAddresses.length; i++) { const marketAddress = marketAddresses[i]; - const rewardSpeed = getOrCreateRewardSpeed(rewardsDistributorAddress, marketAddress); + const rewardSpeed = getOrCreateMarketReward(rewardsDistributorAddress, marketAddress); rewardSpeed.borrowSpeedPerBlockMantissa = rewardDistributorContract.rewardTokenBorrowSpeeds(marketAddress); rewardSpeed.supplySpeedPerBlockMantissa = diff --git a/subgraphs/isolated-pools/src/operations/get.ts b/subgraphs/isolated-pools/src/operations/get.ts index 1a85c721..001386ca 100644 --- a/subgraphs/isolated-pools/src/operations/get.ts +++ b/subgraphs/isolated-pools/src/operations/get.ts @@ -1,7 +1,12 @@ import { Address, log } from '@graphprotocol/graph-ts'; -import { MarketPosition, Market, Pool } from '../../generated/schema'; -import { getMarketPositionId, getMarketId, getPoolId } from '../utilities/ids'; +import { MarketPosition, Market, Pool, RewardsDistributor } from '../../generated/schema'; +import { + getMarketPositionId, + getRewardsDistributorId, + getMarketId, + getPoolId, +} from '../utilities/ids'; export const getPool = (comptroller: Address): Pool | null => { const pool = Pool.load(getPoolId(comptroller)); @@ -27,3 +32,10 @@ export const getMarketPosition = ( const marketPositionId = getMarketPositionId(accountAddress, marketAddress); return MarketPosition.load(marketPositionId); }; + +export const getRewardDistributor = ( + rewardsDistributorAddress: Address, +): RewardsDistributor | null => { + const id = getRewardsDistributorId(rewardsDistributorAddress); + return RewardsDistributor.load(id); +}; diff --git a/subgraphs/isolated-pools/src/operations/getOrCreate.ts b/subgraphs/isolated-pools/src/operations/getOrCreate.ts index 7cf96e8f..6cb9ff4c 100644 --- a/subgraphs/isolated-pools/src/operations/getOrCreate.ts +++ b/subgraphs/isolated-pools/src/operations/getOrCreate.ts @@ -7,7 +7,7 @@ import { MarketPosition, Market, Pool, - RewardSpeed, + MarketReward, RewardsDistributor, } from '../../generated/schema'; import { zeroBigInt32 } from '../constants'; @@ -15,7 +15,7 @@ import { getAccountPoolId, getMarketPositionId, getPoolId, - getRewardSpeedId, + getMarketRewardId, getRewardsDistributorId, } from '../utilities/ids'; import { @@ -104,21 +104,27 @@ export const getOrCreateMarketPosition = ( return { entity: marketPosition, created }; }; -export function getOrCreateRewardSpeed( +export function getOrCreateMarketReward( rewardsDistributorAddress: Address, marketAddress: Address, -): RewardSpeed { - const id = getRewardSpeedId(rewardsDistributorAddress, marketAddress); - let rewardSpeed = RewardSpeed.load(id); +): MarketReward { + const id = getMarketRewardId(rewardsDistributorAddress, marketAddress); + let rewardSpeed = MarketReward.load(id); if (!rewardSpeed) { - rewardSpeed = new RewardSpeed(id); + rewardSpeed = new MarketReward(id); rewardSpeed.rewardsDistributor = rewardsDistributorAddress; rewardSpeed.market = marketAddress; rewardSpeed.borrowSpeedPerBlockMantissa = zeroBigInt32; rewardSpeed.supplySpeedPerBlockMantissa = zeroBigInt32; + rewardSpeed.supplyStateIndex = zeroBigInt32; + rewardSpeed.supplyStateBlockNumberOrTimestamp = zeroBigInt32; + rewardSpeed.borrowStateIndex = zeroBigInt32; + rewardSpeed.borrowStateBlockNumberOrTimestamp = zeroBigInt32; + rewardSpeed.supplyLastRewardingBlockTimestamp = zeroBigInt32; + rewardSpeed.borrowLastRewardingBlockTimestamp = zeroBigInt32; rewardSpeed.save(); } - return rewardSpeed as RewardSpeed; + return rewardSpeed as MarketReward; } export const getOrCreateRewardDistributor = ( diff --git a/subgraphs/isolated-pools/src/utilities/ids.ts b/subgraphs/isolated-pools/src/utilities/ids.ts index c8de9b20..6ede4507 100644 --- a/subgraphs/isolated-pools/src/utilities/ids.ts +++ b/subgraphs/isolated-pools/src/utilities/ids.ts @@ -21,7 +21,7 @@ export const getBadDebtEventId = (transactionHash: Bytes, transactionLogIndex: B export const getRewardsDistributorId = (rewardsDistributor: Address): Bytes => rewardsDistributor; -export const getRewardSpeedId = ( +export const getMarketRewardId = ( rewardsDistributorAddress: Address, marketAddress: Address, ): Bytes => rewardsDistributorAddress.concat(marketAddress); diff --git a/subgraphs/isolated-pools/template.yaml b/subgraphs/isolated-pools/template.yaml index d96cea01..51e50dc0 100644 --- a/subgraphs/isolated-pools/template.yaml +++ b/subgraphs/isolated-pools/template.yaml @@ -172,7 +172,21 @@ templates: - name: RewardsDistributor file: ../../node_modules/@venusprotocol/isolated-pools/artifacts/contracts/Rewards/RewardsDistributor.sol/RewardsDistributor.json eventHandlers: + - event: MarketInitialized(indexed address) + handler: handleMarketInitialized - event: RewardTokenBorrowSpeedUpdated(indexed address,uint256) handler: handleRewardTokenBorrowSpeedUpdated - event: RewardTokenSupplySpeedUpdated(indexed address,uint256) handler: handleRewardTokenSupplySpeedUpdated + - event: RewardTokenSupplyIndexUpdated(indexed address) + handler: handleRewardTokenSupplyIndexUpdated + - event: RewardTokenBorrowIndexUpdated(indexed address,(uint256)) + handler: handleRewardTokenBorrowIndexUpdated + - event: SupplyLastRewardingBlockUpdated(indexed address,uint32) + handler: handleSupplyLastRewardingBlockUpdated + - event: BorrowLastRewardingBlockUpdated(indexed address,uint32) + handler: handleBorrowLastRewardingBlockUpdated + - event: SupplyLastRewardingBlockTimestampUpdated(indexed address,uint256) + handler: handleSupplyLastRewardingBlockTimestampUpdated + - event: BorrowLastRewardingBlockTimestampUpdated(indexed address,uint256) + handler: handleBorrowLastRewardingBlockTimestampUpdated diff --git a/subgraphs/isolated-pools/tests/RewardsDistributor/index.test.ts b/subgraphs/isolated-pools/tests/RewardsDistributor/index.test.ts index b3fa07d7..3e5dfa6e 100644 --- a/subgraphs/isolated-pools/tests/RewardsDistributor/index.test.ts +++ b/subgraphs/isolated-pools/tests/RewardsDistributor/index.test.ts @@ -16,7 +16,7 @@ import { handleRewardTokenBorrowSpeedUpdated, handleRewardTokenSupplySpeedUpdated, } from '../../src/mappings/rewardsDistributor'; -import { getRewardSpeedId } from '../../src/utilities/ids'; +import { getMarketRewardId } from '../../src/utilities/ids'; import { createNewRewardsDistributor } from '../Pool/events'; import { createVBep20AndUnderlyingMock } from '../VToken/mocks'; import { @@ -83,21 +83,21 @@ describe('Rewards Distributor', () => { handleRewardTokenBorrowSpeedUpdated(rewardTokenBorrowSpeedUpdatedEvent); - const rewardId = getRewardSpeedId(rewardsDistributorAddress, vTokenAddress).toHexString(); - assert.fieldEquals('RewardSpeed', rewardId, 'id', rewardId); - assert.fieldEquals('RewardSpeed', rewardId, 'market', vTokenAddress.toHexString()); + const rewardId = getMarketRewardId(rewardsDistributorAddress, vTokenAddress).toHexString(); + assert.fieldEquals('MarketReward', rewardId, 'id', rewardId); + assert.fieldEquals('MarketReward', rewardId, 'market', vTokenAddress.toHexString()); assert.fieldEquals( - 'RewardSpeed', + 'MarketReward', rewardId, 'rewardsDistributor', rewardsDistributorAddress.toHexString(), ); - assert.fieldEquals('RewardSpeed', rewardId, 'supplySpeedPerBlockMantissa', '0'); - assert.fieldEquals('RewardSpeed', rewardId, 'borrowSpeedPerBlockMantissa', newBorrowRate); + assert.fieldEquals('MarketReward', rewardId, 'supplySpeedPerBlockMantissa', '0'); + assert.fieldEquals('MarketReward', rewardId, 'borrowSpeedPerBlockMantissa', newBorrowRate); const rewardsDistributor = RewardsDistributor.load(rewardsDistributorAddress)!; - const rewardSpeeds = rewardsDistributor.rewardSpeeds.load(); - assert.stringEquals(rewardId, rewardSpeeds[0].id.toHexString()); + const marketRewards = rewardsDistributor.marketRewards.load(); + assert.stringEquals(rewardId, marketRewards[0].id.toHexString()); }); test('indexes new supply speed', () => { @@ -109,21 +109,21 @@ describe('Rewards Distributor', () => { ); handleRewardTokenSupplySpeedUpdated(rewardTokenSupplySpeedUpdatedEvent); - const rewardId = getRewardSpeedId(rewardsDistributorAddress, vTokenAddress).toHexString(); + const rewardId = getMarketRewardId(rewardsDistributorAddress, vTokenAddress).toHexString(); - assert.fieldEquals('RewardSpeed', rewardId, 'id', rewardId); - assert.fieldEquals('RewardSpeed', rewardId, 'market', vTokenAddress.toHexString()); + assert.fieldEquals('MarketReward', rewardId, 'id', rewardId); + assert.fieldEquals('MarketReward', rewardId, 'market', vTokenAddress.toHexString()); assert.fieldEquals( - 'RewardSpeed', + 'MarketReward', rewardId, 'rewardsDistributor', rewardsDistributorAddress.toHexString(), ); - assert.fieldEquals('RewardSpeed', rewardId, 'supplySpeedPerBlockMantissa', newSupplyRate); - assert.fieldEquals('RewardSpeed', rewardId, 'borrowSpeedPerBlockMantissa', '0'); + assert.fieldEquals('MarketReward', rewardId, 'supplySpeedPerBlockMantissa', newSupplyRate); + assert.fieldEquals('MarketReward', rewardId, 'borrowSpeedPerBlockMantissa', '0'); const rewardsDistributor = RewardsDistributor.load(rewardsDistributorAddress)!; - const rewardSpeeds = rewardsDistributor.rewardSpeeds.load(); - assert.stringEquals(rewardId, rewardSpeeds[0].id.toHexString()); + const marketRewards = rewardsDistributor.marketRewards.load(); + assert.stringEquals(rewardId, marketRewards[0].id.toHexString()); }); }); diff --git a/subgraphs/isolated-pools/tests/RewardsDistributor/mocks.ts b/subgraphs/isolated-pools/tests/RewardsDistributor/mocks.ts index 63d82ecf..243f765b 100644 --- a/subgraphs/isolated-pools/tests/RewardsDistributor/mocks.ts +++ b/subgraphs/isolated-pools/tests/RewardsDistributor/mocks.ts @@ -24,4 +24,8 @@ export const createRewardsDistributorMock = ( ) .withArgs([ethereum.Value.fromAddress(rewardTokenAddress)]) .returns([ethereum.Value.fromUnsignedBigInt(BigInt.fromString('30000000000'))]); + + createMockedFunction(rewardsDistributorAddress, 'isTimeBased', 'isTimeBased():(bool)').returns([ + ethereum.Value.fromBoolean(false), + ]); };