Skip to content

Commit

Permalink
curve twa
Browse files Browse the repository at this point in the history
  • Loading branch information
soilking committed Apr 17, 2024
1 parent f2994d4 commit 2e7884d
Show file tree
Hide file tree
Showing 10 changed files with 217 additions and 99 deletions.
31 changes: 28 additions & 3 deletions projects/subgraph-bean/src/Bean3CRVHandler_V1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,22 @@ import {
TokenExchangeUnderlying
} from "../generated/Bean3CRV/Bean3CRV";
import { loadBean, updateBeanSupplyPegPercent, updateBeanValues } from "./utils/Bean";
import { BEAN_ERC20_V1 } from "../../subgraph-core/utils/Constants";
import { BEAN_3CRV_V1, BEAN_ERC20_V1, CRV3_POOL_V1, LUSD_3POOL } from "../../subgraph-core/utils/Constants";
import { toDecimal, ZERO_BD, ZERO_BI } from "../../subgraph-core/utils/Decimals";
import { loadOrCreatePool, setPoolReserves, updatePoolPrice, updatePoolValues } from "./utils/Pool";
import {
loadOrCreatePool,
loadOrCreatePoolDailySnapshot,
loadOrCreatePoolHourlySnapshot,
setPoolReserves,
updatePoolPrice,
updatePoolValues
} from "./utils/Pool";
import { Bean3CRV } from "../generated/Bean3CRV-V1/Bean3CRV";
import { ERC20 } from "../generated/Bean3CRV-V1/ERC20";
import { checkBeanCross } from "./utils/Cross";
import { curveDeltaB, curvePriceAndLp } from "./utils/price/CurvePrice";
import { curveDeltaB, curvePriceAndLp, curveTwaDeltaBAndPrice } from "./utils/price/CurvePrice";
import { getTWAPrices } from "./utils/price/TwaOracle";
import { TWAType } from "./utils/price/Types";

export function handleTokenExchange(event: TokenExchange): void {
// Do not index post-exploit data
Expand Down Expand Up @@ -196,3 +205,19 @@ function handleSwap(
updatePoolPrice(poolAddress, timestamp, blockNumber, newPrice);
checkBeanCross(BEAN_ERC20_V1.toHexString(), timestamp, blockNumber, oldBeanPrice, newPrice);
}

export function onSunriseSetCurveTwa(poolAddress: string, timestamp: BigInt, blockNumber: BigInt): void {
const twaBalances = getTWAPrices(poolAddress, TWAType.CURVE, timestamp);
const beanPool = Address.fromString(poolAddress);
const otherPool = beanPool == BEAN_3CRV_V1 ? CRV3_POOL_V1 : LUSD_3POOL;
const twaResult = curveTwaDeltaBAndPrice(twaBalances, beanPool, otherPool);

let poolHourly = loadOrCreatePoolHourlySnapshot(poolAddress, timestamp, blockNumber);
let poolDaily = loadOrCreatePoolDailySnapshot(poolAddress, timestamp, blockNumber);
poolHourly.twaDeltaBeans = twaResult.deltaB;
poolHourly.twaPrice = twaResult.price;
poolDaily.twaDeltaBeans = twaResult.deltaB;
poolDaily.twaPrice = twaResult.price;
poolHourly.save();
poolDaily.save();
}
8 changes: 4 additions & 4 deletions projects/subgraph-bean/src/BeanstalkHandler.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Address, BigInt } from "@graphprotocol/graph-ts";
import { Sunrise } from "../generated/Beanstalk/Beanstalk";
import { getBeanTokenAddress, loadBean, updateBeanSeason, updateBeanTwaDeltaB, updateBeanValues } from "./utils/Bean";
import { getBeanTokenAddress, loadBean, updateBeanSeason, updateBeanTwa, updateBeanValues } from "./utils/Bean";
import { loadOrCreatePool, setPoolTwaDeltaB, updatePoolPrice, updatePoolSeason, updatePoolValues } from "./utils/Pool";
import { BeanstalkPrice } from "../generated/Beanstalk/BeanstalkPrice";
import {
Expand Down Expand Up @@ -153,18 +153,18 @@ export function handleSunrise(event: Sunrise): void {
totalLiquidity.minus(bean.liquidityUSD)
);
checkBeanCross(BEAN_ERC20_V1.toHexString(), event.block.timestamp, event.block.number, bean.price, totalPrice);
updateBeanTwaDeltaB(event.block.timestamp, event.block.number);
updateBeanTwa(event.block.timestamp, event.block.number);
}
}

// POST REPLANT TWA DELTAB //

export function handleMetapoolOracle(event: MetapoolOracle): void {
setPoolTwaDeltaB(BEAN_3CRV.toHexString(), event.params.deltaB, event.block.timestamp, event.block.number);
updateBeanTwaDeltaB(event.block.timestamp, event.block.number);
updateBeanTwa(event.block.timestamp, event.block.number);
}

export function handleWellOracle(event: WellOracle): void {
setPoolTwaDeltaB(event.params.well.toHexString(), event.params.deltaB, event.block.timestamp, event.block.number);
updateBeanTwaDeltaB(event.block.timestamp, event.block.number);
updateBeanTwa(event.block.timestamp, event.block.number);
}
46 changes: 18 additions & 28 deletions projects/subgraph-bean/src/UniswapV2Handler.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { BigDecimal, BigInt } from "@graphprotocol/graph-ts";
import { BigDecimal, BigInt, log } from "@graphprotocol/graph-ts";
import { Swap, Sync } from "../generated/BeanUniswapV2Pair/UniswapV2Pair";
import { loadBean, updateBeanSupplyPegPercent, updateBeanValues } from "./utils/Bean";
import { BEAN_ERC20_V1, BEANSTALK, WETH, WETH_USDC_PAIR } from "../../subgraph-core/utils/Constants";
import { BI_10, toDecimal, ZERO_BD, ZERO_BI } from "../../subgraph-core/utils/Decimals";
import { BEAN_ERC20_V1, WETH } from "../../subgraph-core/utils/Constants";
import { toDecimal, ZERO_BD, ZERO_BI } from "../../subgraph-core/utils/Decimals";
import {
loadOrCreatePool,
loadOrCreatePoolDailySnapshot,
Expand All @@ -13,9 +13,15 @@ import {
} from "./utils/Pool";
import { loadOrCreateToken } from "./utils/Token";
import { checkBeanCross } from "./utils/Cross";
import { uniswapV2DeltaB, uniswapV2Price, uniswapV2Reserves, updatePreReplantPriceETH } from "./utils/price/UniswapPrice";
import { PreReplant } from "../generated/Beanstalk/PreReplant";
import { getTWAPrices, TWAType } from "./utils/price/TwaOracle";
import {
uniswapTwaDeltaBAndPrice,
uniswapV2DeltaB,
uniswapV2Price,
uniswapV2Reserves,
updatePreReplantPriceETH
} from "./utils/price/UniswapPrice";
import { getTWAPrices } from "./utils/price/TwaOracle";
import { TWAType } from "./utils/price/Types";

// export function handleMint(event: Mint): void {
// updatePoolReserves(event.address.toHexString(), event.params.amount0, event.params.amount1, event.block.number);
Expand Down Expand Up @@ -110,31 +116,15 @@ export function handleSync(event: Sync): void {
}

export function onSunriseSetUniswapV2Twa(poolAddress: string, timestamp: BigInt, blockNumber: BigInt): void {
const prices = getTWAPrices(poolAddress, TWAType.UNISWAP, timestamp);

let beanstalk = PreReplant.bind(BEANSTALK);
let reserves: BigInt[];
// After BIP-9, reserves calculation changes
if (blockNumber.lt(BigInt.fromU64(13953949))) {
const result = beanstalk.reserves();
reserves = [result.value0, result.value1];
} else {
const result = beanstalk.lockedReserves();
reserves = [result.value0, result.value1];
}

const mulReserves = reserves[0].times(reserves[1]).times(BI_10.pow(6));
const currentBeans = mulReserves.div(prices[0]).sqrt();
const targetBeans = mulReserves.div(prices[1]).sqrt();
const deltaB = targetBeans.minus(currentBeans);
const twaPrice = BigDecimal.fromString(prices[0].toString()).div(BigDecimal.fromString(prices[1].toString()));
const twaPrices = getTWAPrices(poolAddress, TWAType.UNISWAP, timestamp);
const twaResult = uniswapTwaDeltaBAndPrice(twaPrices, blockNumber);

let poolHourly = loadOrCreatePoolHourlySnapshot(poolAddress, timestamp, blockNumber);
let poolDaily = loadOrCreatePoolDailySnapshot(poolAddress, timestamp, blockNumber);
poolHourly.twaDeltaBeans = deltaB;
poolHourly.twaPrice = twaPrice;
poolDaily.twaDeltaBeans = deltaB;
poolDaily.twaPrice = twaPrice;
poolHourly.twaDeltaBeans = twaResult.deltaB;
poolHourly.twaPrice = twaResult.price;
poolDaily.twaDeltaBeans = twaResult.deltaB;
poolDaily.twaPrice = twaResult.price;
poolHourly.save();
poolDaily.save();
}
24 changes: 17 additions & 7 deletions projects/subgraph-bean/src/utils/Bean.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BigDecimal, BigInt } from "@graphprotocol/graph-ts";
import { BigDecimal, BigInt, log } from "@graphprotocol/graph-ts";
import { Bean, BeanDailySnapshot, BeanHourlySnapshot, Pool } from "../../generated/schema";
import {
BEAN_3CRV,
Expand All @@ -10,7 +10,7 @@ import {
BEAN_LUSD_V1
} from "../../../subgraph-core/utils/Constants";
import { dayFromTimestamp, hourFromTimestamp } from "../../../subgraph-core/utils/Dates";
import { toDecimal, ZERO_BD, ZERO_BI } from "../../../subgraph-core/utils/Decimals";
import { ONE_BD, toDecimal, ZERO_BD, ZERO_BI } from "../../../subgraph-core/utils/Decimals";
import { getV1Crosses } from "./Cross";
import { loadOrCreatePool, loadOrCreatePoolHourlySnapshot } from "./Pool";

Expand Down Expand Up @@ -51,6 +51,7 @@ export function loadOrCreateBeanHourlySnapshot(token: string, timestamp: BigInt,
snapshot.volumeUSD = bean.volumeUSD;
snapshot.liquidityUSD = bean.liquidityUSD;
snapshot.price = bean.price;
snapshot.twaPrice = ZERO_BD;
snapshot.crosses = bean.crosses;
snapshot.deltaVolume = ZERO_BI;
snapshot.deltaVolumeUSD = ZERO_BD;
Expand Down Expand Up @@ -80,6 +81,7 @@ export function loadOrCreateBeanDailySnapshot(token: string, timestamp: BigInt):
snapshot.volumeUSD = bean.volumeUSD;
snapshot.liquidityUSD = bean.liquidityUSD;
snapshot.price = bean.price;
snapshot.twaPrice = ZERO_BD;
snapshot.crosses = bean.crosses;
snapshot.deltaVolume = ZERO_BI;
snapshot.deltaVolumeUSD = ZERO_BD;
Expand Down Expand Up @@ -216,20 +218,28 @@ export function updateInstDeltaB(token: string, blockNumber: BigInt, timestamp:
beanDaily.save();
}

export function updateBeanTwaDeltaB(timestamp: BigInt, blockNumber: BigInt): void {
// Update Bean's TWA deltaB and price. Individual pools' values must be computed prior to calling this method.
export function updateBeanTwa(timestamp: BigInt, blockNumber: BigInt): void {
let beanAddress = getBeanTokenAddress(blockNumber);
let bean = loadBean(beanAddress);
let beanHourly = loadOrCreateBeanHourlySnapshot(beanAddress, timestamp, bean.lastSeason);
let beanDaily = loadOrCreateBeanDailySnapshot(beanAddress, timestamp);

let cumulativeDeltaB = ZERO_BI;
let twaDeltaB = ZERO_BI;
let weightedTwaPrice = ZERO_BD;
for (let i = 0; i < bean.pools.length; i++) {
let poolHourly = loadOrCreatePoolHourlySnapshot(bean.pools[i], timestamp, blockNumber);
cumulativeDeltaB = cumulativeDeltaB.plus(poolHourly.twaDeltaBeans);
twaDeltaB = twaDeltaB.plus(poolHourly.twaDeltaBeans);
weightedTwaPrice = weightedTwaPrice.plus(poolHourly.twaPrice.times(poolHourly.liquidityUSD));
}

beanHourly.twaDeltaB = cumulativeDeltaB;
beanDaily.twaDeltaB = cumulativeDeltaB;
// Assumption is that total bean liquidity was already summed earlier in the same event's processing
const twaPrice = weightedTwaPrice.div(bean.liquidityUSD != ZERO_BD ? bean.liquidityUSD : ONE_BD);

beanHourly.twaDeltaB = twaDeltaB;
beanHourly.twaPrice = twaPrice;
beanDaily.twaDeltaB = twaDeltaB;
beanDaily.twaPrice = twaPrice;
beanHourly.save();
beanDaily.save();
}
2 changes: 2 additions & 0 deletions projects/subgraph-bean/src/utils/Pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export function loadOrCreatePoolHourlySnapshot(pool: string, timestamp: BigInt,
snapshot.pool = pool;
snapshot.reserves = currentPool.reserves;
snapshot.lastPrice = currentPool.lastPrice;
snapshot.twaPrice = ZERO_BD;
snapshot.volume = currentPool.volume;
snapshot.volumeUSD = currentPool.volumeUSD;
snapshot.liquidityUSD = currentPool.liquidityUSD;
Expand Down Expand Up @@ -74,6 +75,7 @@ export function loadOrCreatePoolDailySnapshot(pool: string, timestamp: BigInt, b
snapshot.pool = pool;
snapshot.reserves = currentPool.reserves;
snapshot.lastPrice = currentPool.lastPrice;
snapshot.twaPrice = ZERO_BD;
snapshot.volume = currentPool.volume;
snapshot.volumeUSD = currentPool.volumeUSD;
snapshot.liquidityUSD = currentPool.liquidityUSD;
Expand Down
105 changes: 70 additions & 35 deletions projects/subgraph-bean/src/utils/price/CurvePrice.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Address, BigDecimal, BigInt } from "@graphprotocol/graph-ts";
import { Bean3CRV } from "../../../generated/Bean3CRV-V1/Bean3CRV";
import { BI_10, toDecimal, ZERO_BD, ZERO_BI } from "../../../../subgraph-core/utils/Decimals";
import { BI_10, ONE_BI, toDecimal, ZERO_BD, ZERO_BI } from "../../../../subgraph-core/utils/Decimals";
import { BEAN_3CRV_V1, BEAN_LUSD_V1, CALCULATIONS_CURVE, CRV3_POOL_V1, LUSD, LUSD_3POOL } from "../../../../subgraph-core/utils/Constants";
import { CalculationsCurve } from "../../../generated/Bean3CRV-V1/CalculationsCurve";
import { ERC20 } from "../../../generated/Bean3CRV-V1/ERC20";
import { DeltaBAndPrice } from "./Types";

// Note that the Bean3CRV type applies to any curve pool (including lusd)

Expand Down Expand Up @@ -41,6 +42,7 @@ export function curvePriceAndLp(pool: Address): BigDecimal[] {
return [beanPrice, lpValue];
}

// TODO: this logic can be refactored to remove the contract calls and instead use getD method.
// Returns the deltaB in the given curve pool
export function curveDeltaB(pool: Address, beanReserves: BigInt): BigInt {
let lpContract = Bean3CRV.bind(pool);
Expand All @@ -52,40 +54,73 @@ export function curveDeltaB(pool: Address, beanReserves: BigInt): BigInt {
return deltaB;
}

// // Based on get_D from dune query here https://dune.com/queries/3561823/5993924
// export function get_D(xp: Array<f64>, amp: f64, n_coins: i32, a_precision: i32): f64 {
// let s: f64 = 0;
// let d_prev: f64 = 0;
// let d: f64;
// let ann: f64;
// let d_p: f64;

// for (let i = 0; i < xp.length; i++) {
// s += xp[i];
// }

// if (s == 0) {
// return 0;
// }
// d = s;
// ann = amp * n_coins;

// let _i: i32 = 0;
// while (_i < 255) {
// d_p = d;
// for (let j = 0; j < xp.length; j++) {
// d_p = d_p * d / (xp[j] * n_coins);
// }
// d_prev = d;
// d = (ann * s / a_precision + d_p * n_coins) * d / ((ann - a_precision) * d / a_precision + (n_coins + 1) * d_p);

// if ((d > d_prev && d - d_prev <= 1) || (d_prev > d && d_prev - d <= 1)) {
// break;
// }
// _i++;
// }
// return d;
// }
export function curveCumulativePrices(pool: Address, timestamp: BigInt): BigInt[] {
let curve = Bean3CRV.bind(pool);
const cumulativeLast = curve.get_price_cumulative_last();
const currentBalances = curve.get_balances();
const lastTimestamp = curve.block_timestamp_last();

const deltaLastTimestamp = timestamp.minus(lastTimestamp);
const cumulativeBalances = [
cumulativeLast[0].plus(currentBalances[0].times(deltaLastTimestamp)),
cumulativeLast[1].plus(currentBalances[1].times(deltaLastTimestamp))
];
return cumulativeBalances;
}

// beanPool is the pool with beans trading against otherPool's tokens.
// otherPool is needed to get the virtual price of that token beans are trading against.
export function curveTwaDeltaBAndPrice(twaBalances: BigInt[], beanPool: Address, otherPool: Address): DeltaBAndPrice {
let beanCurve = Bean3CRV.bind(beanPool);
const bean_A = beanCurve.A_precise();
let otherCurve = Bean3CRV.bind(otherPool);
const other_A = otherCurve.A_precise();

const xp = [twaBalances[0].times(BI_10.pow(12)), twaBalances[1].times(other_A).div(BI_10.pow(18))];

const D = getD(xp, bean_A);

return {
deltaB: D.div(BigInt.fromU32(2)).div(BI_10.pow(12)).minus(twaBalances[0]),
price: ZERO_BD // TODO
};
}

// Generated from functions in LibCurve.sol
function getD(xp: BigInt[], a: BigInt): BigInt {
const A_PRECISION = BigInt.fromU32(100);
const N_COINS = BigInt.fromU32(2);

let S: BigInt = BigInt.fromString("0");
let Dprev: BigInt;
let D: BigInt = BigInt.fromString("0");

// Summing elements in the array
for (let i = 0; i < xp.length; ++i) {
S = S.plus(xp[i]);
}
if (S.toString() == "0") return BigInt.fromString("0");

D = S;
let Ann: BigInt = a.times(N_COINS);

for (let i = 0; i < 256; ++i) {
let D_P: BigInt = D;
for (let j = 0; j < xp.length; ++j) {
D_P = D_P.times(D).div(xp[j].times(N_COINS));
}
Dprev = D;
let num: BigInt = Ann.times(S).div(A_PRECISION).plus(D_P.times(N_COINS)).times(D);
let denom: BigInt = Ann.minus(A_PRECISION).times(D).div(A_PRECISION).plus(N_COINS.plus(ONE_BI).times(D_P));
D = num.div(denom);

// Check convergence
if (D == Dprev || D.minus(Dprev) == ONE_BI || Dprev.minus(D) == ONE_BI) {
return D;
}
}
throw new Error("Price: Convergence false");
}

// // Based on get_y from dune query here https://dune.com/queries/3561823/5993924
// export function get_y(i: i32, j: i32, x: f64, xp: Array<f64>, amp: f64, n_coins: i32, a_precision: i32): f64 {
Expand Down
Loading

0 comments on commit 2e7884d

Please sign in to comment.