Skip to content

Commit

Permalink
Add additional EMA windows for vAPY (#803)
Browse files Browse the repository at this point in the history
  • Loading branch information
BeanstalkFarmsOperations authored Apr 5, 2024
2 parents 3b50efc + 45fdea0 commit 7af05fa
Show file tree
Hide file tree
Showing 39 changed files with 223,675 additions and 39,690 deletions.
6 changes: 3 additions & 3 deletions projects/subgraph-beanstalk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@
"deploy-hosted-test": "graph deploy --node http://graph.node.bean.money:8020/ --ipfs http://graph.node.bean.money:5001 beanstalk-testing"
},
"dependencies": {
"@graphprotocol/graph-cli": "0.56.0",
"@graphprotocol/graph-ts": "0.31.0",
"matchstick-as": "^0.5.0"
"@graphprotocol/graph-cli": "0.69.0",
"@graphprotocol/graph-ts": "0.34.0",
"matchstick-as": "^0.6.0"
},
"private": true
}
14 changes: 12 additions & 2 deletions projects/subgraph-beanstalk/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ enum PlotSource {
TRANSFER
}

enum EmaWindow {
ROLLING_24_HOUR
ROLLING_7_DAY
ROLLING_30_DAY
}

type Beanstalk @entity {
" Smart contract address of the protocol's main contract (Factory, Registry, etc) "
id: ID!
Expand Down Expand Up @@ -294,10 +300,12 @@ type SiloAssetDailySnapshot @entity {
}

type SiloYield @entity {
"Season of data points"
"Season of data points - EMA window"
id: ID!
"Sortable int field for season"
season: Int!
"Window used for vAPY calc"
window: EmaWindow!
"Beta used for EMA"
beta: BigDecimal!
"u used for EMA"
Expand All @@ -313,7 +321,7 @@ type SiloYield @entity {
}

type TokenYield @entity {
"Token address - season"
"Token address - season - EMA window"
id: Bytes!
"Token being calculated"
token: Bytes!
Expand Down Expand Up @@ -1110,6 +1118,8 @@ type FertilizerYield @entity {
id: ID!
"Current season"
season: Int!
"Bean EMA Window"
window: EmaWindow!
"Current humidity"
humidity: BigDecimal!
"Current outstanding fert"
Expand Down
15 changes: 0 additions & 15 deletions projects/subgraph-beanstalk/src/DiamondHandler.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,9 @@
import { Address, BigDecimal, BigInt } from "@graphprotocol/graph-ts";
import { ZERO_BI } from "../../subgraph-core/utils/Decimals";
import { DiamondCut } from "../generated/Diamond/Beanstalk";
import { loadBeanstalk } from "./utils/Beanstalk";
import { TOKEN_YIELD_14_000 } from "./utils/HistoricYield";
import { loadTokenYield } from "./utils/SiloEntities";

export function handleDiamondCut(event: DiamondCut): void {
let beanstalk = loadBeanstalk(event.address);

// Load the historical vAPY figures in bulk at start
if (beanstalk.lastUpgrade == ZERO_BI) {
for (let i = 0; i < TOKEN_YIELD_14_000.length; i++) {
let tokenYield = loadTokenYield(Address.fromString(TOKEN_YIELD_14_000[i][0]), <i32>parseInt(TOKEN_YIELD_14_000[i][1]));
tokenYield.beanAPY = BigDecimal.fromString(TOKEN_YIELD_14_000[i][2]);
tokenYield.stalkAPY = BigDecimal.fromString(TOKEN_YIELD_14_000[i][3]);
tokenYield.createdAt = BigInt.fromString(TOKEN_YIELD_14_000[i][4]);
tokenYield.save();
}
}

beanstalk.lastUpgrade = event.block.timestamp;
beanstalk.save();
}
56 changes: 33 additions & 23 deletions projects/subgraph-beanstalk/src/YieldHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,42 @@ import { toDecimal, ZERO_BD } from "../../subgraph-core/utils/Decimals";
import { loadFertilizer } from "./utils/Fertilizer";
import { loadFertilizerYield } from "./utils/FertilizerYield";
import { loadSilo, loadSiloHourlySnapshot, loadSiloYield, loadTokenYield, loadWhitelistTokenSetting } from "./utils/SiloEntities";
import { SILO_YIELD_14_000 } from "./utils/HistoricYield";

const MAX_WINDOW = 720;
const ROLLING_24_WINDOW = 24;
const ROLLING_7_DAY_WINDOW = 168;
const ROLLING_30_DAY_WINDOW = 720;

// Note: minimum value of `t` is 6075
export function updateBeanEMA(t: i32, timestamp: BigInt): void {
let silo = loadSilo(BEANSTALK);
let siloYield = loadSiloYield(t);

// Check for cached info
if (t <= 14_000) {
let cacheIndex = t - 6075;
siloYield.beta = BigDecimal.fromString(SILO_YIELD_14_000[cacheIndex][1]);
siloYield.u = <i32>parseInt(SILO_YIELD_14_000[cacheIndex][2]);
siloYield.beansPerSeasonEMA = BigDecimal.fromString(SILO_YIELD_14_000[cacheIndex][3]);
updateWindowEMA(t, timestamp, ROLLING_24_WINDOW);
updateWindowEMA(t, timestamp, ROLLING_7_DAY_WINDOW);
updateWindowEMA(t, timestamp, ROLLING_30_DAY_WINDOW);
}

/**
*
*
*/
function updateWindowEMA(t: i32, timestamp: BigInt, window: i32): void {
// Historic cache values up to season 20,000
if (t <= 20_000) {
let silo = loadSilo(BEANSTALK);
let siloYield = loadSiloYield(t, window);

siloYield.whitelistedTokens = silo.whitelistedTokens;
siloYield.createdAt = BigInt.fromString(SILO_YIELD_14_000[cacheIndex][4]);
siloYield.save();

updateFertAPY(t, timestamp);
updateFertAPY(t, timestamp, window);

return;
}

// When less then MAX_WINDOW data points are available,
// smooth over whatever is available. Otherwise use MAX_WINDOW.
siloYield.u = t - 6074 < MAX_WINDOW ? t - 6074 : MAX_WINDOW;
let silo = loadSilo(BEANSTALK);
let siloYield = loadSiloYield(t, window);

// When less then window data points are available,
// smooth over whatever is available. Otherwise use the full window.
siloYield.u = t - 6074 < window ? t - 6074 : window;
siloYield.whitelistedTokens = silo.whitelistedTokens;

// Calculate the current beta value
Expand All @@ -41,7 +50,7 @@ export function updateBeanEMA(t: i32, timestamp: BigInt): void {
let currentEMA = ZERO_BD;
let priorEMA = ZERO_BD;

if (siloYield.u < MAX_WINDOW) {
if (siloYield.u < window) {
// Recalculate EMA from initial season since beta has changed
for (let i = 6075; i <= t; i++) {
let season = loadSiloHourlySnapshot(BEANSTALK, i, timestamp);
Expand All @@ -50,7 +59,7 @@ export function updateBeanEMA(t: i32, timestamp: BigInt): void {
}
} else {
// Calculate EMA for the prior 720 seasons
for (let i = t - MAX_WINDOW + 1; i <= t; i++) {
for (let i = t - window + 1; i <= t; i++) {
let season = loadSiloHourlySnapshot(BEANSTALK, i, timestamp);
currentEMA = toDecimal(season.deltaBeanMints).minus(priorEMA).times(siloYield.beta).plus(priorEMA);
priorEMA = currentEMA;
Expand All @@ -68,7 +77,7 @@ export function updateBeanEMA(t: i32, timestamp: BigInt): void {
for (let i = 0; i < siloYield.whitelistedTokens.length; i++) {
let token = Address.fromString(siloYield.whitelistedTokens[i]);
let siloSettings = loadWhitelistTokenSetting(token);
let tokenYield = loadTokenYield(token, t);
let tokenYield = loadTokenYield(token, t, window);

let tokenAPY = calculateAPY(
currentEMA,
Expand All @@ -83,7 +92,7 @@ export function updateBeanEMA(t: i32, timestamp: BigInt): void {
tokenYield.save();
}

updateFertAPY(t, timestamp);
updateFertAPY(t, timestamp, window);
}

/**
Expand Down Expand Up @@ -156,9 +165,10 @@ export function calculateAPY(

return apys;
}
function updateFertAPY(t: i32, timestamp: BigInt): void {
let siloYield = loadSiloYield(t);
let fertilizerYield = loadFertilizerYield(t);

function updateFertAPY(t: i32, timestamp: BigInt, window: i32): void {
let siloYield = loadSiloYield(t, window);
let fertilizerYield = loadFertilizerYield(t, window);
let fertilizer = loadFertilizer(FERTILIZER);
let beanstalk = Beanstalk.bind(BEANSTALK);
let currentFertHumidity = beanstalk.try_getCurrentHumidity();
Expand Down
11 changes: 10 additions & 1 deletion projects/subgraph-beanstalk/src/utils/FertilizerYield.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { FertilizerYield } from "../../generated/schema";
import { ZERO_BD, ZERO_BI } from "../../../subgraph-core/utils/Decimals";

export function loadFertilizerYield(season: i32): FertilizerYield {
export function loadFertilizerYield(season: i32, window: i32): FertilizerYield {
let fertilizerYield = FertilizerYield.load(season.toString());
if (fertilizerYield == null) {
fertilizerYield = new FertilizerYield(season.toString());
Expand All @@ -12,6 +12,15 @@ export function loadFertilizerYield(season: i32): FertilizerYield {
fertilizerYield.deltaBpf = ZERO_BD;
fertilizerYield.simpleAPY = ZERO_BD;
fertilizerYield.createdAt = ZERO_BI;

if (window == 24) {
fertilizerYield.window = "ROLLING_24_HOUR";
} else if (window == 168) {
fertilizerYield.window = "ROLLING_7_DAY";
} else if (window == 720) {
fertilizerYield.window = "ROLLING_30_DAY";
}

fertilizerYield.save();
}
return fertilizerYield as FertilizerYield;
Expand Down
Loading

0 comments on commit 7af05fa

Please sign in to comment.