Skip to content

Commit

Permalink
Track boost rewards
Browse files Browse the repository at this point in the history
  • Loading branch information
prevostc committed Jun 24, 2024
1 parent 4f41be5 commit 5a7c397
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 19 deletions.
35 changes: 28 additions & 7 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ TODO: find a better name for this entity
type Classic @entity {
"The vault address"
id: Bytes!

"The protocol the vault belongs to"
protocol: Protocol!
"The vault"
Expand All @@ -124,15 +125,23 @@ type Classic @entity {

"The vault's share token"
vaultSharesToken: Token!

"The vault's underlying LP token"
underlyingToken: Token!

"The boost tokens of the vault. Tokens earned by staking in the boost. This is the source of truth for other tables reward ordering."
boostRewardTokens: [Token!]!

# ----- PRICES & STATS -----

"The total supply of the vault shares token in circulation. Express with `sharesToken.decimals` decimals."
vaultSharesTotalSupply: BigInt!

"LP token price in native at the time of the interaction. Expressed with 18 decimals."
"Latest LP token price in native we have seen. Expressed with 18 decimals."
underlyingToNativePrice: BigInt!
"Native token price at the time of the interaction. Expressed with 18 decimals."
"Latest boost token prices in native we have seen. Ordered by classic.boostRewardTokens. Expressed with 18 decimals."
boostRewardToNativePrices: [BigInt!]!
"Latest native token price we have seen. Expressed with 18 decimals."
nativeToUSDPrice: BigInt!

"Amount of underlying tokens in the vault"
Expand Down Expand Up @@ -180,9 +189,11 @@ type ClassicSnapshot @entity {
"The total supply of the vault shares token in circulation. Express with `sharesToken.decimals` decimals."
vaultSharesTotalSupply: BigInt!

"LP token price in native at the time of the interaction. Expressed with 18 decimals."
"Latest LP token price in native of this snapshot. Expressed with 18 decimals."
underlyingToNativePrice: BigInt!
"Native token price at the time of the interaction. Expressed with 18 decimals."
"Latest boost token prices in native of this snapshot. Ordered by classic.boostRewardTokens. Expressed with 18 decimals."
boostRewardToNativePrices: [BigInt!]!
"Latest native token price of this snapshot. Expressed with 18 decimals."
nativeToUSDPrice: BigInt!

"Amount of underlying tokens in the vault"
Expand Down Expand Up @@ -234,6 +245,8 @@ type ClassicBoost @entity {
vault: ClassicVault!
"The transaction that created the strategy"
createdWith: Transaction!
"The boost reward token"
rewardToken: Token!
"Technical field to remember if the strategy was already initialized"
isInitialized: Boolean!
}
Expand Down Expand Up @@ -267,9 +280,11 @@ type ClassicHarvestEvent @entity(immutable: true) {
"Total amount of liquidity in the vault at time of harvest"
vaultSharesTotalSupply: BigInt!

"LP token price in native at the time of the interaction. Expressed with 18 decimals."
"LP token price in native at the time of harvest. Expressed with 18 decimals."
underlyingToNativePrice: BigInt!
"Native token price at the time of the interaction. Expressed with 18 decimals."
"Boost token prices in native at the time of harvest. Ordered by classic.boostRewardTokens. Expressed with 18 decimals."
boostRewardToNativePrices: [BigInt!]!
"Native token price at the time of harvest. Expressed with 18 decimals."
nativeToUSDPrice: BigInt!
}

Expand Down Expand Up @@ -307,6 +322,8 @@ enum ClassicPositionInteractionType {
BOOST_STAKE
"The investor unstaked from the boost of the vault and received vault shares"
BOOST_UNSTAKE
"The investor claimed their rewards from the vault"
BOOST_REWARD_CLAIM
}

type ClassicPositionInteraction @entity(immutable: true) {
Expand Down Expand Up @@ -340,11 +357,15 @@ type ClassicPositionInteraction @entity(immutable: true) {

"Amount of vault shares change in the interaction"
vaultBalanceDelta: BigInt!
"Amount of boost shares change in the interaction"
"Amount of vault shares token change on the boost contract in the interaction"
boostBalanceDelta: BigInt!
"Amount of boost tokens change in the interaction. Ordered by classic.boostRewardTokens."
boostRewardBalancesDelta: [BigInt!]!

"LP token price in native at the time of the interaction. Expressed with 18 decimals."
underlyingToNativePrice: BigInt!
"Boost token prices in native at the time of the interaction. Ordered by classic.boostRewardTokens. Expressed with 18 decimals."
boostRewardToNativePrices: [BigInt!]!
"Native token price at the time of the interaction. Expressed with 18 decimals."
nativeToUSDPrice: BigInt!
}
Expand Down
1 change: 1 addition & 0 deletions src/classic/compound.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ function _handleClassicStrategyHarvest(event: ethereum.Event, compoundedAmount:
harvest.compoundedAmount = compoundedAmount
harvest.vaultSharesTotalSupply = classicData.vaultSharesTotalSupply
harvest.underlyingToNativePrice = classicData.underlyingToNativePrice
harvest.boostRewardToNativePrices = classicData.boostRewardToNativePrices
harvest.nativeToUSDPrice = classicData.nativeToUSDPrice
harvest.save()
}
5 changes: 5 additions & 0 deletions src/classic/entity/classic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ export function getClassic(vaultAddress: Bytes): Classic {

classic.vaultSharesToken = vaultAddress
classic.underlyingToken = ADDRESS_ZERO
classic.boostRewardTokens = []

classic.vaultSharesTotalSupply = ZERO_BI

classic.underlyingToNativePrice = ZERO_BI
classic.boostRewardToNativePrices = []
classic.nativeToUSDPrice = ZERO_BI

classic.underlyingAmount = ZERO_BI
Expand Down Expand Up @@ -65,6 +67,7 @@ export function getClassicBoost(boostAddress: Bytes): ClassicBoost {
boost.classic = ADDRESS_ZERO
boost.vault = ADDRESS_ZERO
boost.createdWith = ADDRESS_ZERO
boost.rewardToken = ADDRESS_ZERO
boost.isInitialized = false
}
return boost
Expand All @@ -85,6 +88,7 @@ export function getClassicSnapshot(classic: Classic, timestamp: BigInt, period:
snapshot.vaultSharesTotalSupply = ZERO_BI

snapshot.underlyingToNativePrice = ZERO_BI
snapshot.boostRewardToNativePrices = []
snapshot.nativeToUSDPrice = ZERO_BI

snapshot.underlyingAmount = ZERO_BI
Expand All @@ -95,6 +99,7 @@ export function getClassicSnapshot(classic: Classic, timestamp: BigInt, period:
if (previousSnapshot) {
snapshot.vaultSharesTotalSupply = previousSnapshot.vaultSharesTotalSupply
snapshot.underlyingToNativePrice = previousSnapshot.underlyingToNativePrice
snapshot.boostRewardToNativePrices = previousSnapshot.boostRewardToNativePrices
snapshot.nativeToUSDPrice = previousSnapshot.nativeToUSDPrice
snapshot.underlyingAmount = previousSnapshot.underlyingAmount
}
Expand Down
40 changes: 36 additions & 4 deletions src/classic/interaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Transfer as ClassicVaultTransfer } from "../../generated/templates/Clas
import {
Staked as ClassicBoostStaked,
Withdrawn as ClassicBoostWithdrawn,
RewardPaid as ClassicBoostRewardPaid,
} from "../../generated/templates/ClassicBoost/ClassicBoost"
import { getTransaction } from "../common/entity/transaction"
import { getInvestor } from "../common/entity/investor"
Expand All @@ -29,11 +30,11 @@ export function handleClassicVaultTransfer(event: ClassicVaultTransfer): void {

// don't store transfers to/from the share token mint address
if (!event.params.from.equals(SHARE_TOKEN_MINT_ADDRESS) && !event.params.from.equals(BURN_ADDRESS)) {
updateUserPosition(classic, event, event.params.from, event.params.value.neg(), ZERO_BI)
updateUserPosition(classic, event, event.params.from, event.params.value.neg(), ZERO_BI, [])
}

if (!event.params.to.equals(SHARE_TOKEN_MINT_ADDRESS) && !event.params.to.equals(BURN_ADDRESS)) {
updateUserPosition(classic, event, event.params.to, event.params.value, ZERO_BI)
updateUserPosition(classic, event, event.params.to, event.params.value, ZERO_BI, [])
}
}

Expand All @@ -45,7 +46,7 @@ export function handleClassicBoostStaked(event: ClassicBoostStaked): void {
const investorAddress = event.params.user
const amount = event.params.amount

updateUserPosition(classic, event, investorAddress, ZERO_BI, amount)
updateUserPosition(classic, event, investorAddress, ZERO_BI, amount, [])
}

export function handleClassicBoostWithdrawn(event: ClassicBoostWithdrawn): void {
Expand All @@ -56,7 +57,28 @@ export function handleClassicBoostWithdrawn(event: ClassicBoostWithdrawn): void
const investorAddress = event.params.user
const amount = event.params.amount

updateUserPosition(classic, event, investorAddress, ZERO_BI, amount.neg())
updateUserPosition(classic, event, investorAddress, ZERO_BI, amount.neg(), [])
}

export function handleClassicBoostRewardPaid(event: ClassicBoostRewardPaid): void {
const boostAddress = event.address
const boost = getClassicBoost(boostAddress)
const classic = getClassic(boost.classic)

const investorAddress = event.params.user
const amount = event.params.reward

let boostRewardBalancesDelta = new Array<BigInt>()
for (let i = 0; i < classic.boostRewardTokens.length; i++) {
const rewardTokenAddress = classic.boostRewardTokens[i]
if (rewardTokenAddress.equals(boost.rewardToken)) {
boostRewardBalancesDelta.push(amount)
} else {
boostRewardBalancesDelta.push(ZERO_BI)
}
}

updateUserPosition(classic, event, investorAddress, ZERO_BI, ZERO_BI, boostRewardBalancesDelta)
}

function updateUserPosition(
Expand All @@ -65,6 +87,7 @@ function updateUserPosition(
investorAddress: Address,
vaultBalanceDelta: BigInt,
boostBalanceDelta: BigInt,
boostRewardBalancesDelta: Array<BigInt>,
): void {
if (!isClassicInitialized(classic)) {
return
Expand Down Expand Up @@ -99,13 +122,16 @@ function updateUserPosition(
// interaction
const isSharesTransfer = !vaultBalanceDelta.equals(ZERO_BI)
const isBoostTransfer = !boostBalanceDelta.equals(ZERO_BI)
const isRewardTransfer = boostRewardBalancesDelta.some((delta) => !delta.equals(ZERO_BI))

// if both shares and reward pool are transferred, we need to create two interactions
let interactionId = investor.id.concat(getEventIdentifier(event))
if (isSharesTransfer) {
interactionId = interactionId.concat(Bytes.fromI32(0))
} else if (isBoostTransfer) {
interactionId = interactionId.concat(Bytes.fromI32(1))
} else if (isRewardTransfer) {
interactionId = interactionId.concat(Bytes.fromI32(2))
}
const interaction = new ClassicPositionInteraction(interactionId)
interaction.classic = classic.id
Expand All @@ -118,13 +144,19 @@ function updateUserPosition(
interaction.type = vaultBalanceDelta.gt(ZERO_BI) ? "VAULT_DEPOSIT" : "VAULT_WITHDRAW"
} else if (isBoostTransfer) {
interaction.type = boostBalanceDelta.gt(ZERO_BI) ? "BOOST_STAKE" : "BOOST_UNSTAKE"
} else if (isRewardTransfer) {
interaction.type = "BOOST_REWARD_CLAIM"
}

interaction.vaultBalance = position.vaultBalance
interaction.boostBalance = position.boostBalance
interaction.totalBalance = position.totalBalance
interaction.vaultBalanceDelta = vaultBalanceDelta
interaction.boostBalanceDelta = boostBalanceDelta
interaction.boostRewardBalancesDelta = boostRewardBalancesDelta

interaction.underlyingToNativePrice = classicData.underlyingToNativePrice
interaction.boostRewardToNativePrices = classicData.boostRewardToNativePrices
interaction.nativeToUSDPrice = classicData.nativeToUSDPrice
interaction.save()
}
30 changes: 29 additions & 1 deletion src/classic/lifecycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import {
UpgradeStrat as ClassicVaultUpgradeStrategy,
} from "../../generated/ClassicVaultFactory/ClassicVault"
import { BoostDeployed as ClassicBoostDeployed } from "../../generated/ClassicBoostFactory/ClassicBoostFactory"
import { Initialized as ClassicBoostInitialized } from "../../generated/ClassicBoostFactory/ClassicBoost"
import {
Initialized as ClassicBoostInitialized,
ClassicBoost as ClassicBoostContract,
} from "../../generated/ClassicBoostFactory/ClassicBoost"
import {
ClassicStrategy as ClassicStrategyContract,
Initialized as ClassicStrategyInitialized,
Expand Down Expand Up @@ -211,7 +214,32 @@ export function handleClassicBoostInitialized(event: ClassicBoostInitialized): v
const boostAddress = event.address
log.debug("Boost initialized: {}", [boostAddress.toHexString()])

const boostContract = ClassicBoostContract.bind(boostAddress)
const rewardTokenAddressRes = boostContract.try_rewardToken()
if (rewardTokenAddressRes.reverted) {
log.error("Failed to fetch reward token address for Classic Boost: {}", [boostAddress.toHexString()])
return
}
const rewardTokenAddress = rewardTokenAddressRes.value
const rewardToken = fetchAndSaveTokenData(rewardTokenAddress)

const boost = getClassicBoost(boostAddress)
boost.isInitialized = true
boost.rewardToken = rewardToken.id
boost.save()

const clm = getClassic(boost.classic)
const currentRewardTokens = clm.boostRewardTokens
let foundToken = false
for (let i = 0; i < currentRewardTokens.length; i++) {
if (currentRewardTokens[i].equals(rewardTokenAddress)) {
foundToken = true
break
}
}

if (!foundToken) {
clm.boostRewardTokens.push(rewardTokenAddress)
clm.save()
}
}
2 changes: 1 addition & 1 deletion src/classic/mapping/boost.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { handleClassicBoostInitialized } from "../lifecycle"
export { handleClassicBoostStaked } from "../interaction"
export { handleClassicBoostWithdrawn } from "../interaction"
//export {handleClassicBoostRewardPaid} from "../interaction"
export { handleClassicBoostRewardPaid } from "../interaction"
Loading

0 comments on commit 5a7c397

Please sign in to comment.