Skip to content

Commit

Permalink
Subgraph updates for BIP-48 wstETH Migration (#964)
Browse files Browse the repository at this point in the history
  • Loading branch information
soilking authored Jul 26, 2024
2 parents 9614470 + 83df316 commit be43b5c
Show file tree
Hide file tree
Showing 26 changed files with 405 additions and 225 deletions.
13 changes: 12 additions & 1 deletion projects/subgraph-basin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,15 @@

### Subgraphs

All subgraphs are currently in development in the anticipation of the Wells release.
All currently used subgraphs live on a centralized host controlled by beanstalk farms.

- [Testing Subgraph](https://graph.node.bean.money/subgraphs/name/basin-testing)
- Used during local development for debugging and rapid iteration.
- [Dev Subgraph](https://graph.node.bean.money/subgraphs/name/basin-dev)
- Used for testing fixes or improvements made in the testing subgraph.
- [Canonical Subgraph](https://graph.node.bean.money/subgraphs/name/basin)
- Stable deployment and current source of truth for UI and other production processes.

### Testing

To test with Docker, the first time you will need to run `yarn run graph test -d`. This will build the `matchstick` Docker image. Then, you can use the `yarn testd` script to run all tests. Alternatively, use `yarn testd-named <TestName1> ...` to run specific tests. I have found running in Docker to be preferred since otherwise there can be issues with console output and some test cases fail silently.
17 changes: 17 additions & 0 deletions projects/subgraph-basin/src/utils/BeanstalkPrice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Unfortunately this file must be copied across the various subgraph projects. This is due to the codegen
import { Address, BigInt } from "@graphprotocol/graph-ts";
import { BeanstalkPrice } from "../../generated/templates/Well/BeanstalkPrice";
import { BEANSTALK_PRICE_1, BEANSTALK_PRICE_2, PRICE_2_BLOCK } from "../../../subgraph-core/utils/Constants";

// Gets the BeanstalkPrice contract, bound to the appropriate instance of the contract.
// Note: Will bind to PRICE_1 even if that contract has not been deployed yet
// Thus the caller still needs to check for reverts.
export function getBeanstalkPrice(blockNumber: BigInt): BeanstalkPrice {
let contractAddress: Address;
if (blockNumber < PRICE_2_BLOCK) {
contractAddress = BEANSTALK_PRICE_1;
} else {
contractAddress = BEANSTALK_PRICE_2;
}
return BeanstalkPrice.bind(contractAddress);
}
8 changes: 4 additions & 4 deletions projects/subgraph-basin/src/utils/Token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { Address, BigDecimal, BigInt, log } from "@graphprotocol/graph-ts";
import { ERC20 } from "../../generated/Aquifer/ERC20";
import { Token } from "../../generated/schema";
import { CurvePrice } from "../../generated/templates/Well/CurvePrice";
import { BEANSTALK_PRICE, BEAN_ERC20, BEAN_WETH_CP2_WELL, CURVE_PRICE } from "../../../subgraph-core/utils/Constants";
import { BEAN_ERC20, CURVE_PRICE } from "../../../subgraph-core/utils/Constants";
import { toDecimal, ZERO_BD, ZERO_BI } from "../../../subgraph-core/utils/Decimals";
import { BeanstalkPrice } from "../../generated/templates/Well/BeanstalkPrice";
import { getBeanstalkPrice } from "./BeanstalkPrice";

export function loadOrCreateToken(tokenAddress: Address): Token {
let token = Token.load(tokenAddress);
Expand Down Expand Up @@ -59,8 +59,8 @@ export function updateTokenUSD(tokenAddress: Address, blockNumber: BigInt, beanP
if (tokenAddress == BEAN_ERC20) {
// Attempt to use Beanstalk price contract first

let beanstalkPrice = BeanstalkPrice.bind(BEANSTALK_PRICE);
let price = beanstalkPrice.try_getConstantProductWell(BEAN_WETH_CP2_WELL);
let beanstalkPrice = getBeanstalkPrice(blockNumber);
let price = beanstalkPrice.try_price();
if (!price.reverted) {
token.lastPriceUSD = toDecimal(price.value.price);
} else {
Expand Down
48 changes: 27 additions & 21 deletions projects/subgraph-basin/tests/helpers/Functions.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { BigDecimal, BigInt, ethereum } from "@graphprotocol/graph-ts";
import { createMockedFunction } from "matchstick-as/assembly/index";
import { BEAN_3CRV, BEAN_ERC20, BEAN_WETH_CP2_WELL, CRV3_TOKEN, WETH } from "../../../subgraph-core/utils/Constants";
import { BEAN_3CRV, BEAN_ERC20, BEAN_WETH_CP2_WELL, BEANSTALK_PRICE_1, CRV3_TOKEN, WETH } from "../../../subgraph-core/utils/Constants";
import { BEAN_USD_PRICE, WELL } from "./Constants";
import { setMockCurvePrice, setMockWellPrice } from "../../../subgraph-core/tests/event-mocking/Price";
import { setMockBeanPrice } from "../../../subgraph-core/tests/event-mocking/Price";
import { ONE_BD, ZERO_BD } from "../../../subgraph-core/utils/Decimals";

let prevMocked = ZERO_BD;
Expand All @@ -15,26 +15,32 @@ export function createContractCallMocks(priceMultiple: BigDecimal = ONE_BD): voi

const price = BigInt.fromString(new BigDecimal(BEAN_USD_PRICE).times(priceMultiple).truncate(0).toString());

setMockCurvePrice({
contract: BEAN_3CRV,
tokens: [BEAN_ERC20, CRV3_TOKEN],
balances: [BigInt.fromString("14306013160240"), BigInt.fromString("12306817594155799426763734")],
setMockBeanPrice({
price: price,
liquidity: BigInt.fromString("26025239751318"),
deltaB: BigInt.fromString("-866349934591"),
lpUsd: BigInt.fromString("969328"),
lpBdv: BigInt.fromString("1032515")
});

setMockWellPrice({
contract: BEAN_WETH_CP2_WELL,
tokens: [BEAN_ERC20, WETH],
balances: [BigInt.fromString("2000000000"), BigInt.fromString("1500000000000000000")],
price: price,
liquidity: BigInt.fromString("26025239751318"),
deltaB: BigInt.fromString("-866349934591"),
lpUsd: BigInt.fromString("969328"),
lpBdv: BigInt.fromString("1032515")
liquidity: BigInt.fromString("26025239751318").times(BigInt.fromU32(2)),
deltaB: BigInt.fromString("-866349934591").times(BigInt.fromU32(2)),
ps: [
{
contract: BEAN_3CRV,
tokens: [BEAN_ERC20, CRV3_TOKEN],
balances: [BigInt.fromString("14306013160240"), BigInt.fromString("12306817594155799426763734")],
price: price,
liquidity: BigInt.fromString("26025239751318"),
deltaB: BigInt.fromString("-866349934591"),
lpUsd: BigInt.fromString("969328"),
lpBdv: BigInt.fromString("1032515")
},
{
contract: BEAN_WETH_CP2_WELL,
tokens: [BEAN_ERC20, WETH],
balances: [BigInt.fromString("2000000000"), BigInt.fromString("1500000000000000000")],
price: price,
liquidity: BigInt.fromString("26025239751318"),
deltaB: BigInt.fromString("-866349934591"),
lpUsd: BigInt.fromString("969328"),
lpBdv: BigInt.fromString("1032515")
}
]
});

createMockedFunction(BEAN_ERC20, "name", "name():(string)")
Expand Down
25 changes: 22 additions & 3 deletions projects/subgraph-bean/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,25 @@
<img src="https://github.com/BeanstalkFarms/Beanstalk-Brand-Assets/blob/main/BEAN/bean-128x128.png" alt="Beanstalk logo" align="right" width="120" />

# Bean Subgraph
## Beanstalk Subgraph

The Bean subgraph can be found here:
https://thegraph.com/explorer/subgraph?id=0x925753106fcdb6d2f30c3db295328a0a1c5fd1d1-1
[![Discord][discord-badge]][discord-url]

[discord-badge]: https://img.shields.io/discord/880413392916054098?label=Beanstalk
[discord-url]: https://discord.gg/beanstalk

**Indexes events emitted by [Beanstalk](https://etherscan.io/address/0xc1e088fc1323b20bcbee9bd1b9fc9546db5624c5) and its trading pools.**

### Subgraphs

All currently used subgraphs live on a centralized host controlled by beanstalk farms.

- [Testing Subgraph](https://graph.node.bean.money/subgraphs/name/bean-testing)
- Used during local development for debugging and rapid iteration.
- [Dev Subgraph](https://graph.node.bean.money/subgraphs/name/bean-dev)
- Used for testing fixes or improvements made in the testing subgraph.
- [Canonical Subgraph](https://graph.node.bean.money/subgraphs/name/bean)
- Stable deployment and current source of truth for UI and other production processes.

### Testing

To test with Docker, the first time you will need to run `yarn run graph test -d`. This will build the `matchstick` Docker image. Then, you can use the `yarn testd` script to run all tests. Alternatively, use `yarn testd-named <TestName1> ...` to run specific tests. I have found running in Docker to be preferred since otherwise there can be issues with console output and some test cases fail silently.
10 changes: 8 additions & 2 deletions projects/subgraph-bean/src/BeanWellHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,10 @@ function handleLiquidityChange(
if (beanPrice.reverted) {
return;
}
let wellPrice = getPoolPrice(beanPrice, Address.fromString(poolAddress))!;
let wellPrice = getPoolPrice(beanPrice, Address.fromString(poolAddress));
if (wellPrice == null) {
return;
}

let startingLiquidity = getPoolLiquidityUSD(poolAddress, blockNumber);

Expand Down Expand Up @@ -142,7 +145,10 @@ function handleSwapEvent(
if (beanPrice.reverted) {
return;
}
let wellPrice = getPoolPrice(beanPrice, Address.fromString(poolAddress))!;
let wellPrice = getPoolPrice(beanPrice, Address.fromString(poolAddress));
if (wellPrice == null) {
return;
}

let startingLiquidity = getPoolLiquidityUSD(poolAddress, blockNumber);

Expand Down
11 changes: 1 addition & 10 deletions projects/subgraph-bean/src/BeanstalkHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,7 @@ import { Address, BigInt } from "@graphprotocol/graph-ts";
import { Chop, DewhitelistToken, Reward, Sunrise } from "../generated/Beanstalk/Beanstalk";
import { getBeanTokenAddress, loadBean, updateBeanSeason, updateBeanSupplyPegPercent, updateBeanTwa, updateBeanValues } from "./utils/Bean";
import { loadOrCreatePool, updatePoolPrice, updatePoolSeason, updatePoolValues } from "./utils/Pool";
import { BeanstalkPrice } from "../generated/Beanstalk/BeanstalkPrice";
import {
BEANSTALK_PRICE,
BEAN_3CRV,
BEAN_ERC20,
BEAN_ERC20_V1,
BEAN_WETH_CP2_WELL,
BEAN_WETH_V1,
CURVE_PRICE
} from "../../subgraph-core/utils/Constants";
import { BEAN_3CRV, BEAN_ERC20, BEAN_ERC20_V1, BEAN_WETH_V1, CURVE_PRICE } from "../../subgraph-core/utils/Constants";
import { ZERO_BD, ZERO_BI, toDecimal } from "../../subgraph-core/utils/Decimals";
import { CurvePrice } from "../generated/Beanstalk/CurvePrice";
import { checkBeanCross } from "./utils/Cross";
Expand Down
4 changes: 1 addition & 3 deletions projects/subgraph-bean/src/BlockHandler.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ethereum } from "@graphprotocol/graph-ts";
import { BEAN_ERC20, BEAN_WETH_CP2_WELL_BLOCK, BEANSTALK_PRICE, EXPLOIT_BLOCK } from "../../subgraph-core/utils/Constants";
import { BEAN_ERC20, BEAN_WETH_CP2_WELL_BLOCK, EXPLOIT_BLOCK } from "../../subgraph-core/utils/Constants";
import { checkPegCrossEth as univ2_checkPegCrossEth } from "./UniswapV2Handler";
import { loadBean, updateBeanValues } from "./utils/Bean";
import { toDecimal, ZERO_BD, ZERO_BI } from "../../subgraph-core/utils/Decimals";
Expand Down Expand Up @@ -38,8 +38,6 @@ export function beanstalkPrice_updatePoolPrices(priceOnlyOnCross: boolean, block
const prevPrice = bean.price;
const newPrice = toDecimal(priceResult.value.price);

// log.debug("Prev/New bean price {} / {}", [prevPrice.toString(), newPrice.toString()]);

// Check for overall peg cross
const beanCrossed = checkBeanCross(BEAN_ERC20.toHexString(), block.timestamp, block.number, prevPrice, newPrice);

Expand Down
12 changes: 11 additions & 1 deletion projects/subgraph-bean/src/constants/PooledTokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import {
BEAN_3CRV_V1,
BEAN_LUSD_V1,
BEAN_3CRV,
BEAN_WETH_CP2_WELL
BEAN_WETH_CP2_WELL,
BEAN_WSTETH_CP2_WELL,
WSTETH
} from "../../../subgraph-core/utils/Constants";

// Use this mapping to determine which tokens are in each pool. Pools may each follow a distinct interface,
Expand Down Expand Up @@ -58,6 +60,10 @@ const poolTokens: PoolTokens[] = [
{
pool: BEAN_WETH_CP2_WELL.toHexString(),
tokens: [BEAN_ERC20.toHexString(), WETH.toHexString()]
},
{
pool: BEAN_WSTETH_CP2_WELL.toHexString(),
tokens: [BEAN_ERC20.toHexString(), WSTETH.toHexString()]
}
];

Expand Down Expand Up @@ -92,5 +98,9 @@ const tokens: Token[] = [
{
address: LUSD.toHexString(),
info: { name: "LUSD", decimals: BigInt.fromU32(18) }
},
{
address: WSTETH.toHexString(),
info: { name: "wstETH", decimals: BigInt.fromU32(18) }
}
];
13 changes: 2 additions & 11 deletions projects/subgraph-bean/src/utils/Bean.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
import { BigDecimal, BigInt, log } from "@graphprotocol/graph-ts";
import { BigDecimal, BigInt } from "@graphprotocol/graph-ts";
import { Bean, BeanDailySnapshot, BeanHourlySnapshot, Pool } from "../../generated/schema";
import {
BEAN_3CRV,
BEAN_ERC20_V1,
BEAN_ERC20,
BEAN_WETH_V1,
BEAN_WETH_CP2_WELL,
BEAN_3CRV_V1,
BEAN_LUSD_V1,
BEANSTALK
} from "../../../subgraph-core/utils/Constants";
import { BEAN_ERC20_V1, BEAN_ERC20, BEAN_WETH_V1, BEAN_3CRV_V1, BEAN_LUSD_V1, BEANSTALK } from "../../../subgraph-core/utils/Constants";
import { dayFromTimestamp, hourFromTimestamp } from "../../../subgraph-core/utils/Dates";
import { ONE_BD, toDecimal, ZERO_BD, ZERO_BI } from "../../../subgraph-core/utils/Decimals";
import { checkBeanCross, getV1Crosses } from "./Cross";
Expand Down
12 changes: 11 additions & 1 deletion projects/subgraph-bean/src/utils/BeanWells.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { BigInt, Address } from "@graphprotocol/graph-ts";
import { BEAN_WETH_CP2_WELL, BEAN_WETH_CP2_WELL_BLOCK } from "../../../subgraph-core/utils/Constants";
import {
BEAN_WETH_CP2_WELL,
BEAN_WETH_CP2_WELL_BLOCK,
BEAN_WSTETH_CP2_WELL,
BEAN_WSTETH_CP2_WELL_BLOCK
} from "../../../subgraph-core/utils/Constants";

export enum WellFunction {
ConstantProduct
Expand All @@ -16,5 +21,10 @@ export const BEAN_WELLS: BeanWell[] = [
address: BEAN_WETH_CP2_WELL,
startBlock: BEAN_WETH_CP2_WELL_BLOCK,
wellFunction: WellFunction.ConstantProduct
},
{
address: BEAN_WSTETH_CP2_WELL,
startBlock: BEAN_WSTETH_CP2_WELL_BLOCK,
wellFunction: WellFunction.ConstantProduct
}
];
6 changes: 5 additions & 1 deletion projects/subgraph-bean/src/utils/LockedBeans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import {
BEAN_3CRV,
BEAN_WETH_CP2_WELL,
BEAN_WETH_UNRIPE_MIGRATION_BLOCK,
BEAN_WSTETH_CP2_WELL,
BEAN_WSTETH_UNRIPE_MIGRATION_BLOCK,
BEANSTALK,
GAUGE_BIP45_BLOCK,
UNRIPE_BEAN,
Expand Down Expand Up @@ -58,8 +60,10 @@ export function calcLockedBeans(blockNumber: BigInt): BigInt {
function getUnderlyingUnripe(blockNumber: BigInt): Address {
if (blockNumber < BEAN_WETH_UNRIPE_MIGRATION_BLOCK) {
return BEAN_3CRV;
} else {
} else if (blockNumber < BEAN_WSTETH_UNRIPE_MIGRATION_BLOCK) {
return BEAN_WETH_CP2_WELL;
} else {
return BEAN_WSTETH_CP2_WELL;
}
}

Expand Down
31 changes: 24 additions & 7 deletions projects/subgraph-bean/src/utils/price/BeanstalkPrice.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Address, BigInt, log } from "@graphprotocol/graph-ts";
// Unfortunately this file must be copied across the various subgraph projects. This is due to the codegen
import { Address, BigInt } from "@graphprotocol/graph-ts";
import {
BeanstalkPrice,
BeanstalkPrice__priceResultPPsStruct,
BeanstalkPrice__priceResultPStruct
} from "../../../generated/Beanstalk/BeanstalkPrice";
import { loadBean } from "../Bean";
import { BEANSTALK_PRICE } from "../../../../subgraph-core/utils/Constants";
import { BEANSTALK_PRICE_1, BEANSTALK_PRICE_2, PRICE_1_BLOCK, PRICE_2_BLOCK } from "../../../../subgraph-core/utils/Constants";
import { ZERO_BI } from "../../../../subgraph-core/utils/Decimals";

// Can't use the autogenerated one because the fields need to be updateable
Expand Down Expand Up @@ -55,18 +56,19 @@ export class BeanstalkPriceResult {
constructor(value: BeanstalkPrice__priceResultPStruct | null, whitelistedPools: string[]) {
if (value !== null) {
this._value = new PriceOverallStruct(value);
let anyDewhitelisted = false;
let poolsCount = this._value!.ps.length;
let dewhitelistCount = 0;
for (let i = 0; i < this._value!.ps.length; ++i) {
const index = whitelistedPools.indexOf(this._value!.ps[i].pool.toHexString());
if (index == -1) {
// The pool was dewhitelisted
this._dewhitelistedPools.push(this._value!.ps.splice(i--, 1)[0]);
anyDewhitelisted = true;
++dewhitelistCount;
}
}

// Recalculate overall price/liquidity/delta
if (anyDewhitelisted) {
// Recalculate overall price/liquidity/delta if some but not all pools got dewhitelisted
if (dewhitelistCount > 0 && dewhitelistCount < poolsCount) {
this._value!.price = ZERO_BI;
this._value!.liquidity = ZERO_BI;
this._value!.deltaB = ZERO_BI;
Expand All @@ -76,6 +78,8 @@ export class BeanstalkPriceResult {
this._value!.deltaB = this._value!.deltaB.plus(this._value!.ps[i].deltaB);
}
this._value!.price = this._value!.price.div(this._value!.liquidity);
} else if (dewhitelistCount == poolsCount) {
this._value!.ps = this._dewhitelistedPools;
}
}
}
Expand All @@ -100,7 +104,7 @@ export class BeanstalkPriceResult {
// (1) Only including whitelisted tokens in the final price calculation and the prices list
// (2) Which contract to call (in anticipation of new BeanstalkPrice contract deployments)
export function BeanstalkPrice_try_price(beanAddr: Address, blockNumber: BigInt): BeanstalkPriceResult {
let beanstalkPrice = BeanstalkPrice.bind(BEANSTALK_PRICE);
let beanstalkPrice = getBeanstalkPrice(blockNumber);
let beanPrice = beanstalkPrice.try_price();

if (beanPrice.reverted) {
Expand Down Expand Up @@ -129,3 +133,16 @@ export function getPoolPrice(priceResult: BeanstalkPriceResult, pool: Address):
}
return null;
}

// Gets the BeanstalkPrice contract, bound to the appropriate instance of the contract.
// Note: Will bind to PRICE_1 even if that contract has not been deployed yet
// Thus the caller still needs to check for reverts.
export function getBeanstalkPrice(blockNumber: BigInt): BeanstalkPrice {
let contractAddress: Address;
if (blockNumber < PRICE_2_BLOCK) {
contractAddress = BEANSTALK_PRICE_1;
} else {
contractAddress = BEANSTALK_PRICE_2;
}
return BeanstalkPrice.bind(contractAddress);
}
Loading

0 comments on commit be43b5c

Please sign in to comment.