diff --git a/.github/workflows/deploy.subgraph.yaml b/.github/workflows/deploy.subgraph.yaml index 6583a64381..95c88ee5ed 100644 --- a/.github/workflows/deploy.subgraph.yaml +++ b/.github/workflows/deploy.subgraph.yaml @@ -28,7 +28,7 @@ jobs: exit 1 fi # Check if the subgraph is a valid selection - if [[ "${{ github.event.inputs.subgraph }}" != "beanstalk" && "${{ github.event.inputs.subgraph }}" != "bean" && "${{ github.event.inputs.subgraph }}" != "basin" && "${{ github.event.inputs.subgraph }}" != "beanft" ]]; then + if [[ ! "${{ github.event.inputs.subgraph }}" =~ ^(beanstalk|bean|basin|beanft) ]]; then echo "Error: Subgraph must be one of 'beanstalk', 'bean', 'basin', 'beanft'." exit 1 fi diff --git a/projects/subgraph-basin/manifests/codegen-abis.yaml b/projects/subgraph-basin/manifests/codegen-abis.yaml index 491eab9455..fa6c273743 100644 --- a/projects/subgraph-basin/manifests/codegen-abis.yaml +++ b/projects/subgraph-basin/manifests/codegen-abis.yaml @@ -3,7 +3,7 @@ # the only important part is in the `abis` and `templates` sections. # - For abis, its only the list of abis that is relevant. The name of the dataSource is also visible. # - For templates, it is only the name of the template that is relevant. -specVersion: 0.0.4 +specVersion: 0.0.9 schema: file: ../schema.graphql dataSources: @@ -33,10 +33,11 @@ dataSources: file: ../../subgraph-core/abis/CurvePrice.json - name: BeanstalkPrice file: ../../subgraph-core/abis/BeanstalkPrice.json - eventHandlers: - - event: BoreWell(address,address,address[],(address,bytes),(address,bytes)[],bytes) - handler: handleBoreWell - file: ../src/templates/AquiferHandler.ts + blockHandlers: + - handler: handleInitVersion + filter: + kind: once + file: ../src/utils/Init.ts templates: - kind: ethereum/contract name: Well @@ -52,7 +53,8 @@ templates: abis: - name: Well file: ../../subgraph-core/abis/Well.json - eventHandlers: - - event: Sync(uint256[],uint256,address) - handler: handleSync - file: ../src/WellHandler.ts + blockHandlers: + - handler: handleInitVersion + filter: + kind: once + file: ../src/utils/Init.ts diff --git a/projects/subgraph-basin/manifests/ethereum.yaml b/projects/subgraph-basin/manifests/ethereum.yaml index 332c1b9393..64b5112c0c 100644 --- a/projects/subgraph-basin/manifests/ethereum.yaml +++ b/projects/subgraph-basin/manifests/ethereum.yaml @@ -1,7 +1,32 @@ -specVersion: 0.0.4 +specVersion: 0.0.9 schema: file: ../schema.graphql dataSources: + ### + # INITIALIZATION + ### + - kind: ethereum/contract + name: Version + network: mainnet + source: + address: "0xBA51AAAA95aeEFc1292515b36D86C51dC7877773" + abi: Aquifer + startBlock: 17977922 + endBlock: 17977922 + mapping: + kind: ethereum/events + apiVersion: 0.0.7 + language: wasm/assemblyscript + entities: + - Version + abis: + - name: Aquifer + file: ../../subgraph-core/abis/Aquifer.json + blockHandlers: + - handler: handleInitVersion + filter: + kind: once + file: ../src/utils/Init.ts - kind: ethereum/contract name: Aquifer network: mainnet diff --git a/projects/subgraph-basin/schema.graphql b/projects/subgraph-basin/schema.graphql index 80d545cd04..688f00497d 100644 --- a/projects/subgraph-basin/schema.graphql +++ b/projects/subgraph-basin/schema.graphql @@ -17,6 +17,18 @@ enum SwapEvent { SHIFT } +# This same entity schema is intended for use across the subgraphs +type Version @entity { + "= 'subgraph'" + id: ID! + "= 'beanstalk'" + subgraphName: String! + "Verison number of the subgraph" + versionNumber: String! + "Which blockchain is being indexed, i.e. 'ethereum', 'arbitrum', etc." + chain: String! +} + type Token @entity { " Smart contract address of the token " id: Bytes! diff --git a/projects/subgraph-basin/src/utils/Init.ts b/projects/subgraph-basin/src/utils/Init.ts new file mode 100644 index 0000000000..630642264e --- /dev/null +++ b/projects/subgraph-basin/src/utils/Init.ts @@ -0,0 +1,24 @@ +import { BigInt, ethereum } from "@graphprotocol/graph-ts"; +import { Version } from "../../generated/schema"; + +export function handleInitVersion(block: ethereum.Block): void { + const versionEntity = new Version("subgraph"); + versionEntity.versionNumber = "2.2.2"; + versionEntity.subgraphName = subgraphNameForBlockNumber(block.number); + versionEntity.chain = chainForBlockNumber(block.number); + versionEntity.save(); +} + +function subgraphNameForBlockNumber(blockNumber: BigInt): string { + if (blockNumber == BigInt.fromU32(17977922)) { + return "basin"; + } + throw new Error("Unable to initialize subgraph name for this block number"); +} + +function chainForBlockNumber(blockNumber: BigInt): string { + if (blockNumber == BigInt.fromU32(17977922)) { + return "ethereum"; + } + throw new Error("Unable to initialize chain for this block number"); +} diff --git a/projects/subgraph-bean/manifests/codegen-abis.yaml b/projects/subgraph-bean/manifests/codegen-abis.yaml index 5a690c0445..1ab42fdf24 100644 --- a/projects/subgraph-bean/manifests/codegen-abis.yaml +++ b/projects/subgraph-bean/manifests/codegen-abis.yaml @@ -3,7 +3,7 @@ # the only important part is in the `abis` and `templates` sections. # - For abis, its only the list of abis that is relevant. The name of the dataSource is also visible. # - For templates, it is only the name of the template that is relevant. -specVersion: 0.0.4 +specVersion: 0.0.9 schema: file: ../schema.graphql dataSources: @@ -43,7 +43,8 @@ dataSources: file: ../../subgraph-core/abis/CurvePrice.json - name: CalculationsCurve file: ../../subgraph-core/abis/CalculationsCurve.json - eventHandlers: - - event: Transfer(indexed address,indexed address,uint256) - handler: handleTransfer - file: ../src/BeanHandler.ts + blockHandlers: + - handler: handleInitVersion + filter: + kind: once + file: ../src/utils/Init.ts diff --git a/projects/subgraph-bean/manifests/ethereum.yaml b/projects/subgraph-bean/manifests/ethereum.yaml index 156bc5b6d2..f42e9afb87 100644 --- a/projects/subgraph-bean/manifests/ethereum.yaml +++ b/projects/subgraph-bean/manifests/ethereum.yaml @@ -1,7 +1,32 @@ -specVersion: 0.0.4 +specVersion: 0.0.9 schema: file: ../schema.graphql dataSources: + ### + # INITIALIZATION + ### + - kind: ethereum/contract + name: Version + network: mainnet + source: + address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" + abi: Beanstalk + startBlock: 12974075 + endBlock: 12974075 + mapping: + kind: ethereum/events + apiVersion: 0.0.7 + language: wasm/assemblyscript + entities: + - Version + abis: + - name: Beanstalk + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Replanted.json + blockHandlers: + - handler: handleInitVersion + filter: + kind: once + file: ../src/utils/Init.ts - kind: ethereum/contract name: BeanV1 network: mainnet diff --git a/projects/subgraph-bean/schema.graphql b/projects/subgraph-bean/schema.graphql index f740f8b026..9392462efa 100644 --- a/projects/subgraph-bean/schema.graphql +++ b/projects/subgraph-bean/schema.graphql @@ -1,3 +1,15 @@ +# This same entity schema is intended for use across the subgraphs +type Version @entity { + "= 'subgraph'" + id: ID! + "= 'beanstalk'" + subgraphName: String! + "Verison number of the subgraph" + versionNumber: String! + "Which blockchain is being indexed, i.e. 'ethereum', 'arbitrum', etc." + chain: String! +} + type Token @entity { "Smart contract address of the token" id: ID! diff --git a/projects/subgraph-bean/src/utils/Bean.ts b/projects/subgraph-bean/src/utils/Bean.ts index 33c74a2477..17b5220e53 100644 --- a/projects/subgraph-bean/src/utils/Bean.ts +++ b/projects/subgraph-bean/src/utils/Bean.ts @@ -1,6 +1,14 @@ import { BigDecimal, BigInt } from "@graphprotocol/graph-ts"; import { Bean, BeanDailySnapshot, BeanHourlySnapshot, Pool } from "../../generated/schema"; -import { BEAN_ERC20_V1, BEAN_ERC20, BEAN_WETH_V1, 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, + NEW_BEAN_TOKEN_BLOCK +} 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"; @@ -178,11 +186,11 @@ export function calcLiquidityWeightedBeanPrice(token: string): BigDecimal { } export function getBeanTokenAddress(blockNumber: BigInt): string { - return blockNumber < BigInt.fromString("15278082") ? BEAN_ERC20_V1.toHexString() : BEAN_ERC20.toHexString(); + return blockNumber < NEW_BEAN_TOKEN_BLOCK ? BEAN_ERC20_V1.toHexString() : BEAN_ERC20.toHexString(); } export function updateBeanSupplyPegPercent(blockNumber: BigInt): void { - if (blockNumber < BigInt.fromString("15278082")) { + if (blockNumber < NEW_BEAN_TOKEN_BLOCK) { let bean = loadBean(BEAN_ERC20_V1.toHexString()); let lpSupply = ZERO_BD; diff --git a/projects/subgraph-bean/src/utils/Init.ts b/projects/subgraph-bean/src/utils/Init.ts new file mode 100644 index 0000000000..3ca88c6450 --- /dev/null +++ b/projects/subgraph-bean/src/utils/Init.ts @@ -0,0 +1,24 @@ +import { BigInt, ethereum } from "@graphprotocol/graph-ts"; +import { Version } from "../../generated/schema"; + +export function handleInitVersion(block: ethereum.Block): void { + const versionEntity = new Version("subgraph"); + versionEntity.versionNumber = "2.3.1"; + versionEntity.subgraphName = subgraphNameForBlockNumber(block.number); + versionEntity.chain = chainForBlockNumber(block.number); + versionEntity.save(); +} + +function subgraphNameForBlockNumber(blockNumber: BigInt): string { + if (blockNumber == BigInt.fromU32(12974075)) { + return "bean"; + } + throw new Error("Unable to initialize subgraph name for this block number"); +} + +function chainForBlockNumber(blockNumber: BigInt): string { + if (blockNumber == BigInt.fromU32(12974075)) { + return "ethereum"; + } + throw new Error("Unable to initialize chain for this block number"); +} diff --git a/projects/subgraph-bean/src/utils/LockedBeans.ts b/projects/subgraph-bean/src/utils/LockedBeans.ts index 34de6ed31a..bd006d47fa 100644 --- a/projects/subgraph-bean/src/utils/LockedBeans.ts +++ b/projects/subgraph-bean/src/utils/LockedBeans.ts @@ -8,7 +8,7 @@ import { BEANSTALK, GAUGE_BIP45_BLOCK, UNRIPE_BEAN, - UNRIPE_BEAN_3CRV + UNRIPE_LP } from "../../../subgraph-core/utils/Constants"; import { SeedGauge } from "../../generated/Bean-ABIs/SeedGauge"; import { ONE_BI, ZERO_BD, ZERO_BI } from "../../../subgraph-core/utils/Decimals"; @@ -46,7 +46,7 @@ export function calcLockedBeans(blockNumber: BigInt): BigInt { const recapPaidPercent = new BigDecimal(recapPercentResult.value).div(BigDecimal.fromString("1000000")); const lockedBeansUrBean = LibLockedUnderlying_getLockedUnderlying(UNRIPE_BEAN, recapPaidPercent); - const lockedUnripeLp = LibLockedUnderlying_getLockedUnderlying(UNRIPE_BEAN_3CRV, recapPaidPercent); + const lockedUnripeLp = LibLockedUnderlying_getLockedUnderlying(UNRIPE_LP, recapPaidPercent); const underlyingLpPool = getUnderlyingUnripe(blockNumber); const poolBeanReserves = loadOrCreatePool(underlyingLpPool.toHexString(), blockNumber).reserves[0]; diff --git a/projects/subgraph-bean/tests/l2sr.test.ts b/projects/subgraph-bean/tests/l2sr.test.ts index 6ff0134fe0..a31842cc21 100644 --- a/projects/subgraph-bean/tests/l2sr.test.ts +++ b/projects/subgraph-bean/tests/l2sr.test.ts @@ -8,7 +8,7 @@ import { BEAN_WETH_UNRIPE_MIGRATION_BLOCK, GAUGE_BIP45_BLOCK, UNRIPE_BEAN, - UNRIPE_BEAN_3CRV + UNRIPE_LP } from "../../subgraph-core/utils/Constants"; import { BI_10, ONE_BI, ZERO_BI } from "../../subgraph-core/utils/Decimals"; import { @@ -46,10 +46,10 @@ describe("L2SR", () => { test("Calculation - block 19736119", () => { mockSeedGaugeLockedBeansReverts(mockReserves, mockReservesTime); mockERC20TokenSupply(UNRIPE_BEAN, BigInt.fromString("109291429462926")); - mockERC20TokenSupply(UNRIPE_BEAN_3CRV, BigInt.fromString("88784724593495")); + mockERC20TokenSupply(UNRIPE_LP, BigInt.fromString("88784724593495")); const recapPaidPercent = BigDecimal.fromString("0.045288"); const lockedUnderlyingBean = LibLockedUnderlying_getPercentLockedUnderlying(UNRIPE_BEAN, recapPaidPercent); - const lockedUnderlyingLp = LibLockedUnderlying_getPercentLockedUnderlying(UNRIPE_BEAN_3CRV, recapPaidPercent); + const lockedUnderlyingLp = LibLockedUnderlying_getPercentLockedUnderlying(UNRIPE_LP, recapPaidPercent); assert.assertTrue(lockedUnderlyingBean.equals(BigDecimal.fromString("0.6620572696973799"))); assert.assertTrue(lockedUnderlyingLp.equals(BigDecimal.fromString("0.6620572696973799"))); @@ -60,7 +60,7 @@ describe("L2SR", () => { mockGetRecapPaidPercent(BigDecimal.fromString("0.045288")); mockGetTotalUnderlying(UNRIPE_BEAN, BigInt.fromString("24584183207621")); - mockGetTotalUnderlying(UNRIPE_BEAN_3CRV, BigInt.fromString("246676046856767267392929")); + mockGetTotalUnderlying(UNRIPE_LP, BigInt.fromString("246676046856767267392929")); mockERC20TokenSupply(BEAN_WETH_CP2_WELL, BigInt.fromString("256164804872196346760208")); const lockedBeans = calcLockedBeans(BEAN_WETH_UNRIPE_MIGRATION_BLOCK); diff --git a/projects/subgraph-beanft/manifests/codegen-abis.yaml b/projects/subgraph-beanft/manifests/codegen-abis.yaml index 5a09f00b7c..0cbc6ef2a0 100644 --- a/projects/subgraph-beanft/manifests/codegen-abis.yaml +++ b/projects/subgraph-beanft/manifests/codegen-abis.yaml @@ -3,7 +3,7 @@ # the only important part is in the `abis` and `templates` sections. # - For abis, its only the list of abis that is relevant. The name of the dataSource is also visible. # - For templates, it is only the name of the template that is relevant. -specVersion: 0.0.5 +specVersion: 0.0.9 schema: file: ../schema.graphql dataSources: @@ -30,7 +30,8 @@ dataSources: file: ../abis/barnraise.json - name: basin file: ../abis/basin.json - eventHandlers: - - event: Transfer(indexed address,indexed address,indexed uint256) - handler: handleTransferGenesis - file: ../src/mappings.ts + blockHandlers: + - handler: handleInitVersion + filter: + kind: once + file: ../src/utils/Init.ts diff --git a/projects/subgraph-beanft/manifests/ethereum.yaml b/projects/subgraph-beanft/manifests/ethereum.yaml index 54b3b8fa7f..2cc798c09b 100644 --- a/projects/subgraph-beanft/manifests/ethereum.yaml +++ b/projects/subgraph-beanft/manifests/ethereum.yaml @@ -1,7 +1,32 @@ -specVersion: 0.0.5 +specVersion: 0.0.9 schema: file: ../schema.graphql dataSources: + ### + # INITIALIZATION + ### + - kind: ethereum/contract + name: Version + network: mainnet + source: + address: "0xa755A670Aaf1FeCeF2bea56115E65e03F7722A79" + abi: genesis + startBlock: 13323594 + endBlock: 13323594 + mapping: + kind: ethereum/events + apiVersion: 0.0.7 + language: wasm/assemblyscript + entities: + - Version + abis: + - name: genesis + file: ../abis/genesis.json + blockHandlers: + - handler: handleInitVersion + filter: + kind: once + file: ../src/utils/Init.ts - kind: ethereum name: genesis network: mainnet diff --git a/projects/subgraph-beanft/schema.graphql b/projects/subgraph-beanft/schema.graphql index 0a25b889fe..4b842617ce 100644 --- a/projects/subgraph-beanft/schema.graphql +++ b/projects/subgraph-beanft/schema.graphql @@ -1,11 +1,23 @@ +# This same entity schema is intended for use across the subgraphs +type Version @entity { + "= 'subgraph'" + id: ID! + "= 'beanstalk'" + subgraphName: String! + "Verison number of the subgraph" + versionNumber: String! + "Which blockchain is being indexed, i.e. 'ethereum', 'arbitrum', etc." + chain: String! +} + type BeaNFTUser @entity { id: ID! barnRaise: [Int!] genesis: [Int!] winter: [Int!] basin: [Int!] -}, +} type CollectionData @entity { id: ID! minted: [Int!] -} \ No newline at end of file +} diff --git a/projects/subgraph-beanft/src/utils/Init.ts b/projects/subgraph-beanft/src/utils/Init.ts new file mode 100644 index 0000000000..19e683964b --- /dev/null +++ b/projects/subgraph-beanft/src/utils/Init.ts @@ -0,0 +1,24 @@ +import { BigInt, ethereum } from "@graphprotocol/graph-ts"; +import { Version } from "../../generated/schema"; + +export function handleInitVersion(block: ethereum.Block): void { + const versionEntity = new Version("subgraph"); + versionEntity.versionNumber = "1.0.2"; + versionEntity.subgraphName = subgraphNameForBlockNumber(block.number); + versionEntity.chain = chainForBlockNumber(block.number); + versionEntity.save(); +} + +function subgraphNameForBlockNumber(blockNumber: BigInt): string { + if (blockNumber == BigInt.fromU32(13323594)) { + return "beanft"; + } + throw new Error("Unable to initialize subgraph name for this block number"); +} + +function chainForBlockNumber(blockNumber: BigInt): string { + if (blockNumber == BigInt.fromU32(13323594)) { + return "ethereum"; + } + throw new Error("Unable to initialize chain for this block number"); +} diff --git a/projects/subgraph-beanstalk/README.md b/projects/subgraph-beanstalk/README.md index c7bbaa04b6..183cdba415 100644 --- a/projects/subgraph-beanstalk/README.md +++ b/projects/subgraph-beanstalk/README.md @@ -27,3 +27,11 @@ To test with Docker, the first time you will need to run `yarn run graph test -d ### Deploying When using graph cli commands, you will often need to specify which manifest file should be used. This is necessary to support multiple chains in the same codebase. The commands which need it will be evident - as they will fail when unable to find a `subgraph.yaml` file. In those commands, include `./manifest/${chain}.yaml` as the final argument to the command. See scripts inside `package.json` for examples. + +### Development + +# Handler organization strategy + +Any events that are currently relevant to Beanstalk should reference the codegen for whichever is the latest protocol ABI, and not include v1/v2 etc in the name. Legacy events (that are no longer present on-chain) should reference the codegen for the upgrade in which they were initially deployed, and use the appropriate version number in the method names. Legacy handlers should also be placed in the `legacy` folder. + +Underlying logic should be included in a separate file provided in the `utils` folder. The advantage of this is in accommodating a scenario where an event signature gets updated. The replacement event handler can call into the shared util along with the legacy handler. diff --git a/projects/subgraph-beanstalk/manifests/codegen-abis.yaml b/projects/subgraph-beanstalk/manifests/codegen-abis.yaml index d8170253b6..613d8aaefc 100644 --- a/projects/subgraph-beanstalk/manifests/codegen-abis.yaml +++ b/projects/subgraph-beanstalk/manifests/codegen-abis.yaml @@ -3,7 +3,7 @@ # the only important part is in the `abis` and `templates` sections. # - For abis, its only the list of abis that is relevant. The name of the dataSource is also visible. # - For templates, it is only the name of the template that is relevant. -specVersion: 0.0.4 +specVersion: 0.0.9 schema: file: ../schema.graphql dataSources: @@ -34,8 +34,6 @@ dataSources: file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP37.json - name: SeedGauge file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP45.json - - name: UniswapV2Pair - file: ../../subgraph-core/abis/UniswapV2Pair.json - name: ERC20 file: ../../subgraph-core/abis/ERC20.json - name: CurvePrice @@ -46,7 +44,8 @@ dataSources: file: ../../subgraph-core/abis/CurvePrice.json - name: Fertilizer file: ../../subgraph-core/abis/Fertilizer.json - eventHandlers: - - event: AddDeposit(indexed address,indexed address,int96,uint256,uint256) - handler: handleAddDeposit_V3 - file: ../src/SiloHandler.ts + blockHandlers: + - handler: handleInitVersion + filter: + kind: once + file: ../src/utils/Init.ts diff --git a/projects/subgraph-beanstalk/manifests/ethereum.yaml b/projects/subgraph-beanstalk/manifests/ethereum.yaml index c7e93d94d5..16b7b9fae1 100644 --- a/projects/subgraph-beanstalk/manifests/ethereum.yaml +++ b/projects/subgraph-beanstalk/manifests/ethereum.yaml @@ -1,396 +1,558 @@ -specVersion: 0.0.4 +specVersion: 0.0.9 schema: file: ../schema.graphql dataSources: - # Historical Cache loading + ### + # HISTORICAL vAPY CACHE. + # Set to occur one block after the rest of initialization steps, so that the Version entity is loaded. + ### - kind: ethereum/contract name: TokenCacheWindow1_1 network: mainnet source: address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" abi: PreReplant - startBlock: 12974075 + startBlock: 12974076 + endBlock: 12974076 mapping: kind: ethereum/events apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - Diamond + - SiloYield abis: - name: PreReplant file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Pre-Replant.json - eventHandlers: - - event: DiamondCut((address,uint8,bytes4[])[],address,bytes) - handler: handleLoadToken1_1 - file: ../src/yield_cache/window_1/LoadToken_1.ts + blockHandlers: + - handler: handleLoadToken1_1 + filter: + kind: once + file: ../src/utils/yield_cache/window_1/LoadToken_1.ts - kind: ethereum/contract name: TokenCacheWindow1_2 network: mainnet source: address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" abi: PreReplant - startBlock: 12974075 + startBlock: 12974076 + endBlock: 12974076 mapping: kind: ethereum/events apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - Diamond + - SiloYield abis: - name: PreReplant file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Pre-Replant.json - eventHandlers: - - event: DiamondCut((address,uint8,bytes4[])[],address,bytes) - handler: handleLoadToken1_2 - file: ../src/yield_cache/window_1/LoadToken_2.ts + blockHandlers: + - handler: handleLoadToken1_2 + filter: + kind: once + file: ../src/utils/yield_cache/window_1/LoadToken_2.ts - kind: ethereum/contract name: SiloCacheWindow1_1 network: mainnet source: address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" abi: PreReplant - startBlock: 12974075 + startBlock: 12974076 + endBlock: 12974076 mapping: kind: ethereum/events apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - Diamond + - SiloYield abis: - name: PreReplant file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Pre-Replant.json - eventHandlers: - - event: DiamondCut((address,uint8,bytes4[])[],address,bytes) - handler: handleLoadSilo1_1 - file: ../src/yield_cache/window_1/LoadSilo_1.ts + blockHandlers: + - handler: handleLoadSilo1_1 + filter: + kind: once + file: ../src/utils/yield_cache/window_1/LoadSilo_1.ts - kind: ethereum/contract name: SiloCacheWindow1_2 network: mainnet source: address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" abi: PreReplant - startBlock: 12974075 + startBlock: 12974076 + endBlock: 12974076 mapping: kind: ethereum/events apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - Diamond + - SiloYield abis: - name: PreReplant file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Pre-Replant.json - eventHandlers: - - event: DiamondCut((address,uint8,bytes4[])[],address,bytes) - handler: handleLoadSilo1_2 - file: ../src/yield_cache/window_1/LoadSilo_2.ts + blockHandlers: + - handler: handleLoadSilo1_2 + filter: + kind: once + file: ../src/utils/yield_cache/window_1/LoadSilo_2.ts - kind: ethereum/contract name: SiloCacheWindow1_3 network: mainnet source: address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" abi: PreReplant - startBlock: 12974075 + startBlock: 12974076 + endBlock: 12974076 mapping: kind: ethereum/events apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - Diamond + - SiloYield abis: - name: PreReplant file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Pre-Replant.json - eventHandlers: - - event: DiamondCut((address,uint8,bytes4[])[],address,bytes) - handler: handleLoadSilo1_3 - file: ../src/yield_cache/window_1/LoadSilo_3.ts - # Window 2 + blockHandlers: + - handler: handleLoadSilo1_3 + filter: + kind: once + file: ../src/utils/yield_cache/window_1/LoadSilo_3.ts - kind: ethereum/contract name: TokenCacheWindow2_1 network: mainnet source: address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" abi: PreReplant - startBlock: 12974075 + startBlock: 12974076 + endBlock: 12974076 mapping: kind: ethereum/events apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - Diamond + - SiloYield abis: - name: PreReplant file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Pre-Replant.json - eventHandlers: - - event: DiamondCut((address,uint8,bytes4[])[],address,bytes) - handler: handleLoadToken2_1 - file: ../src/yield_cache/window_2/LoadToken_1.ts + blockHandlers: + - handler: handleLoadToken2_1 + filter: + kind: once + file: ../src/utils/yield_cache/window_2/LoadToken_1.ts - kind: ethereum/contract name: TokenCacheWindow2_2 network: mainnet source: address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" abi: PreReplant - startBlock: 12974075 + startBlock: 12974076 + endBlock: 12974076 mapping: kind: ethereum/events apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - Diamond + - SiloYield abis: - name: PreReplant file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Pre-Replant.json - eventHandlers: - - event: DiamondCut((address,uint8,bytes4[])[],address,bytes) - handler: handleLoadToken2_2 - file: ../src/yield_cache/window_2/LoadToken_2.ts + blockHandlers: + - handler: handleLoadToken2_2 + filter: + kind: once + file: ../src/utils/yield_cache/window_2/LoadToken_2.ts - kind: ethereum/contract name: SiloCacheWindow2_1 network: mainnet source: address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" abi: PreReplant - startBlock: 12974075 + startBlock: 12974076 + endBlock: 12974076 mapping: kind: ethereum/events apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - Diamond + - SiloYield abis: - name: PreReplant file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Pre-Replant.json - eventHandlers: - - event: DiamondCut((address,uint8,bytes4[])[],address,bytes) - handler: handleLoadSilo2_1 - file: ../src/yield_cache/window_2/LoadSilo_1.ts + blockHandlers: + - handler: handleLoadSilo2_1 + filter: + kind: once + file: ../src/utils/yield_cache/window_2/LoadSilo_1.ts - kind: ethereum/contract name: SiloCacheWindow2_2 network: mainnet source: address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" abi: PreReplant - startBlock: 12974075 + startBlock: 12974076 + endBlock: 12974076 mapping: kind: ethereum/events apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - Diamond + - SiloYield abis: - name: PreReplant file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Pre-Replant.json - eventHandlers: - - event: DiamondCut((address,uint8,bytes4[])[],address,bytes) - handler: handleLoadSilo2_2 - file: ../src/yield_cache/window_2/LoadSilo_2.ts + blockHandlers: + - handler: handleLoadSilo2_2 + filter: + kind: once + file: ../src/utils/yield_cache/window_2/LoadSilo_2.ts - kind: ethereum/contract name: SiloCacheWindow2_3 network: mainnet source: address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" abi: PreReplant - startBlock: 12974075 + startBlock: 12974076 + endBlock: 12974076 mapping: kind: ethereum/events apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - Diamond + - SiloYield abis: - name: PreReplant file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Pre-Replant.json - eventHandlers: - - event: DiamondCut((address,uint8,bytes4[])[],address,bytes) - handler: handleLoadSilo2_3 - file: ../src/yield_cache/window_2/LoadSilo_3.ts - # Window 3 + blockHandlers: + - handler: handleLoadSilo2_3 + filter: + kind: once + file: ../src/utils/yield_cache/window_2/LoadSilo_3.ts - kind: ethereum/contract name: TokenCacheWindow3_1 network: mainnet source: address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" abi: PreReplant - startBlock: 12974075 + startBlock: 12974076 + endBlock: 12974076 mapping: kind: ethereum/events apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - Diamond + - SiloYield abis: - name: PreReplant file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Pre-Replant.json - eventHandlers: - - event: DiamondCut((address,uint8,bytes4[])[],address,bytes) - handler: handleLoadToken3_1 - file: ../src/yield_cache/window_3/LoadToken_1.ts + blockHandlers: + - handler: handleLoadToken3_1 + filter: + kind: once + file: ../src/utils/yield_cache/window_3/LoadToken_1.ts - kind: ethereum/contract name: TokenCacheWindow3_2 network: mainnet source: address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" abi: PreReplant - startBlock: 12974075 + startBlock: 12974076 + endBlock: 12974076 mapping: kind: ethereum/events apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - Diamond + - SiloYield abis: - name: PreReplant file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Pre-Replant.json - eventHandlers: - - event: DiamondCut((address,uint8,bytes4[])[],address,bytes) - handler: handleLoadToken3_2 - file: ../src/yield_cache/window_3/LoadToken_2.ts + blockHandlers: + - handler: handleLoadToken3_2 + filter: + kind: once + file: ../src/utils/yield_cache/window_3/LoadToken_2.ts - kind: ethereum/contract name: SiloCacheWindow3_1 network: mainnet source: address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" abi: PreReplant - startBlock: 12974075 + startBlock: 12974076 + endBlock: 12974076 mapping: kind: ethereum/events apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - Diamond + - SiloYield abis: - name: PreReplant file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Pre-Replant.json - eventHandlers: - - event: DiamondCut((address,uint8,bytes4[])[],address,bytes) - handler: handleLoadSilo3_1 - file: ../src/yield_cache/window_3/LoadSilo_1.ts + blockHandlers: + - handler: handleLoadSilo3_1 + filter: + kind: once + file: ../src/utils/yield_cache/window_3/LoadSilo_1.ts - kind: ethereum/contract name: SiloCacheWindow3_2 network: mainnet source: address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" abi: PreReplant - startBlock: 12974075 + startBlock: 12974076 + endBlock: 12974076 mapping: kind: ethereum/events apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - Diamond + - SiloYield abis: - name: PreReplant file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Pre-Replant.json - eventHandlers: - - event: DiamondCut((address,uint8,bytes4[])[],address,bytes) - handler: handleLoadSilo3_2 - file: ../src/yield_cache/window_3/LoadSilo_2.ts + blockHandlers: + - handler: handleLoadSilo3_2 + filter: + kind: once + file: ../src/utils/yield_cache/window_3/LoadSilo_2.ts - kind: ethereum/contract name: SiloCacheWindow3_3 network: mainnet source: address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" abi: PreReplant - startBlock: 12974075 + startBlock: 12974076 + endBlock: 12974076 mapping: kind: ethereum/events apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - Diamond + - SiloYield abis: - name: PreReplant file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Pre-Replant.json - eventHandlers: - - event: DiamondCut((address,uint8,bytes4[])[],address,bytes) - handler: handleLoadSilo3_3 - file: ../src/yield_cache/window_3/LoadSilo_3.ts - # Silo V3 + blockHandlers: + - handler: handleLoadSilo3_3 + filter: + kind: once + file: ../src/utils/yield_cache/window_3/LoadSilo_3.ts + ### + # INITIALIZATION + ### - kind: ethereum/contract - name: Silo-V3 + name: Version network: mainnet source: address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" - abi: SiloV3 - startBlock: 17636279 # Placeholder + abi: PreReplant + startBlock: 12974075 + endBlock: 12974075 mapping: kind: ethereum/events apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - Silo-V3 + - Version + abis: + - name: PreReplant + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Pre-Replant.json + blockHandlers: + - handler: handleInitVersion + filter: + kind: once + file: ../src/utils/Init.ts + ### + # EXPLOIT + ### + - kind: ethereum/contract + name: Exploit + network: mainnet + source: + address: "0xDC59ac4FeFa32293A95889Dc396682858d52e5Db" + abi: ERC20 + startBlock: 14602789 # Exploit + endBlock: 14602789 # Exploit + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Version abis: - - name: SiloV3 - file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP36.json - - name: UniswapV2Pair - file: ../../subgraph-core/abis/UniswapV2Pair.json - name: ERC20 file: ../../subgraph-core/abis/ERC20.json + blockHandlers: + - handler: handleExploit + filter: + kind: once + file: ../src/handlers/BeanHandler.ts + ### + # SILO + ### + - kind: ethereum/contract + name: Silo + network: mainnet + source: + address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" + abi: SeedGauge + startBlock: 15277986 # Replanted (Silo currently has no support for pre-exploit) + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Silo + abis: + - name: SeedGauge + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP45.json eventHandlers: - event: AddDeposit(indexed address,indexed address,int96,uint256,uint256) - handler: handleAddDeposit_V3 + handler: handleAddDeposit - event: RemoveDeposit(indexed address,indexed address,int96,uint256,uint256) - handler: handleRemoveDeposit_V3 + handler: handleRemoveDeposit - event: RemoveDeposits(indexed address,indexed address,int96[],uint256[],uint256,uint256[]) - handler: handleRemoveDeposits_V3 - - event: WhitelistToken(indexed address,bytes4,uint32,uint256) - handler: handleWhitelistToken_V3 + handler: handleRemoveDeposits + - event: Convert(indexed address,address,address,uint256,uint256) + handler: handleConvert + - event: StalkBalanceChanged(indexed address,int256,int256) + handler: handleStalkBalanceChanged + - event: SeedsBalanceChanged(indexed address,int256) + handler: handleSeedsBalanceChanged + - event: Plant(indexed address,uint256) + handler: handlePlant + - event: WhitelistToken(indexed address,bytes4,uint32,uint256,bytes4,bytes4,uint128,uint64) + handler: handleWhitelistToken + - event: DewhitelistToken(indexed address) + handler: handleDewhitelistToken - event: UpdatedStalkPerBdvPerSeason(indexed address,uint32,uint32) handler: handleUpdatedStalkPerBdvPerSeason - file: ../src/SiloHandler.ts - # Silo V2 / Replanted + - event: RemoveWithdrawal(indexed address,indexed address,uint32,uint256) + handler: handleRemoveWithdrawal + - event: RemoveWithdrawals(indexed address,indexed address,uint32[],uint256) + handler: handleRemoveWithdrawals + file: ../src/handlers/SiloHandler.ts - kind: ethereum/contract - name: Silo-Replanted + name: LegacySilo-Replanted-SeedGauge network: mainnet source: address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" - abi: MarketV2 - startBlock: 15277986 + abi: Replanted + startBlock: 15277986 # Replanted + endBlock: 19927634 # SeedGauge mapping: kind: ethereum/events apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - Silo-Replanted + - Silo abis: - - name: MarketV2 - file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP29.json - - name: UniswapV2Pair - file: ../../subgraph-core/abis/UniswapV2Pair.json - - name: ERC20 - file: ../../subgraph-core/abis/ERC20.json + - name: Replanted + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Replanted.json eventHandlers: - event: AddDeposit(indexed address,indexed address,uint32,uint256,uint256) - handler: handleAddDeposit + handler: handleAddDeposit_v2 - event: RemoveDeposit(indexed address,indexed address,uint32,uint256) - handler: handleRemoveDeposit + handler: handleRemoveDeposit_v2 - event: RemoveDeposits(indexed address,indexed address,uint32[],uint256[],uint256) - handler: handleRemoveDeposits + handler: handleRemoveDeposits_v2 - event: AddWithdrawal(indexed address,indexed address,uint32,uint256) handler: handleAddWithdrawal - - event: RemoveWithdrawal(indexed address,indexed address,uint32,uint256) - handler: handleRemoveWithdrawal - - event: RemoveWithdrawals(indexed address,indexed address,uint32[],uint256) - handler: handleRemoveWithdrawals - - event: SeedsBalanceChanged(indexed address,int256) - handler: handleSeedsBalanceChanged - - event: StalkBalanceChanged(indexed address,int256,int256) - handler: handleStalkBalanceChanged - - event: Plant(indexed address,uint256) - handler: handlePlant - event: WhitelistToken(indexed address,bytes4,uint256,uint256) - handler: handleWhitelistToken - - event: DewhitelistToken(indexed address) - handler: handleDewhitelistToken - file: ../src/SiloHandler.ts - # Field - Original + handler: handleWhitelistToken_v2 + file: ../src/handlers/legacy/LegacySiloHandler.ts + - kind: ethereum/contract + name: LegacySiloCalls-Replanted-SiloV3 + network: mainnet + source: + address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" + abi: Replanted + startBlock: 15277986 # Replanted + endBlock: 17671557 # SiloV3 + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Silo + abis: + - name: Replanted + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Replanted.json + callHandlers: + - function: transferDeposit(address,address,uint32,uint256) + handler: handleTransferDepositCall + - function: transferDeposits(address,address,uint32[],uint256[]) + handler: handleTransferDepositsCall + file: ../src/handlers/legacy/LegacySiloHandler.ts + - kind: ethereum/contract + name: LegacySilo-SiloV3-SeedGauge + network: mainnet + source: + address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" + abi: SiloV3 + startBlock: 17671557 # SiloV3 + endBlock: 19927634 # SeedGauge + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Silo + abis: + - name: SiloV3 + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP36.json + eventHandlers: + - event: WhitelistToken(indexed address,bytes4,uint32,uint256) + handler: handleWhitelistToken_v3 + file: ../src/handlers/legacy/LegacySiloHandler.ts + ### + # SEED GAUGE + ### + - kind: ethereum/contract + name: SeedGauge + network: mainnet + source: + address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" + abi: SeedGauge + startBlock: 19628074 + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - SeedGauge + abis: + - name: SeedGauge + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP45.json + - name: BeanstalkPrice + file: ../../subgraph-core/abis/BeanstalkPrice.json + eventHandlers: + - event: BeanToMaxLpGpPerBdvRatioChange(indexed uint256,uint256,int80) + handler: handleBeanToMaxLpGpPerBdvRatioChange + - event: GaugePointChange(indexed uint256,indexed address,uint256) + handler: handleGaugePointChange + - event: UpdateAverageStalkPerBdvPerSeason(uint256) + handler: handleUpdateAverageStalkPerBdvPerSeason + - event: FarmerGerminatingStalkBalanceChanged(indexed address,int256,uint8) + handler: handleFarmerGerminatingStalkBalanceChanged + - event: TotalGerminatingBalanceChanged(uint256,indexed address,int256,int256) + handler: handleTotalGerminatingBalanceChanged + - event: TotalGerminatingStalkChanged(uint256,int256) + handler: handleTotalGerminatingStalkChanged + - event: TotalStalkChangedFromGermination(int256,int256) + handler: handleTotalStalkChangedFromGermination + - event: UpdateGaugeSettings(indexed address,bytes4,bytes4,uint64) + handler: handleUpdateGaugeSettings + file: ../src/handlers/GaugeHandler.ts + ### + # FIELD + ### - kind: ethereum/contract name: Field network: mainnet source: address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" - abi: PreReplant - startBlock: 12974075 + abi: SeedGauge + startBlock: 12974075 # Field has all-time support mapping: kind: ethereum/events apiVersion: 0.0.6 @@ -398,25 +560,46 @@ dataSources: entities: - Field abis: - - name: PreReplant - file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Pre-Replant.json - - name: UniswapV2Pair - file: ../../subgraph-core/abis/UniswapV2Pair.json - - name: ERC20 - file: ../../subgraph-core/abis/ERC20.json + - name: SeedGauge + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP45.json - name: CurvePrice file: ../../subgraph-core/abis/CurvePrice.json - name: BeanstalkPrice file: ../../subgraph-core/abis/BeanstalkPrice.json eventHandlers: - - event: WeatherChange(indexed uint256,uint256,int8) - handler: handleWeatherChange - event: Sow(indexed address,uint256,uint256,uint256) handler: handleSow - event: Harvest(indexed address,uint256[],uint256) handler: handleHarvest - event: PlotTransfer(indexed address,indexed address,indexed uint256,uint256) handler: handlePlotTransfer + - event: TemperatureChange(indexed uint256,uint256,int8) + handler: handleTemperatureChange + file: ../src/handlers/FieldHandler.ts + - kind: ethereum/contract + name: LegacyField-PreReplant-SeedGauge + network: mainnet + source: + address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" + abi: PreReplant + startBlock: 12974075 + endBlock: 19927634 # SeedGauge + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Field + abis: + - name: PreReplant + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Pre-Replant.json + - name: CurvePrice + file: ../../subgraph-core/abis/CurvePrice.json + - name: BeanstalkPrice + file: ../../subgraph-core/abis/BeanstalkPrice.json + eventHandlers: + - event: WeatherChange(indexed uint256,uint256,int8) + handler: handleWeatherChange - event: SupplyIncrease(indexed uint256,uint256,uint256,uint256,int256) handler: handleSupplyIncrease - event: SupplyDecrease(indexed uint256,uint256,int256) @@ -425,361 +608,353 @@ dataSources: handler: handleSupplyNeutral - event: FundFundraiser(indexed address,indexed uint32,uint256) handler: handleFundFundraiser - file: ../src/FieldHandler.ts + file: ../src/handlers/legacy/LegacyFieldHandler.ts + ### + # MARKETPLACE + ### - kind: ethereum/contract - name: Season + name: Marketplace network: mainnet source: address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" - abi: PreReplant - startBlock: 12974075 + abi: SeedGauge + startBlock: 14148509 # BIP-11 Pod Marketplace mapping: kind: ethereum/events apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - Season + - PodMarketplace abis: - - name: PreReplant - file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Pre-Replant.json - - name: Replanted - file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Replanted.json - - name: UniswapV2Pair - file: ../../subgraph-core/abis/UniswapV2Pair.json - - name: ERC20 - file: ../../subgraph-core/abis/ERC20.json + - name: SeedGauge + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP45.json eventHandlers: - - event: Sunrise(indexed uint256) - handler: handleSunrise - - event: SeasonSnapshot(indexed uint32,uint256,uint256,uint256,uint256,uint256,uint256) - handler: handleSeasonSnapshot - - event: Incentivization(indexed address,uint256) - handler: handleIncentive - file: ../src/SeasonHandler.ts + - event: PodListingCreated(indexed address,uint256,uint256,uint256,uint24,uint256,uint256,bytes,uint8,uint8) + handler: handlePodListingCreated + - event: PodListingFilled(indexed address,indexed address,uint256,uint256,uint256,uint256) + handler: handlePodListingFilled + - event: PodOrderCreated(indexed address,bytes32,uint256,uint24,uint256,uint256,bytes,uint8) + handler: handlePodOrderCreated + - event: PodOrderFilled(indexed address,indexed address,bytes32,uint256,uint256,uint256,uint256) + handler: handlePodOrderFilled + # NOT a duplicate, this signature does not have an indexed uint256. + # Both signatures are required to recognize all events. + - event: PodListingCancelled(indexed address,uint256) + handler: handlePodListingCancelled + - event: PodOrderCancelled(indexed address,bytes32) + handler: handlePodOrderCancelled + file: ../src/handlers/MarketplaceHandler.ts - kind: ethereum/contract - name: Marketplace + name: LegacyMarketplace-PreReplant-SeedGauge network: mainnet source: address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" abi: PreReplant - startBlock: 12974075 + startBlock: 14148509 # BIP-11 Pod Marketplace + endBlock: 19927634 # SeedGauge mapping: kind: ethereum/events apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - Season + - PodMarketplace abis: - name: PreReplant file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Pre-Replant.json - - name: UniswapV2Pair - file: ../../subgraph-core/abis/UniswapV2Pair.json - - name: ERC20 - file: ../../subgraph-core/abis/ERC20.json eventHandlers: - event: PodListingCreated(indexed address,uint256,uint256,uint256,uint24,uint256,bool) - handler: handlePodListingCreated - - event: PodListingCancelled(indexed address,uint256) - handler: handlePodListingCancelled - - event: PodListingCancelled(indexed address,indexed uint256) - handler: handlePodListingCancelled + handler: handlePodListingCreated_v1 - event: PodListingFilled(indexed address,indexed address,uint256,uint256,uint256) - handler: handlePodListingFilled + handler: handlePodListingFilled_v1 - event: PodOrderCreated(indexed address,bytes32,uint256,uint24,uint256) - handler: handlePodOrderCreated + handler: handlePodOrderCreated_v1 - event: PodOrderFilled(indexed address,indexed address,bytes32,uint256,uint256,uint256) - handler: handlePodOrderFilled - - event: PodOrderCancelled(indexed address,bytes32) - handler: handlePodOrderCancelled - file: ../src/MarketplaceHandler.ts + handler: handlePodOrderFilled_v1 + # NOT a duplicate, this signature has an indexed uint256. + # Both signatures are required to recognize all events. + - event: PodListingCancelled(indexed address,indexed uint256) + handler: handlePodListingCancelled_indexed + file: ../src/handlers/legacy/LegacyMarketplaceHandler.ts - kind: ethereum/contract - name: Marketplace-Replanted + name: LegacyMarketplace-Replanted-SeedGauge network: mainnet source: address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" abi: Replanted - startBlock: 15277986 + startBlock: 15277986 # Replanted + endBlock: 19927634 # SeedGauge mapping: kind: ethereum/events apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - Marketplace-Replanted + - PodMarketplace abis: - name: Replanted file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Replanted.json - - name: UniswapV2Pair - file: ../../subgraph-core/abis/UniswapV2Pair.json - - name: ERC20 - file: ../../subgraph-core/abis/ERC20.json eventHandlers: - event: PodListingCreated(indexed address,uint256,uint256,uint256,uint24,uint256,uint8) handler: handlePodListingCreated_v1_1 - file: ../src/MarketplaceHandler.ts + file: ../src/handlers/legacy/LegacyMarketplaceHandler.ts + ### + # FERTILIZER + ### - kind: ethereum/contract - name: Diamond + name: Fertilizer-1155 network: mainnet source: - address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" - abi: PreReplant - startBlock: 12974075 + address: "0x402c84De2Ce49aF88f5e2eF3710ff89bFED36cB6" + abi: Fertilizer + startBlock: 14910573 mapping: kind: ethereum/events apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - Diamond + - Fertilizer abis: - - name: PreReplant - file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Pre-Replant.json + - name: Fertilizer + file: ../../subgraph-core/abis/Fertilizer.json + - name: SeedGauge + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP45.json eventHandlers: - - event: DiamondCut((address,uint8,bytes4[])[],address,bytes) - handler: handleDiamondCut - file: ../src/DiamondHandler.ts + - event: TransferBatch(indexed address,indexed address,indexed address,uint256[],uint256[]) + handler: handleTransferBatch + - event: TransferSingle(indexed address,indexed address,indexed address,uint256,uint256) + handler: handleTransferSingle + file: ../src/handlers/BarnHandler.ts - kind: ethereum/contract - name: Bean + name: Fertilizer-Beanstalk network: mainnet source: - address: "0xDC59ac4FeFa32293A95889Dc396682858d52e5Db" - abi: ERC20 - startBlock: 12974075 + address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" + abi: SeedGauge + startBlock: 15277986 mapping: kind: ethereum/events apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - Bean + - Chop abis: - - name: ERC20 - file: ../../subgraph-core/abis/ERC20.json - - name: PreReplant - file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Pre-Replant.json + - name: SeedGauge + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP45.json eventHandlers: - - event: Transfer(indexed address,indexed address,uint256) - handler: handleLegacyTransfer - file: ../src/BeanHandler.ts + - event: Chop(indexed address,indexed address,uint256,uint256) + handler: handleChop + - event: ChangeUnderlying(indexed address,int256) + handler: handleChangeUnderlying + file: ../src/handlers/BarnHandler.ts + ### + # SEASON + ### - kind: ethereum/contract - name: Bean-Replanted + name: Season network: mainnet source: - address: "0xbea0000029ad1c77d3d5d23ba2d8893db9d1efab" - abi: ERC20 - startBlock: 15277986 + address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" + abi: SeedGauge + startBlock: 12974075 mapping: kind: ethereum/events apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - Bean + - Season abis: - - name: ERC20 - file: ../../subgraph-core/abis/ERC20.json - - name: PreReplant - file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Pre-Replant.json + - name: SeedGauge + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP45.json + - name: BeanstalkPrice + file: ../../subgraph-core/abis/BeanstalkPrice.json eventHandlers: - - event: Transfer(indexed address,indexed address,uint256) - handler: handleTransfer - file: ../src/BeanHandler.ts + - event: Reward(indexed uint32,uint256,uint256,uint256) + handler: handleReward + - event: WellOracle(indexed uint32,address,int256,bytes) + handler: handleWellOracle + - event: Soil(indexed uint32,uint256) + handler: handleSoil + - event: Incentivization(indexed address,uint256) + handler: handleIncentive + file: ../src/handlers/SeasonHandler.ts - kind: ethereum/contract - name: Replant + name: LegacySeason-PreReplant-SeedGauge network: mainnet source: address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" - abi: MarketV2 - startBlock: 15277986 + abi: PreReplant + startBlock: 12974075 + endBlock: 19927634 # SeedGauge mapping: kind: ethereum/events apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - Replant + - Season abis: - - name: MarketV2 - file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP29.json - - name: UniswapV2Pair - file: ../../subgraph-core/abis/UniswapV2Pair.json - - name: ERC20 - file: ../../subgraph-core/abis/ERC20.json + - name: PreReplant + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Pre-Replant.json eventHandlers: - - event: Chop(indexed address,indexed address,uint256,uint256) - handler: handleChop - file: ../src/ReplantHandler.ts + - event: SeasonSnapshot(indexed uint32,uint256,uint256,uint256,uint256,uint256,uint256) + handler: handleSeasonSnapshot + file: ../src/handlers/legacy/LegacySeasonHandler.ts - kind: ethereum/contract - name: Season-Replanted + name: LegacySeason-Replanted-SeedGauge network: mainnet source: address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" - abi: BasinBip - startBlock: 15277986 + abi: Replanted + startBlock: 15277986 # Replanted + endBlock: 19927634 # SeedGauge mapping: kind: ethereum/events apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - Season-Replanted + - Season abis: - - name: BasinBip - file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP37.json - - name: UniswapV2Pair - file: ../../subgraph-core/abis/UniswapV2Pair.json - - name: ERC20 - file: ../../subgraph-core/abis/ERC20.json + - name: Replanted + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Replanted.json - name: CurvePrice file: ../../subgraph-core/abis/CurvePrice.json - name: BeanstalkPrice file: ../../subgraph-core/abis/BeanstalkPrice.json eventHandlers: - - event: Reward(indexed uint32,uint256,uint256,uint256) - handler: handleReward - event: MetapoolOracle(indexed uint32,int256,uint256[2]) handler: handleMetapoolOracle - - event: WellOracle(indexed uint32,address,int256,bytes) - handler: handleWellOracle - - event: Soil(indexed uint32,uint256) - handler: handleSoil - file: ../src/SeasonHandler.ts + file: ../src/handlers/legacy/LegacySeasonHandler.ts - kind: ethereum/contract - name: Fertilizer + name: Season-PreReplant-Replanted network: mainnet source: - address: "0x402c84De2Ce49aF88f5e2eF3710ff89bFED36cB6" - abi: Fertilizer - startBlock: 14910573 + address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" + abi: SeedGauge + startBlock: 12974075 + endBlock: 15277986 # Replanted mapping: kind: ethereum/events apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - Fertilizer + - Season abis: - - name: Fertilizer - file: ../../subgraph-core/abis/Fertilizer.json - - name: MarketV2 - file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP29.json + - name: SeedGauge + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP45.json eventHandlers: - - event: TransferBatch(indexed address,indexed address,indexed address,uint256[],uint256[]) - handler: handleTransferBatch - - event: TransferSingle(indexed address,indexed address,indexed address,uint256,uint256) - handler: handleTransferSingle - file: ../src/FertilizerHandler.ts + - event: Sunrise(indexed uint256) + handler: handleSunrise + file: ../src/handlers/SeasonHandler.ts - kind: ethereum/contract - name: Farm + name: LegacySeason-Replanted-SiloV3 network: mainnet source: address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" - abi: MarketV2 - startBlock: 15277986 + abi: Replanted + startBlock: 15277986 # Replanted + endBlock: 17671557 # SiloV3 mapping: kind: ethereum/events apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - Farm + - Season abis: - - name: MarketV2 - file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP29.json - - name: ERC20 - file: ../../subgraph-core/abis/ERC20.json + - name: Replanted + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Replanted.json + - name: SeedGauge + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP45.json eventHandlers: - - event: InternalBalanceChanged(indexed address,indexed address,int256) - handler: handleInternalBalanceChanged - file: ../src/FarmHandler.ts + - event: Sunrise(indexed uint256) + handler: handleReplantSunrise + file: ../src/handlers/legacy/LegacySeasonHandler.ts - kind: ethereum/contract - name: Silo-Calls + name: Season-SiloV3- network: mainnet source: address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" - abi: Replanted - startBlock: 15277986 + abi: SeedGauge + startBlock: 17671557 # SiloV3 mapping: kind: ethereum/events apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - Silo + - Season + abis: + - name: SeedGauge + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP45.json + eventHandlers: + - event: Sunrise(indexed uint256) + handler: handleSunrise + file: ../src/handlers/SeasonHandler.ts + ### + # BEAN ERC20 + ### + - kind: ethereum/contract + name: Bean-Replanted + network: mainnet + source: + address: "0xbea0000029ad1c77d3d5d23ba2d8893db9d1efab" + abi: ERC20 + startBlock: 15277986 # Replanted + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Season abis: - - name: Replanted - file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Replanted.json - name: ERC20 file: ../../subgraph-core/abis/ERC20.json - callHandlers: - - function: transferDeposit(address,address,uint32,uint256) - handler: handleTransferDepositCall - - function: transferDeposits(address,address,uint32[],uint256[]) - handler: handleTransferDepositsCall - file: ../src/SiloHandler.ts + eventHandlers: + - event: Transfer(indexed address,indexed address,uint256) + handler: handleTransfer + file: ../src/handlers/BeanHandler.ts - kind: ethereum/contract - name: BIP29-PodMarketplace + name: Bean-PreReplant-Exploit network: mainnet source: - address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" - abi: MarketV2 - startBlock: 15277986 + address: "0xDC59ac4FeFa32293A95889Dc396682858d52e5Db" + abi: ERC20 + startBlock: 12974075 + endBlock: 14602789 # Exploit mapping: kind: ethereum/events apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - PodMarketplaceV2 + - Season abis: - - name: MarketV2 - file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP29.json - - name: UniswapV2Pair - file: ../../subgraph-core/abis/UniswapV2Pair.json - name: ERC20 file: ../../subgraph-core/abis/ERC20.json eventHandlers: - - event: PodListingCreated(indexed address,uint256,uint256,uint256,uint24,uint256,uint256,bytes,uint8,uint8) - handler: handlePodListingCreated_v2 - - event: PodListingFilled(indexed address,indexed address,uint256,uint256,uint256,uint256) - handler: handlePodListingFilled_v2 - - event: PodOrderCreated(indexed address,bytes32,uint256,uint24,uint256,uint256,bytes,uint8) - handler: handlePodOrderCreated_v2 - - event: PodOrderFilled(indexed address,indexed address,bytes32,uint256,uint256,uint256,uint256) - handler: handlePodOrderFilled_v2 - file: ../src/MarketplaceHandler.ts + - event: Transfer(indexed address,indexed address,uint256) + handler: handleTransfer + file: ../src/handlers/BeanHandler.ts + ### + # FARM BALANCE + ### - kind: ethereum/contract - name: BIP45-SeedGauge + name: Farm network: mainnet source: address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" abi: SeedGauge - startBlock: 19628074 + startBlock: 15277986 # Replanted mapping: kind: ethereum/events apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - SeedGauge + - SiloAsset abis: - name: SeedGauge file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP45.json - - name: BeanstalkPrice - file: ../../subgraph-core/abis/BeanstalkPrice.json eventHandlers: - - event: TemperatureChange(indexed uint256,uint256,int8) - handler: handleTemperatureChange - - event: BeanToMaxLpGpPerBdvRatioChange(indexed uint256,uint256,int80) - handler: handleBeanToMaxLpGpPerBdvRatioChange - - event: GaugePointChange(indexed uint256,indexed address,uint256) - handler: handleGaugePointChange - - event: UpdateAverageStalkPerBdvPerSeason(uint256) - handler: handleUpdateAverageStalkPerBdvPerSeason - - event: FarmerGerminatingStalkBalanceChanged(indexed address,int256,uint8) - handler: handleFarmerGerminatingStalkBalanceChanged - - event: TotalGerminatingBalanceChanged(uint256,indexed address,int256,int256) - handler: handleTotalGerminatingBalanceChanged - - event: TotalGerminatingStalkChanged(uint256,int256) - handler: handleTotalGerminatingStalkChanged - - event: TotalStalkChangedFromGermination(int256,int256) - handler: handleTotalStalkChangedFromGermination - - event: WhitelistToken(indexed address,bytes4,uint32,uint256,bytes4,bytes4,uint128,uint64) - handler: handleWhitelistToken_BIP45 - - event: UpdateGaugeSettings(indexed address,bytes4,bytes4,uint64) - handler: handleUpdateGaugeSettings - file: ../src/GaugeHandler.ts + - event: InternalBalanceChanged(indexed address,indexed address,int256) + handler: handleInternalBalanceChanged + file: ../src/handlers/FarmHandler.ts # features: # - grafting # graft: -# base: QmcZq7RVCbzixsh7PoHCVKXHsXnpigNo7JWvzj6rUsuvPd -# block: 19393254 +# base: QmUqT47H7o3gcZ2mHRJf2J5hpitWDYxsMmacGeuvo1wKU7 +# block: 19927630 diff --git a/projects/subgraph-beanstalk/manifests/no-apy.yaml b/projects/subgraph-beanstalk/manifests/no-apy.yaml new file mode 100644 index 0000000000..b3b188e95c --- /dev/null +++ b/projects/subgraph-beanstalk/manifests/no-apy.yaml @@ -0,0 +1,595 @@ +# Use this file for faster testing of whether the subgraph build works +specVersion: 0.0.9 +schema: + file: ../schema.graphql +dataSources: + ### + # INITIALIZATION + ### + - kind: ethereum/contract + name: Version + network: mainnet + source: + address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" + abi: PreReplant + startBlock: 12974075 + endBlock: 12974075 + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Version + abis: + - name: PreReplant + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Pre-Replant.json + blockHandlers: + - handler: handleInitVersion + filter: + kind: once + file: ../src/utils/Init.ts + ### + # SILO + ### + - kind: ethereum/contract + name: Silo + network: mainnet + source: + address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" + abi: SeedGauge + startBlock: 15277986 # Replanted (Silo currently has no support for pre-exploit) + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Silo + abis: + - name: SeedGauge + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP45.json + eventHandlers: + - event: AddDeposit(indexed address,indexed address,int96,uint256,uint256) + handler: handleAddDeposit + - event: RemoveDeposit(indexed address,indexed address,int96,uint256,uint256) + handler: handleRemoveDeposit + - event: RemoveDeposits(indexed address,indexed address,int96[],uint256[],uint256,uint256[]) + handler: handleRemoveDeposits + - event: Convert(indexed address,address,address,uint256,uint256) + handler: handleConvert + - event: StalkBalanceChanged(indexed address,int256,int256) + handler: handleStalkBalanceChanged + - event: SeedsBalanceChanged(indexed address,int256) + handler: handleSeedsBalanceChanged + - event: Plant(indexed address,uint256) + handler: handlePlant + - event: WhitelistToken(indexed address,bytes4,uint32,uint256,bytes4,bytes4,uint128,uint64) + handler: handleWhitelistToken + - event: DewhitelistToken(indexed address) + handler: handleDewhitelistToken + - event: UpdatedStalkPerBdvPerSeason(indexed address,uint32,uint32) + handler: handleUpdatedStalkPerBdvPerSeason + - event: RemoveWithdrawal(indexed address,indexed address,uint32,uint256) + handler: handleRemoveWithdrawal + - event: RemoveWithdrawals(indexed address,indexed address,uint32[],uint256) + handler: handleRemoveWithdrawals + file: ../src/handlers/SiloHandler.ts + - kind: ethereum/contract + name: LegacySilo-Replanted-SeedGauge + network: mainnet + source: + address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" + abi: Replanted + startBlock: 15277986 # Replanted + endBlock: 19927634 # SeedGauge + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Silo + abis: + - name: Replanted + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Replanted.json + eventHandlers: + - event: AddDeposit(indexed address,indexed address,uint32,uint256,uint256) + handler: handleAddDeposit_v2 + - event: RemoveDeposit(indexed address,indexed address,uint32,uint256) + handler: handleRemoveDeposit_v2 + - event: RemoveDeposits(indexed address,indexed address,uint32[],uint256[],uint256) + handler: handleRemoveDeposits_v2 + - event: AddWithdrawal(indexed address,indexed address,uint32,uint256) + handler: handleAddWithdrawal + - event: WhitelistToken(indexed address,bytes4,uint256,uint256) + handler: handleWhitelistToken_v2 + file: ../src/handlers/legacy/LegacySiloHandler.ts + - kind: ethereum/contract + name: LegacySiloCalls-Replanted-SiloV3 + network: mainnet + source: + address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" + abi: Replanted + startBlock: 15277986 # Replanted + endBlock: 17671557 # SiloV3 + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Silo + abis: + - name: Replanted + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Replanted.json + callHandlers: + - function: transferDeposit(address,address,uint32,uint256) + handler: handleTransferDepositCall + - function: transferDeposits(address,address,uint32[],uint256[]) + handler: handleTransferDepositsCall + file: ../src/handlers/legacy/LegacySiloHandler.ts + - kind: ethereum/contract + name: LegacySilo-SiloV3-SeedGauge + network: mainnet + source: + address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" + abi: SiloV3 + startBlock: 17671557 # SiloV3 + endBlock: 19927634 # SeedGauge + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Silo + abis: + - name: SiloV3 + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP36.json + eventHandlers: + - event: WhitelistToken(indexed address,bytes4,uint32,uint256) + handler: handleWhitelistToken_v3 + file: ../src/handlers/legacy/LegacySiloHandler.ts + ### + # SEED GAUGE + ### + - kind: ethereum/contract + name: SeedGauge + network: mainnet + source: + address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" + abi: SeedGauge + startBlock: 19628074 + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - SeedGauge + abis: + - name: SeedGauge + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP45.json + - name: BeanstalkPrice + file: ../../subgraph-core/abis/BeanstalkPrice.json + eventHandlers: + - event: BeanToMaxLpGpPerBdvRatioChange(indexed uint256,uint256,int80) + handler: handleBeanToMaxLpGpPerBdvRatioChange + - event: GaugePointChange(indexed uint256,indexed address,uint256) + handler: handleGaugePointChange + - event: UpdateAverageStalkPerBdvPerSeason(uint256) + handler: handleUpdateAverageStalkPerBdvPerSeason + - event: FarmerGerminatingStalkBalanceChanged(indexed address,int256,uint8) + handler: handleFarmerGerminatingStalkBalanceChanged + - event: TotalGerminatingBalanceChanged(uint256,indexed address,int256,int256) + handler: handleTotalGerminatingBalanceChanged + - event: TotalGerminatingStalkChanged(uint256,int256) + handler: handleTotalGerminatingStalkChanged + - event: TotalStalkChangedFromGermination(int256,int256) + handler: handleTotalStalkChangedFromGermination + - event: UpdateGaugeSettings(indexed address,bytes4,bytes4,uint64) + handler: handleUpdateGaugeSettings + file: ../src/handlers/GaugeHandler.ts + ### + # FIELD + ### + - kind: ethereum/contract + name: Field + network: mainnet + source: + address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" + abi: SeedGauge + startBlock: 12974075 # Field has all-time support + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Field + abis: + - name: SeedGauge + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP45.json + - name: CurvePrice + file: ../../subgraph-core/abis/CurvePrice.json + - name: BeanstalkPrice + file: ../../subgraph-core/abis/BeanstalkPrice.json + eventHandlers: + - event: Sow(indexed address,uint256,uint256,uint256) + handler: handleSow + - event: Harvest(indexed address,uint256[],uint256) + handler: handleHarvest + - event: PlotTransfer(indexed address,indexed address,indexed uint256,uint256) + handler: handlePlotTransfer + - event: TemperatureChange(indexed uint256,uint256,int8) + handler: handleTemperatureChange + file: ../src/handlers/FieldHandler.ts + - kind: ethereum/contract + name: LegacyField-PreReplant-SeedGauge + network: mainnet + source: + address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" + abi: PreReplant + startBlock: 12974075 + endBlock: 19927634 # SeedGauge + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Field + abis: + - name: PreReplant + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Pre-Replant.json + - name: CurvePrice + file: ../../subgraph-core/abis/CurvePrice.json + - name: BeanstalkPrice + file: ../../subgraph-core/abis/BeanstalkPrice.json + eventHandlers: + - event: WeatherChange(indexed uint256,uint256,int8) + handler: handleWeatherChange + - event: SupplyIncrease(indexed uint256,uint256,uint256,uint256,int256) + handler: handleSupplyIncrease + - event: SupplyDecrease(indexed uint256,uint256,int256) + handler: handleSupplyDecrease + - event: SupplyNeutral(indexed uint256,int256) + handler: handleSupplyNeutral + - event: FundFundraiser(indexed address,indexed uint32,uint256) + handler: handleFundFundraiser + file: ../src/handlers/legacy/LegacyFieldHandler.ts + ### + # MARKETPLACE + ### + - kind: ethereum/contract + name: Marketplace + network: mainnet + source: + address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" + abi: SeedGauge + startBlock: 14148509 # BIP-11 Pod Marketplace + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - PodMarketplace + abis: + - name: SeedGauge + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP45.json + eventHandlers: + - event: PodListingCreated(indexed address,uint256,uint256,uint256,uint24,uint256,uint256,bytes,uint8,uint8) + handler: handlePodListingCreated + - event: PodListingFilled(indexed address,indexed address,uint256,uint256,uint256,uint256) + handler: handlePodListingFilled + - event: PodOrderCreated(indexed address,bytes32,uint256,uint24,uint256,uint256,bytes,uint8) + handler: handlePodOrderCreated + - event: PodOrderFilled(indexed address,indexed address,bytes32,uint256,uint256,uint256,uint256) + handler: handlePodOrderFilled + # NOT a duplicate, this signature does not have an indexed uint256. + # Both signatures are required to recognize all events. + - event: PodListingCancelled(indexed address,uint256) + handler: handlePodListingCancelled + - event: PodOrderCancelled(indexed address,bytes32) + handler: handlePodOrderCancelled + file: ../src/handlers/MarketplaceHandler.ts + - kind: ethereum/contract + name: LegacyMarketplace-PreReplant-SeedGauge + network: mainnet + source: + address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" + abi: PreReplant + startBlock: 14148509 # BIP-11 Pod Marketplace + endBlock: 19927634 # SeedGauge + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - PodMarketplace + abis: + - name: PreReplant + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Pre-Replant.json + eventHandlers: + - event: PodListingCreated(indexed address,uint256,uint256,uint256,uint24,uint256,bool) + handler: handlePodListingCreated_v1 + - event: PodListingFilled(indexed address,indexed address,uint256,uint256,uint256) + handler: handlePodListingFilled_v1 + - event: PodOrderCreated(indexed address,bytes32,uint256,uint24,uint256) + handler: handlePodOrderCreated_v1 + - event: PodOrderFilled(indexed address,indexed address,bytes32,uint256,uint256,uint256) + handler: handlePodOrderFilled_v1 + # NOT a duplicate, this signature has an indexed uint256. + # Both signatures are required to recognize all events. + - event: PodListingCancelled(indexed address,indexed uint256) + handler: handlePodListingCancelled_indexed + file: ../src/handlers/legacy/LegacyMarketplaceHandler.ts + - kind: ethereum/contract + name: LegacyMarketplace-Replanted-SeedGauge + network: mainnet + source: + address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" + abi: Replanted + startBlock: 15277986 # Replanted + endBlock: 19927634 # SeedGauge + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - PodMarketplace + abis: + - name: Replanted + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Replanted.json + eventHandlers: + - event: PodListingCreated(indexed address,uint256,uint256,uint256,uint24,uint256,uint8) + handler: handlePodListingCreated_v1_1 + file: ../src/handlers/legacy/LegacyMarketplaceHandler.ts + ### + # FERTILIZER + ### + - kind: ethereum/contract + name: Fertilizer-1155 + network: mainnet + source: + address: "0x402c84De2Ce49aF88f5e2eF3710ff89bFED36cB6" + abi: Fertilizer + startBlock: 14910573 + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Fertilizer + abis: + - name: Fertilizer + file: ../../subgraph-core/abis/Fertilizer.json + - name: SeedGauge + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP45.json + eventHandlers: + - event: TransferBatch(indexed address,indexed address,indexed address,uint256[],uint256[]) + handler: handleTransferBatch + - event: TransferSingle(indexed address,indexed address,indexed address,uint256,uint256) + handler: handleTransferSingle + file: ../src/handlers/BarnHandler.ts + - kind: ethereum/contract + name: Fertilizer-Beanstalk + network: mainnet + source: + address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" + abi: SeedGauge + startBlock: 15277986 + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Chop + abis: + - name: SeedGauge + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP45.json + eventHandlers: + - event: Chop(indexed address,indexed address,uint256,uint256) + handler: handleChop + - event: ChangeUnderlying(indexed address,int256) + handler: handleChangeUnderlying + file: ../src/handlers/BarnHandler.ts + ### + # SEASON + ### + - kind: ethereum/contract + name: Season + network: mainnet + source: + address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" + abi: SeedGauge + startBlock: 12974075 + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Season + abis: + - name: SeedGauge + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP45.json + - name: BeanstalkPrice + file: ../../subgraph-core/abis/BeanstalkPrice.json + eventHandlers: + - event: Reward(indexed uint32,uint256,uint256,uint256) + handler: handleReward + - event: WellOracle(indexed uint32,address,int256,bytes) + handler: handleWellOracle + - event: Soil(indexed uint32,uint256) + handler: handleSoil + - event: Incentivization(indexed address,uint256) + handler: handleIncentive + file: ../src/handlers/SeasonHandler.ts + - kind: ethereum/contract + name: LegacySeason-PreReplant-SeedGauge + network: mainnet + source: + address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" + abi: PreReplant + startBlock: 12974075 + endBlock: 19927634 # SeedGauge + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Season + abis: + - name: PreReplant + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Pre-Replant.json + eventHandlers: + - event: SeasonSnapshot(indexed uint32,uint256,uint256,uint256,uint256,uint256,uint256) + handler: handleSeasonSnapshot + file: ../src/handlers/legacy/LegacySeasonHandler.ts + - kind: ethereum/contract + name: LegacySeason-Replanted-SeedGauge + network: mainnet + source: + address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" + abi: Replanted + startBlock: 15277986 # Replanted + endBlock: 19927634 # SeedGauge + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Season + abis: + - name: Replanted + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Replanted.json + - name: CurvePrice + file: ../../subgraph-core/abis/CurvePrice.json + - name: BeanstalkPrice + file: ../../subgraph-core/abis/BeanstalkPrice.json + eventHandlers: + - event: MetapoolOracle(indexed uint32,int256,uint256[2]) + handler: handleMetapoolOracle + file: ../src/handlers/legacy/LegacySeasonHandler.ts + - kind: ethereum/contract + name: Season-PreReplant-Replanted + network: mainnet + source: + address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" + abi: PreReplant + startBlock: 12974075 + endBlock: 15277986 # Replanted + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Season + abis: + - name: PreReplant + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Pre-Replant.json + eventHandlers: + - event: Sunrise(indexed uint256) + handler: handleSunrise + file: ../src/handlers/SeasonHandler.ts + - kind: ethereum/contract + name: LegacySeason-Replanted-SiloV3 + network: mainnet + source: + address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" + abi: Replanted + startBlock: 15277986 # Replanted + endBlock: 17671557 # SiloV3 + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Season + abis: + - name: Replanted + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-Replanted.json + eventHandlers: + - event: Sunrise(indexed uint256) + handler: handleReplantSunrise + file: ../src/handlers/legacy/LegacySeasonHandler.ts + - kind: ethereum/contract + name: Season-SiloV3- + network: mainnet + source: + address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" + abi: SeedGauge + startBlock: 17671557 # SiloV3 + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Season + abis: + - name: SeedGauge + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP45.json + eventHandlers: + - event: Sunrise(indexed uint256) + handler: handleSunrise + file: ../src/handlers/SeasonHandler.ts + ### + # BEAN ERC20 + ### + - kind: ethereum/contract + name: Bean-Replanted + network: mainnet + source: + address: "0xbea0000029ad1c77d3d5d23ba2d8893db9d1efab" + abi: ERC20 + startBlock: 15277986 # Replanted + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Season + abis: + - name: ERC20 + file: ../../subgraph-core/abis/ERC20.json + eventHandlers: + - event: Transfer(indexed address,indexed address,uint256) + handler: handleTransfer + file: ../src/handlers/BeanHandler.ts + - kind: ethereum/contract + name: Bean-PreReplant-Exploit + network: mainnet + source: + address: "0xDC59ac4FeFa32293A95889Dc396682858d52e5Db" + abi: ERC20 + startBlock: 12974075 + endBlock: 14602789 # Exploit + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Season + abis: + - name: ERC20 + file: ../../subgraph-core/abis/ERC20.json + eventHandlers: + - event: Transfer(indexed address,indexed address,uint256) + handler: handleTransfer + file: ../src/handlers/BeanHandler.ts + ### + # FARM BALANCE + ### + - kind: ethereum/contract + name: Farm + network: mainnet + source: + address: "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5" + abi: SeedGauge + startBlock: 15277986 # Replanted + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - SiloAsset + abis: + - name: SeedGauge + file: ../../subgraph-core/abis/Beanstalk/Beanstalk-BIP45.json + eventHandlers: + - event: InternalBalanceChanged(indexed address,indexed address,int256) + handler: handleInternalBalanceChanged + file: ../src/handlers/FarmHandler.ts diff --git a/projects/subgraph-beanstalk/schema.graphql b/projects/subgraph-beanstalk/schema.graphql index ee657640a4..e486ed70c7 100644 --- a/projects/subgraph-beanstalk/schema.graphql +++ b/projects/subgraph-beanstalk/schema.graphql @@ -21,86 +21,67 @@ enum EmaWindow { ROLLING_30_DAY } -type Beanstalk @entity { - " Smart contract address of the protocol's main contract (Factory, Registry, etc) " +# This same entity schema is intended for use across the subgraphs +type Version @entity { + "= 'subgraph'" id: ID! + "= 'beanstalk'" + subgraphName: String! + "Verison number of the subgraph" + versionNumber: String! + "Which blockchain is being indexed, i.e. 'ethereum', 'arbitrum', etc." + chain: String! +} - " Name of the protocol, including version. e.g. Uniswap v3 " +type Beanstalk @entity { + "Smart contract address of the protocol's main contract (Factory, Registry, etc) " + id: ID! + "Name of the protocol, including version. e.g. Uniswap v3" name: String! - - " Slug of protocol, including version. e.g. uniswap-v3 " - slug: String! - - " Version of the subgraph schema, in SemVer format (e.g. 1.0.0) " - schemaVersion: String! - - " Version of the subgraph implementation, in SemVer format (e.g. 1.0.0) " - subgraphVersion: String! - - " Version of the methodology used to compute metrics, loosely based on SemVer format (e.g. 1.0.0) " - methodologyVersion: String! - - " Timestamp of the latest DiamondCut call " - lastUpgrade: BigInt! - - " Season specific data " + "Bean token address of the protocol" + token: String! + "Address of the fertilizer contract" + fertilizer1155: String + "Season specific data" seasons: [Season!]! @derivedFrom(field: "beanstalk") - - " Silo level data " + "Silo level data" silo: Silo! @derivedFrom(field: "beanstalk") - - " Field level data " + "Field level data" field: Field! @derivedFrom(field: "beanstalk") - - " Last season called " + "Last season called" lastSeason: Int! - - " Array of the addresses for all active farmers in the silo " + "Array of the addresses for all active farmers in the silo" activeFarmers: [String!]! - - " Array of the addresses for all farmers that had silo transfers and need stalk/seeds/roots updated " + "Array of the addresses for all farmers that had silo transfers and need stalk/seeds/roots updated" farmersToUpdate: [String!]! } -# An entity that holds season level data type Season @entity { - " Season Number" + "Season Number" id: ID! - - " Beanstalk Contract Address " + "Beanstalk Contract Address" beanstalk: Beanstalk! - - " Season number in Int form for sorting " + "Season number in Int form for sorting" season: Int! - - " Block in which the season start was triggered by the sunrise call " + "Block in which the season start was triggered by the sunrise call" sunriseBlock: BigInt! - - " Block timestamp when sunrise was called " + "Block timestamp when sunrise was called" createdAt: BigInt! - - " Price of BEAN during sunrise " + "Price of BEAN during sunrise" price: BigDecimal! - - " Total Bean supply " + "Total Bean supply" beans: BigInt! - - " Bean Market Cap " + "Bean Market Cap" marketCap: BigDecimal! - - " Time weighted deltaB " + "Time weighted deltaB" deltaB: BigInt! - - " Change in Bean supply " + "Change in Bean supply" deltaBeans: BigInt! - - " Amount of Beans minted during sunrise " + "Amount of Beans minted during sunrise" rewardBeans: BigInt! - - " Amount of Beans paid to sunrise caller " + "Amount of Beans paid to sunrise caller" incentiveBeans: BigInt! - - " New harvestable index for the season " + "New harvestable index for the season" harvestableIndex: BigInt! } @@ -137,6 +118,10 @@ type Silo @entity { beanMints: BigInt! "Current number of active farmers deposited in the silo" activeFarmers: Int! + "Season when the previous hourly snapshot was taken/updated" + lastHourlySnapshotSeason: Int + "Day of when the previous daily snapshot was taken/updated" + lastDailySnapshotDay: BigInt "Link to hourly snapshot data" hourlySnapshots: [SiloHourlySnapshot!]! @derivedFrom(field: "silo") "Link to daily snapshot data" @@ -144,7 +129,7 @@ type Silo @entity { } type SiloHourlySnapshot @entity { - "ID of silo-Unix Hour Timestamp" + "ID of silo - Season" id: ID! "Season for the snapshot" season: Int! @@ -170,32 +155,28 @@ type SiloHourlySnapshot @entity { beanMints: BigInt! "Point in time current number of active farmers deposited in the silo" activeFarmers: Int! - "Point in time delta BDV of all deposited assets" + deltaDepositedBDV: BigInt! - "Point in time delta stalk balance" deltaStalk: BigInt! - "Point in time current plantable stalk for bean seigniorage not yet claimed" deltaPlantableStalk: BigInt! - "Point in time delta seeds balance" deltaSeeds: BigInt! - "Point in time delta roots balance" deltaRoots: BigInt! - "Point in time germinating stalk balance" deltaGerminatingStalk: BigInt! - "Point in time delta total for bean mints sent to the silo" deltaBeanMints: BigInt! - "Point in time delta number of active farmers deposited in the silo" deltaActiveFarmers: Int! - "[Seed Gauge] The caseId used in the seasonal adjustment of beanToMaxLpGpPerBdvRatio" - caseId: BigInt + "Timestamp of initial snapshot creation" createdAt: BigInt! "Timestamp of last entity update" updatedAt: BigInt! + + # caseId is unique to hourly snapshot + "[Seed Gauge] The caseId used in the seasonal adjustment of beanToMaxLpGpPerBdvRatio" + caseId: BigInt } type SiloDailySnapshot @entity { - "ID of silo-Unix Hour Timestamp" + "ID of silo - Day" id: ID! "Last season for the snapshot" season: Int! @@ -221,22 +202,16 @@ type SiloDailySnapshot @entity { beanMints: BigInt! "Point in time current number of active farmers deposited in the silo" activeFarmers: Int! - "Point in time delta BDV of all deposited assets" + deltaDepositedBDV: BigInt! - "Point in time delta stalk balance" deltaStalk: BigInt! - "Point in time current plantable stalk for bean seigniorage not yet claimed" deltaPlantableStalk: BigInt! - "Point in time delta seeds balance" deltaSeeds: BigInt! - "Point in time delta roots balance" deltaRoots: BigInt! - "Point in time germinating stalk balance" deltaGerminatingStalk: BigInt! - "Point in time delta total for bean mints sent to the silo" deltaBeanMints: BigInt! - "Point in time delta number of active farmers deposited in the silo" deltaActiveFarmers: Int! + "Timestamp of initial snapshot creation" createdAt: BigInt! "Timestamp of last entity update" @@ -250,14 +225,18 @@ type SiloAsset @entity { silo: Silo! "Token address for this asset" token: String! - "Current BDV of deposits" - depositedBDV: BigInt! "Current Token amount of deposits" depositedAmount: BigInt! + "Current BDV of deposits" + depositedBDV: BigInt! "Current Token amount of silo withdrawals" withdrawnAmount: BigInt! "Current internal (farm) balance of the asset" farmAmount: BigInt! + "Season when the previous hourly snapshot was taken/updated" + lastHourlySnapshotSeason: Int + "Day of when the previous daily snapshot was taken/updated" + lastDailySnapshotDay: BigInt "Link to hourly snapshot data" hourlySnapshots: [SiloAssetHourlySnapshot!]! @derivedFrom(field: "siloAsset") "Link to daily snapshot data" @@ -265,7 +244,7 @@ type SiloAsset @entity { } type SiloAssetHourlySnapshot @entity { - "Silo Asset ID - Unix Timestamp" + "Silo Asset ID - Season" id: ID! "Season for the snapshot" season: Int! @@ -279,14 +258,12 @@ type SiloAssetHourlySnapshot @entity { withdrawnAmount: BigInt! "Point in time current internal (farm) balance of the asset" farmAmount: BigInt! - "Point in time delta BDV of deposits" + deltaDepositedBDV: BigInt! - "Point in time delta Token amount of deposits" deltaDepositedAmount: BigInt! - "Point in time delta Token amount of silo withdrawals" deltaWithdrawnAmount: BigInt! - "Point in time delta internal (farm) balance of the asset" deltaFarmAmount: BigInt! + "Timestamp of initial snapshot creation" createdAt: BigInt! "Timestamp of last entity update" @@ -294,7 +271,7 @@ type SiloAssetHourlySnapshot @entity { } type SiloAssetDailySnapshot @entity { - "Silo Asset ID - Unix Timestamp" + "Silo Asset ID - Day" id: ID! "Last season for the snapshot" season: Int! @@ -308,14 +285,12 @@ type SiloAssetDailySnapshot @entity { withdrawnAmount: BigInt! "Point in time current internal (farm) balance of the asset" farmAmount: BigInt! - "Point in time delta BDV of deposits" + deltaDepositedBDV: BigInt! - "Point in time delta Token amount of deposits" deltaDepositedAmount: BigInt! - "Point in time delta Token amount of silo withdrawals" deltaWithdrawnAmount: BigInt! - "Point in time delta internal (farm) balance of the asset" deltaFarmAmount: BigInt! + "Timestamp of initial snapshot creation" createdAt: BigInt! "Timestamp of last entity update" @@ -381,10 +356,18 @@ type WhitelistTokenSetting @entity { optimalPercentDepositedBdv: BigInt "Last timestamp entity was updated" updatedAt: BigInt! + "Season when the previous hourly snapshot was taken/updated" + lastHourlySnapshotSeason: Int + "Day of when the previous daily snapshot was taken/updated" + lastDailySnapshotDay: BigInt "Link to hourly snapshot data" hourlySnapshots: [WhitelistTokenHourlySnapshot!]! @derivedFrom(field: "token") "Link to daily snapshot data" dailySnapshots: [WhitelistTokenDailySnapshot!]! @derivedFrom(field: "token") + + # Unique to the main entity, not on dailySnapshots + "Number of decimals in this token" + decimals: Int! } type WhitelistTokenHourlySnapshot @entity { @@ -410,15 +393,29 @@ type WhitelistTokenHourlySnapshot @entity { gaugePoints: BigInt "[Seed Gauge] The current optimal targeted distribution of BDV for this whitelisted asset" optimalPercentDepositedBdv: BigInt + + deltaStalkEarnedPerSeason: BigInt! + deltaStalkIssuedPerBdv: BigInt! + deltaMilestoneSeason: Int! + deltaGaugePoints: BigInt + deltaOptimalPercentDepositedBdv: BigInt + "Timestamp of initial snapshot creation" createdAt: BigInt! "Timestamp of last entity update" updatedAt: BigInt! + + # These are unique to the snapshot, and not on the main entity + "Point in time hourly bdv" + bdv: BigInt + deltaBdv: BigInt } type WhitelistTokenDailySnapshot @entity { - "Token address - Unix Timestamp" + "Token address - Day" id: ID! + "The season for this snapshot" + season: Int! "WhitelistTokenSetting associated with this snapshot" token: WhitelistTokenSetting! "Encoded BDV selector" @@ -437,14 +434,26 @@ type WhitelistTokenDailySnapshot @entity { gaugePoints: BigInt "[Seed Gauge] The current optimal targeted distribution of BDV for this whitelisted asset" optimalPercentDepositedBdv: BigInt + + deltaStalkEarnedPerSeason: BigInt! + deltaStalkIssuedPerBdv: BigInt! + deltaMilestoneSeason: Int! + deltaGaugePoints: BigInt + deltaOptimalPercentDepositedBdv: BigInt + "Timestamp of initial snapshot creation" createdAt: BigInt! "Timestamp of last entity update" updatedAt: BigInt! + + # These are unique to the snapshot, and not on the main entity + "Point in time daily bdv" + bdv: BigInt + deltaBdv: BigInt } type Field @entity { - " Contract address for this field or farmer " + "Contract address for this field or farmer" id: ID! "Contract address of beanstalk" beanstalk: Beanstalk! @@ -476,6 +485,10 @@ type Field @entity { podIndex: BigInt! "Current pod rate: Total unharvestable pods / bean supply" podRate: BigDecimal! + "Season when the previous hourly snapshot was taken/updated" + lastHourlySnapshotSeason: Int + "Day of when the previous daily snapshot was taken/updated" + lastDailySnapshotDay: BigInt "Link to hourly snapshot data" hourlySnapshots: [FieldHourlySnapshot!]! @derivedFrom(field: "field") "Link to daily snapshot data" @@ -483,7 +496,7 @@ type Field @entity { } type FieldHourlySnapshot @entity { - "Field ID - Unix Timestamp" + "Field ID - Season" id: ID! "Field associated with this snapshot" field: Field! @@ -507,40 +520,44 @@ type FieldHourlySnapshot @entity { harvestedPods: BigInt! "Point in time amount of soil remaining" soil: BigInt! + "Point in time amount of soil issued" + issuedSoil: BigInt! "Point in time pod index" podIndex: BigInt! "Point in time pod rate: Total unharvestable pods / bean supply" podRate: BigDecimal! - "Point in time delta number of unique sowers" + + deltaTemperature: Int! + deltaRealRateOfReturn: BigDecimal! deltaNumberOfSowers: Int! - "Point in time delta number of sows" deltaNumberOfSows: Int! - "Point in time delta total of sown beans" deltaSownBeans: BigInt! - "Point in time delta non-harvestable pods" deltaUnharvestablePods: BigInt! - "Point in time delta harvestable pods" deltaHarvestablePods: BigInt! - "Point in time delta harvested pods" deltaHarvestedPods: BigInt! - "Point in time amount of soil issued" - issuedSoil: BigInt! - "Number of blocks between sunrise and soil being sold out" - blocksToSoldOutSoil: BigInt! - "Bool flag if soil sold out for the season" - soilSoldOut: Boolean! - "The caseId used in the seasonal adjustment of temperature" - caseId: BigInt! - "Creation Block Number" - blockNumber: BigInt! + deltaSoil: BigInt! + deltaIssuedSoil: BigInt! + deltaPodIndex: BigInt! + deltaPodRate: BigDecimal! + "Timestamp of initial snapshot creation" createdAt: BigInt! "Timestamp of last entity update" updatedAt: BigInt! + + # These are unique to hourly snapshot + "The caseId used in the seasonal adjustment of temperature" + caseId: BigInt + "Block that started this season/at time of snapshot creation" + seasonBlock: BigInt! + "Number of blocks between sunrise and soil being sold out" + blocksToSoldOutSoil: BigInt + "Bool flag if soil sold out for the season" + soilSoldOut: Boolean! } type FieldDailySnapshot @entity { - "Field ID - Unix Timestamp" + "Field ID - Day" id: ID! "Field associated with this snapshot" field: Field! @@ -564,24 +581,26 @@ type FieldDailySnapshot @entity { harvestedPods: BigInt! "Point in time amount of soil remaining" soil: BigInt! + "Point in time amount of soil issued" + issuedSoil: BigInt! "Point in time pod index" podIndex: BigInt! "Point in time pod rate: Total unharvestable pods / bean supply" podRate: BigDecimal! - "Point in time delta number of unique sowers" + + deltaTemperature: Int! + deltaRealRateOfReturn: BigDecimal! deltaNumberOfSowers: Int! - "Point in time delta number of sows" deltaNumberOfSows: Int! - "Point in time delta total of sown beans" deltaSownBeans: BigInt! - "Point in time delta non-harvestable pods" deltaUnharvestablePods: BigInt! - "Point in time delta harvestable pods" deltaHarvestablePods: BigInt! - "Point in time delta harvested pods" deltaHarvestedPods: BigInt! - "Point in time amount of soil issued" - issuedSoil: BigInt! + deltaSoil: BigInt! + deltaIssuedSoil: BigInt! + deltaPodIndex: BigInt! + deltaPodRate: BigDecimal! + "Timestamp of initial snapshot creation" createdAt: BigInt! "Timestamp of last entity update" @@ -606,35 +625,31 @@ type Farmer @entity { type SiloDeposit @entity { """ - Pre Silo V3: - Account - Token Address - Season - - Post Silo-V3: - Account - Token Address - Stem + Account - Token Address - Deposit Version - (Season|Stem) """ id: ID! "Farmer address" farmer: Farmer! "Token Address" token: String! + "Version of deposit. Options are season, v3, v3.1. `season` type includes those deposits which are calculated according to their silo v1 deposits pre-explout" + depositVersion: String! "Season of deposit" - season: Int! - "Stem of deposit - Introduced in Silo V3" + season: Int + "Stem of deposit" stem: BigInt - "Current token amount deposited" - amount: BigInt! - "Original token amount deposited" + "Silo v3.1 equivalent stem. This value will always be assigned regardless of the deposit version." + stemV31: BigInt! + "Token amount deposited" depositedAmount: BigInt! - "Token amount withdrawn" - withdrawnAmount: BigInt! - "Current BDV of the deposit" - bdv: BigInt! "Original deposited BDV" depositedBDV: BigInt! - "Withdrawn BDV" - withdrawnBDV: BigInt! - "Transaction hashes for multiple deposits in one season" + "Transaction hashes pertaining to this deposit" hashes: [String!]! + "Block of first deposit" + createdBlock: BigInt! + "Block when last updated" + updatedBlock: BigInt! "Timestamp of first deposit" createdAt: BigInt! "Timestamp when last updated" @@ -656,8 +671,6 @@ type SiloWithdraw @entity { claimed: Boolean! "Token amount withdrawn" amount: BigInt! - "Transaction hash of withdrawal" - hashes: [String!]! "Timestamp created" createdAt: BigInt! } @@ -700,7 +713,7 @@ type Plot @entity { } type PodMarketplace @entity { - " Contract address of beanstalk " + "Contract address of beanstalk" id: ID! "Current season of the marketplace" season: Int! @@ -738,6 +751,10 @@ type PodMarketplace @entity { podVolume: BigInt! "Cumulative bean volume between listings and orders" beanVolume: BigInt! + "Season when the previous hourly snapshot was taken/updated" + lastHourlySnapshotSeason: Int + "Day of when the previous daily snapshot was taken/updated" + lastDailySnapshotDay: BigInt "Link to hourly snapshot data" hourlySnapshots: [PodMarketplaceHourlySnapshot!]! @derivedFrom(field: "podMarketplace") "Link to daily snapshot data" @@ -745,7 +762,7 @@ type PodMarketplace @entity { } type PodMarketplaceHourlySnapshot @entity { - "Marketplace ID - Unix Timestamp" + "Marketplace ID - Season" id: ID! "Point in time latest season" season: Int! @@ -775,30 +792,20 @@ type PodMarketplaceHourlySnapshot @entity { podVolume: BigInt! "Point in time current cumulative bean volume between listings and orders" beanVolume: BigInt! - "Point in time current delta pods listed for sale" + deltaListedPods: BigInt! - "Point in time current delta of total pods listed" deltaAvailableListedPods: BigInt! - "Point in time current delta pod listings filled" deltaFilledListedPods: BigInt! - "Point in time current delta pod listings that expired" deltaExpiredListedPods: BigInt! - "Point in time current delta pod listings that were cancelled" deltaCancelledListedPods: BigInt! - "Point in time current delta ordered beans in pod orders created" deltaOrderBeans: BigInt! - "Point in time current delta available ordered beans in pod orders" deltaAvailableOrderBeans: BigInt! - "Point in time current delta filled ordered beans in pod orders" deltaFilledOrderBeans: BigInt! - "Point in time current delta pod orders filled" deltaFilledOrderedPods: BigInt! - "Point in time current delta cancelled ordered beans in pod orders" deltaCancelledOrderBeans: BigInt! - "Point in time current delta pod volume between listings and orders" deltaPodVolume: BigInt! - "Point in time current delta bean volume between listings and orders" deltaBeanVolume: BigInt! + "Timestamp of initial snapshot creation" createdAt: BigInt! "Timestamp of last entity update" @@ -806,7 +813,7 @@ type PodMarketplaceHourlySnapshot @entity { } type PodMarketplaceDailySnapshot @entity { - "Marketplace ID - Unix Timestamp" + "Marketplace ID - Day" id: ID! "Point in time latest season" season: Int! @@ -836,30 +843,20 @@ type PodMarketplaceDailySnapshot @entity { podVolume: BigInt! "Point in time current cumulative bean volume between listings and orders" beanVolume: BigInt! - "Point in time current delta pods listed for sale" + deltaListedPods: BigInt! - "Point in time current delta of total pods listed" deltaAvailableListedPods: BigInt! - "Point in time current delta pod listings filled" deltaFilledListedPods: BigInt! - "Point in time current delta pod listings that expired" deltaExpiredListedPods: BigInt! - "Point in time current delta pod listings that were cancelled" deltaCancelledListedPods: BigInt! - "Point in time current delta ordered beans in pod orders created" deltaOrderBeans: BigInt! - "Point in time current delta available ordered beans in pod orders" deltaAvailableOrderBeans: BigInt! - "Point in time current delta filled ordered beans in pod orders" deltaFilledOrderBeans: BigInt! - "Point in time current delta pod orders filled" deltaFilledOrderedPods: BigInt! - "Point in time current delta cancelled ordered beans in pod orders" deltaCancelledOrderBeans: BigInt! - "Point in time current delta pod volume between listings and orders" deltaPodVolume: BigInt! - "Point in time current delta bean volume between listings and orders" deltaBeanVolume: BigInt! + "Timestamp of initial snapshot creation" createdAt: BigInt! "Timestamp of last entity update" @@ -1174,6 +1171,8 @@ type PodFill @entity { type Fertilizer @entity { "Token address for fert" id: ID! + "Address of parent beanstalk" + beanstalk: String! "Total overall suppy of fert tokens" supply: BigInt! tokens: [FertilizerToken!]! @derivedFrom(field: "fertilizer") @@ -1231,328 +1230,66 @@ type FertilizerYield @entity { ##### Event-Level Data ##### ################################## -### We need to add these in - -""" -An event is any user action that occurs in a protocol. Generally, they are Ethereum events -emitted by a function in the smart contracts, stored in transaction receipts as event logs. -However, some user actions of interest are function calls that don't emit events. For example, -the deposit and withdraw functions in Yearn do not emit any events. In our subgraphs, we still -store them as events, although they are not technically Ethereum events emitted by smart -contracts. -""" -interface SiloEvent { - " { Event type }-{ Transaction hash }-{ Log index } " - id: ID! - " Transaction hash of the transaction that emitted this event " - hash: String! - " Event log index. For transactions that don't emit event, create arbitrary index starting from 0 " - logIndex: Int! - " The protocol this transaction belongs to " - protocol: Beanstalk! - " Block number of this event " - blockNumber: BigInt! - " Timestamp of this event " - createdAt: BigInt! -} - -interface FieldEvent { - " { Event type }-{ Transaction hash }-{ Log index } " - id: ID! - " Transaction hash of the transaction that emitted this event " - hash: String! - " Event log index. For transactions that don't emit event, create arbitrary index starting from 0 " - logIndex: Int! - " The protocol this transaction belongs to " - protocol: Beanstalk! - " Block number of this event " - blockNumber: BigInt! - " Timestamp of this event " - createdAt: BigInt! -} - interface MarketplaceEvent { - " { Event type }-{ Transaction hash }-{ Log index } " - id: ID! - " Transaction hash of the transaction that emitted this event " - hash: String! - " Event log index. For transactions that don't emit event, create arbitrary index starting from 0 " - logIndex: Int! - " The protocol this transaction belongs to " - protocol: Beanstalk! - " Block number of this event " - blockNumber: BigInt! - " Timestamp of this event " - createdAt: BigInt! -} - -type PodTransfer implements FieldEvent @entity(immutable: true) { - " podtransfer-{ Transaction hash }-{ Log index } " - id: ID! - " Transaction hash of the transaction that emitted this event " - hash: String! - " Event log index. For transactions that don't emit event, create arbitrary index starting from 0 " - logIndex: Int! - " The protocol this transaction belongs to " - protocol: Beanstalk! - " Address that received the pods " - toFarmer: String! - " Address that sent the pods " - fromFarmer: String! - " Index of the pods sent" - index: BigInt! - " Total pods being sent" - pods: BigInt! - " Block number of this event " - blockNumber: BigInt! - " Timestamp of this event " - createdAt: BigInt! -} - -type Harvest implements FieldEvent @entity(immutable: true) { - "harvest-{ Transaction hash }-{ Log index } " - id: ID! - " Transaction hash of the transaction that emitted this event " - hash: String! - " Event log index. For transactions that don't emit event, create arbitrary index starting from 0 " - logIndex: Int! - " The protocol this transaction belongs to " - protocol: Beanstalk! - " Address harvesting beans " - farmer: String! - " Plots being harvested " - plots: [BigInt!]! - " Total beans harvested " - beans: BigInt! - " Block number of this event " - blockNumber: BigInt! - " Timestamp of this event " - createdAt: BigInt! -} - -type Chop implements SiloEvent @entity(immutable: true) { - "chop-{ Transaction hash }-{ Log index }" - id: ID! - " Transaction hash of the transaction that emitted this event " - hash: String! - " Event log index. For transactions that don't emit event, create arbitrary index starting from 0 " - logIndex: Int! - " The protocol this transaction belongs to " - protocol: Beanstalk! - " Address chopping " - farmer: String! - " Unripe token being chopped " - unripe: String! - " Amount being chopped" - amount: BigInt! - " Underlying token " - underlying: String! - " Block number of this event " - blockNumber: BigInt! - " Timestamp of this event " - createdAt: BigInt! -} - -type Incentive implements SiloEvent @entity(immutable: true) { - "incentive-{ Transaction hash }-{ Log index }" - id: ID! - " Transaction hash of the transaction that emitted this event " - hash: String! - " Event log index. For transactions that don't emit event, create arbitrary index starting from 0 " - logIndex: Int! - " The protocol this transaction belongs to " - protocol: Beanstalk! - " Address incentivized " - caller: String! - " Amount minted as incentive" - amount: BigInt! - " Block number of this event " - blockNumber: BigInt! - " Timestamp of this event " - createdAt: BigInt! -} - -type Reward implements SiloEvent @entity(immutable: true) { - "reward-{ Transaction hash }-{ Log index }" - id: ID! - " Transaction hash of the transaction that emitted this event " - hash: String! - " Event log index. For transactions that don't emit event, create arbitrary index starting from 0 " - logIndex: Int! - " The protocol this transaction belongs to " - protocol: Beanstalk! - " Season of reward " - season: Int! - " Amount minted to pod line" - toField: BigInt! - " Amount minted to silo" - toSilo: BigInt! - " Amount minted to fertilizer" - toFertilizer: BigInt! - " Block number of this event " - blockNumber: BigInt! - " Timestamp of this event " - createdAt: BigInt! -} - -type MetapoolOracle implements SiloEvent @entity(immutable: true) { - "metapoolOracle-{ Transaction hash }-{ Log index }" - id: ID! - " Transaction hash of the transaction that emitted this event " - hash: String! - " Event log index. For transactions that don't emit event, create arbitrary index starting from 0 " - logIndex: Int! - " The protocol this transaction belongs to " - protocol: Beanstalk! - " Season of oracle " - season: Int! - " DeltaB for season" - deltaB: BigInt! - " Cumulative balance A" - balanceA: BigInt! - " Cumulative balance B" - balanceB: BigInt! - " Block number of this event " - blockNumber: BigInt! - " Timestamp of this event " - createdAt: BigInt! -} - -type WellOracle implements SiloEvent @entity(immutable: true) { - "wellOracle-{ Transaction hash }-{ Log index }" + "{ Event type }-{ Transaction hash }-{ Log index }" id: ID! - " Transaction hash of the transaction that emitted this event " + "Transaction hash of the transaction that emitted this event" hash: String! - " Event log index. For transactions that don't emit event, create arbitrary index starting from 0 " + "Event log index. For transactions that don't emit event, create arbitrary index starting from 0" logIndex: Int! - " The protocol this transaction belongs to " + "The protocol this transaction belongs to" protocol: Beanstalk! - " Season of oracle " - season: Int! - " DeltaB for season" - deltaB: BigInt! - " Time weighted cumulative reserves " - cumulativeReserves: Bytes! - " Block number of this event " + "Block number of this event" blockNumber: BigInt! - " Timestamp of this event " + "Timestamp of this event" createdAt: BigInt! } -type AddDeposit implements SiloEvent @entity(immutable: true) { - "addDeposit-{ Transaction hash }-{ Log index }" +type Chop @entity(immutable: true) { + "(chop|convert)-{ Transaction hash }-{ Log index }" id: ID! - " Transaction hash of the transaction that emitted this event " - hash: String! - " Event log index. For transactions that don't emit event, create arbitrary index starting from 0 " - logIndex: Int! - " The protocol this transaction belongs to " - protocol: Beanstalk! - " Account adding deposit" - account: String! - " Token added" - token: String! - " Season of deposit added " - season: Int! - " Stem of deposit added " - stem: BigInt - " Amount of token added " - amount: BigInt! - " BDV of the deposit " - bdv: BigInt! - " Block number of this event " - blockNumber: BigInt! - " Timestamp of this event " - createdAt: BigInt! -} - -type RemoveDeposit implements SiloEvent @entity(immutable: true) { - "removeDeposit-{ Transaction hash }-{ Log index }" - id: ID! - " Transaction hash of the transaction that emitted this event " - hash: String! - " Event log index. For transactions that don't emit event, create arbitrary index starting from 0 " - logIndex: Int! - " The protocol this transaction belongs to " - protocol: Beanstalk! - " Account removing deposit" - account: String! - " Token removed" - token: String! - " Season of deposit removed " - season: Int! - " Stem of deposit removed " - stem: BigInt - " Amount of token removed " - amount: BigInt! - " BDV of deposit removed " - bdv: BigInt - " Block number of this event " - blockNumber: BigInt! - " Timestamp of this event " - createdAt: BigInt! -} - -type StalkChange implements SiloEvent @entity(immutable: true) { - "stalkChange-{ Transaction hash }-{ Log index }" - id: ID! - " Transaction hash of the transaction that emitted this event " - hash: String! - " Event log index. For transactions that don't emit event, create arbitrary index starting from 0 " - logIndex: Int! - " The protocol this transaction belongs to " - protocol: Beanstalk! - " Account removing deposit" - account: String! - " Token removed" - delta: BigInt! - " Season when the change happened " - season: Int! - " Block number of this event " - blockNumber: BigInt! - " Timestamp of this event " - createdAt: BigInt! -} - -type SeedChange implements SiloEvent @entity(immutable: true) { - "seedChange-{ Transaction hash }-{ Log index }" - id: ID! - " Transaction hash of the transaction that emitted this event " + "Account address" + farmer: Farmer! + "The unripe token which was chopped" + unripeToken: UnripeToken! + "Unripe token amount which was chopped" + unripeAmount: BigInt! + "Bdv of the unripe tokens which were chopped" + unripeBdv: BigInt! + "The underlying ERC20 token received by `farmer` as a result of this chop" + underlyingToken: WhitelistTokenSetting! + "Amount of underlying tokens `farmer` received" + underlyingAmount: BigInt! + "Amount of bdv `farmer` received" + underlyingBdv: BigInt! + "The effective chop rate for this chop" + chopRate: BigDecimal! + "Transaction hash of the transaction that emitted this event" hash: String! - " Event log index. For transactions that don't emit event, create arbitrary index starting from 0 " - logIndex: Int! - " The protocol this transaction belongs to " - protocol: Beanstalk! - " Account removing deposit" - account: String! - " Token removed" - delta: BigInt! - " Season when the change happened " - season: Int! - " Block number of this event " + "The block number of this event" blockNumber: BigInt! - " Timestamp of this event " + "Timestamp of this chop" createdAt: BigInt! } type PodListingCreated implements MarketplaceEvent @entity(immutable: true) { "podListingCreated-{ Transaction hash }-{ Log index }" id: ID! - " Transaction hash of the transaction that emitted this event " + "Transaction hash of the transaction that emitted this event" hash: String! - " Event log index. For transactions that don't emit event, create arbitrary index starting from 0 " + "Event log index. For transactions that don't emit event, create arbitrary index starting from 0" logIndex: Int! - " The protocol this transaction belongs to " + "The protocol this transaction belongs to" protocol: Beanstalk! - " Historical ID for joins" + "Historical ID for joins" historyID: String! - " Account creating the listing" + "Account creating the listing" account: String! "Where these pods were in line when listed" placeInLine: BigInt! - " Index of the plot listed" + "Index of the plot listed" index: BigInt! - " Start value of the plot listed " + "Start value of the plot listed" start: BigInt! "Amount of pods listed" amount: BigInt! @@ -1568,22 +1305,22 @@ type PodListingCreated implements MarketplaceEvent @entity(immutable: true) { pricingFunction: Bytes "Pricing Type" pricingType: Int - " Block number of this event " + "Block number of this event" blockNumber: BigInt! - " Timestamp of this event " + "Timestamp of this event" createdAt: BigInt! } type PodListingFilled implements MarketplaceEvent @entity(immutable: true) { "podListingFilled-{ Transaction hash }-{ Log index }" id: ID! - " Transaction hash of the transaction that emitted this event " + "Transaction hash of the transaction that emitted this event" hash: String! - " Event log index. For transactions that don't emit event, create arbitrary index starting from 0 " + "Event log index. For transactions that don't emit event, create arbitrary index starting from 0" logIndex: Int! - " The protocol this transaction belongs to " + "The protocol this transaction belongs to" protocol: Beanstalk! - " Historical ID for joins" + "Historical ID for joins" historyID: String! "Account selling pods" fromFarmer: String! @@ -1599,49 +1336,49 @@ type PodListingFilled implements MarketplaceEvent @entity(immutable: true) { amount: BigInt! "Beans paid to fill the listing" costInBeans: BigInt - " Block number of this event " + "Block number of this event" blockNumber: BigInt! - " Timestamp of this event " + "Timestamp of this event" createdAt: BigInt! } type PodListingCancelled implements MarketplaceEvent @entity(immutable: true) { "seedChange-{ Transaction hash }-{ Log index }" id: ID! - " Transaction hash of the transaction that emitted this event " + "Transaction hash of the transaction that emitted this event" hash: String! - " Event log index. For transactions that don't emit event, create arbitrary index starting from 0 " + "Event log index. For transactions that don't emit event, create arbitrary index starting from 0" logIndex: Int! - " The protocol this transaction belongs to " + "The protocol this transaction belongs to" protocol: Beanstalk! - " Historical ID for joins" + "Historical ID for joins" historyID: String! - " Account cancelling listing" + "Account cancelling listing" account: String! "Where these pods were in line when cancelled" placeInLine: BigInt! - " Index of plot listing being cancelled" + "Index of plot listing being cancelled" index: BigInt! - " Block number of this event " + "Block number of this event" blockNumber: BigInt! - " Timestamp of this event " + "Timestamp of this event" createdAt: BigInt! } type PodOrderCreated implements MarketplaceEvent @entity(immutable: true) { "podOrderCreated-{ Transaction hash }-{ Log index }" id: ID! - " Transaction hash of the transaction that emitted this event " + "Transaction hash of the transaction that emitted this event" hash: String! - " Event log index. For transactions that don't emit event, create arbitrary index starting from 0 " + "Event log index. For transactions that don't emit event, create arbitrary index starting from 0" logIndex: Int! - " The protocol this transaction belongs to " + "The protocol this transaction belongs to" protocol: Beanstalk! - " Historical ID for joins" + "Historical ID for joins" historyID: String! - " Account creating the listing" + "Account creating the listing" account: String! - " ID of the pod order" + "ID of the pod order" orderId: String! """ The represented value emitted with this event changed with BIP-29 at block 15277986 @@ -1657,22 +1394,22 @@ type PodOrderCreated implements MarketplaceEvent @entity(immutable: true) { pricingFunction: Bytes "Pricing Type" pricingType: Int - " Block number of this event " + "Block number of this event" blockNumber: BigInt! - " Timestamp of this event " + "Timestamp of this event" createdAt: BigInt! } type PodOrderFilled implements MarketplaceEvent @entity(immutable: true) { "podOrderFilled-{ Transaction hash }-{ Log index }" id: ID! - " Transaction hash of the transaction that emitted this event " + "Transaction hash of the transaction that emitted this event" hash: String! - " Event log index. For transactions that don't emit event, create arbitrary index starting from 0 " + "Event log index. For transactions that don't emit event, create arbitrary index starting from 0" logIndex: Int! - " The protocol this transaction belongs to " + "The protocol this transaction belongs to" protocol: Beanstalk! - " Historical ID for joins" + "Historical ID for joins" historyID: String! "Account selling pods" fromFarmer: String! @@ -1688,72 +1425,30 @@ type PodOrderFilled implements MarketplaceEvent @entity(immutable: true) { amount: BigInt! "Beans paid to fill the order" costInBeans: BigInt - " Block number of this event " + "Block number of this event" blockNumber: BigInt! - " Timestamp of this event " + "Timestamp of this event" createdAt: BigInt! } type PodOrderCancelled implements MarketplaceEvent @entity(immutable: true) { "podOrderCancelled-{ Transaction hash }-{ Log index }" id: ID! - " Transaction hash of the transaction that emitted this event " + "Transaction hash of the transaction that emitted this event" hash: String! - " Event log index. For transactions that don't emit event, create arbitrary index starting from 0 " + "Event log index. For transactions that don't emit event, create arbitrary index starting from 0" logIndex: Int! - " The protocol this transaction belongs to " + "The protocol this transaction belongs to" protocol: Beanstalk! - " Historical ID for joins" + "Historical ID for joins" historyID: String! - " Account cancelling listing" + "Account cancelling listing" account: String! - " ID of order cancelled" + "ID of order cancelled" orderId: String! - " Block number of this event " + "Block number of this event" blockNumber: BigInt! - " Timestamp of this event " - createdAt: BigInt! -} - -type WhitelistToken implements SiloEvent @entity(immutable: true) { - "whitelistToken-{ Transaction hash }-{ Log index }" - id: ID! - " Transaction hash of the transaction that emitted this event " - hash: String! - " Event log index. For transactions that don't emit event, create arbitrary index starting from 0 " - logIndex: Int! - " The protocol this transaction belongs to " - protocol: Beanstalk! - "Token address whitelisted" - token: String! - "Stalk per BDV" - stalk: BigInt - "Seeds per BDV" - seeds: BigInt - "Stalk earned per season" - stalkPerSeason: BigInt - "Selector for token" - selector: String - " Block number of this event " - blockNumber: BigInt! - " Timestamp of this event " - createdAt: BigInt! -} - -type DewhitelistToken implements SiloEvent @entity(immutable: true) { - "dewhitelistToken-{ Transaction hash }-{ Log index }" - id: ID! - " Transaction hash of the transaction that emitted this event " - hash: String! - " Event log index. For transactions that don't emit event, create arbitrary index starting from 0 " - logIndex: Int! - " The protocol this transaction belongs to " - protocol: Beanstalk! - "Token address dewhitelisted" - token: String! - " Block number of this event " - blockNumber: BigInt! - " Timestamp of this event " + "Timestamp of this event" createdAt: BigInt! } @@ -1769,10 +1464,162 @@ type Germinating @entity { isFarmer: Boolean! "The season in which the germination started" season: Int! - "Germinating stalk. This only applies to farmer/Beanstalk address" + "Germinating stalk. This only applies to farmer/protocol address" stalk: BigInt! "Germinating tokens. This only applies to a Token address" tokenAmount: BigInt! "Germinating bdv. This only applies to a Token address" bdv: BigInt! } + +# This entity exists solely for resolving the issue in LibGerminate when deposits from multiple seasons +# complete their germination (the event emission itself has a bug) +type PrevFarmerGerminatingEvent @entity { + "Farmer address" + id: Bytes! + "The `block.number` of the `FarmerGerminatingStalkBalanceChanged` event" + eventBlock: BigInt! + "The `logIndex` of the `FarmerGerminatingStalkBalanceChanged` event" + logIndex: BigInt! + "The value for `deltaGerminatingStalk` from this previous `FarmerGerminatingStalkBalanceChanged` event." + deltaGerminatingStalk: BigInt! +} + +type UnripeToken @entity { + "Token Address" + id: Bytes! + "The ripe token underlying this unripe asset" + underlyingToken: WhitelistTokenSetting! + "The total amount of `underlyingToken` for this unripe token (getTotalUnderlying)" + totalUnderlying: BigInt! + "The amount of `underlyingToken` corresponding to one of this unripe token (getUnderlyingPerUnripeToken)" + amountUnderlyingOne: BigInt! + "The bdv of `amountUnderlyingOne` of `underlyingToken`. Assumed to not always be the same as bdv(id)" + bdvUnderlyingOne: BigInt! + "The amount of `underlyingToken` which would be received if one of this unripe token were to be chopped (getPenalty)" + choppableAmountOne: BigInt! + "The bdv that would be received if one of this unripe token were to be chopped" + choppableBdvOne: BigInt! + "The chop rate, in percent (getPercentPenalty)" + chopRate: BigDecimal! + "The amount recapitalized, in percent (getRecapFundedPercent)" + recapPercent: BigDecimal! + "The total amount of this unripe token which has been chopped" + totalChoppedAmount: BigInt! + "The total bdv of this unripe token which has been chopped" + totalChoppedBdv: BigInt! + "The total bdv of all `underlyingToken` that has been received from chopping" + totalChoppedBdvReceived: BigInt! + "Season when the previous hourly snapshot was taken/updated" + lastHourlySnapshotSeason: Int + "Day of when the previous daily snapshot was taken/updated" + lastDailySnapshotDay: BigInt + "Link to hourly snapshot data" + hourlySnapshots: [UnripeTokenHourlySnapshot!]! @derivedFrom(field: "unripeToken") + "Link to daily snapshot data" + dailySnapshots: [UnripeTokenDailySnapshot!]! @derivedFrom(field: "unripeToken") +} + +type UnripeTokenHourlySnapshot @entity { + "UnripeToken ID - Season" + id: ID! + "Season for the snapshot" + season: Int! + "Unripe token associated with this snapshot" + unripeToken: UnripeToken! + + "Point in time ripe token underlying this unripe asset" + underlyingToken: WhitelistTokenSetting! + "Point in time total amount of `underlyingToken` for this unripe token (getTotalUnderlying)" + totalUnderlying: BigInt! + "Point in time amount of `underlyingToken` corresponding to one of this unripe token (getUnderlyingPerUnripeToken)" + amountUnderlyingOne: BigInt! + "Point in time bdv of `amountUnderlyingOne` of `underlyingToken`. Assumed to not always be the same as bdv(id)" + bdvUnderlyingOne: BigInt! + "Point in time amount of `underlyingToken` which would be received if one of this unripe token were to be chopped (getPenalty)" + choppableAmountOne: BigInt! + "Point in time bdv that would be received if one of this unripe token were to be chopped" + choppableBdvOne: BigInt! + "Point in time chop rate, in percent (getPercentPenalty)" + chopRate: BigDecimal! + "Point in time amount recapitalized, in percent (getRecapFundedPercent)" + recapPercent: BigDecimal! + "Point in time total amount of this unripe token which has been chopped" + totalChoppedAmount: BigInt! + "Point in time total bdv of this unripe token which has been chopped" + totalChoppedBdv: BigInt! + "Point in time total bdv of all `underlyingToken` that has been received from chopping" + totalChoppedBdvReceived: BigInt! + + deltaUnderlyingToken: Boolean! + "Note that the contents of this field are nonsense when deltaUnderlyingToken = true" + deltaTotalUnderlying: BigInt! + "Note that the contents of this field are nonsense when deltaUnderlyingToken = true" + deltaAmountUnderlyingOne: BigInt! + deltaBdvUnderlyingOne: BigInt! + "Note that the contents of this field are nonsense when deltaUnderlyingToken = true" + deltaChoppableAmountOne: BigInt! + deltaChoppableBdvOne: BigInt! + deltaChopRate: BigDecimal! + deltaRecapPercent: BigDecimal! + deltaTotalChoppedAmount: BigInt! + deltaTotalChoppedBdv: BigInt! + deltaTotalChoppedBdvReceived: BigInt! + + "Timestamp of initial snapshot creation" + createdAt: BigInt! + "Timestamp of last entity update" + updatedAt: BigInt! +} + +type UnripeTokenDailySnapshot @entity { + "UnripeToken ID - Day" + id: ID! + "Last season for the snapshot" + season: Int! + "Unripe token associated with this snapshot" + unripeToken: UnripeToken! + + "Point in time ripe token underlying this unripe asset" + underlyingToken: WhitelistTokenSetting! + "Point in time total amount of `underlyingToken` for this unripe token (getTotalUnderlying)" + totalUnderlying: BigInt! + "Point in time amount of `underlyingToken` corresponding to one of this unripe token (getUnderlyingPerUnripeToken)" + amountUnderlyingOne: BigInt! + "Point in time bdv of `amountUnderlyingOne` of `underlyingToken`. Assumed to not always be the same as bdv(id)" + bdvUnderlyingOne: BigInt! + "Point in time amount of `underlyingToken` which would be received if one of this unripe token were to be chopped (getPenalty)" + choppableAmountOne: BigInt! + "Point in time bdv that would be received if one of this unripe token were to be chopped" + choppableBdvOne: BigInt! + "Point in time chop rate, in percent (getPercentPenalty)" + chopRate: BigDecimal! + "Point in time amount recapitalized, in percent (getRecapFundedPercent)" + recapPercent: BigDecimal! + "Point in time total amount of this unripe token which has been chopped" + totalChoppedAmount: BigInt! + "Point in time total bdv of this unripe token which has been chopped" + totalChoppedBdv: BigInt! + "Point in time total bdv of all `underlyingToken` that has been received from chopping" + totalChoppedBdvReceived: BigInt! + + deltaUnderlyingToken: Boolean! + "Note that the contents of this field are nonsense when deltaUnderlyingToken = true" + deltaTotalUnderlying: BigInt! + "Note that the contents of this field are nonsense when deltaUnderlyingToken = true" + deltaAmountUnderlyingOne: BigInt! + deltaBdvUnderlyingOne: BigInt! + "Note that the contents of this field are nonsense when deltaUnderlyingToken = true" + deltaChoppableAmountOne: BigInt! + deltaChoppableBdvOne: BigInt! + deltaChopRate: BigDecimal! + deltaRecapPercent: BigDecimal! + deltaTotalChoppedAmount: BigInt! + deltaTotalChoppedBdv: BigInt! + deltaTotalChoppedBdvReceived: BigInt! + + "Timestamp of initial snapshot creation" + createdAt: BigInt! + "Timestamp of last entity update" + updatedAt: BigInt! +} diff --git a/projects/subgraph-beanstalk/src/BeanHandler.ts b/projects/subgraph-beanstalk/src/BeanHandler.ts deleted file mode 100644 index 7c77aa0ed8..0000000000 --- a/projects/subgraph-beanstalk/src/BeanHandler.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { BigDecimal, BigInt, log } from "@graphprotocol/graph-ts"; -import { Transfer as LegacyTransfer } from "../generated/Beanstalk-ABIs/ERC20"; -import { Transfer } from "../generated/Beanstalk-ABIs/ERC20"; -import { ADDRESS_ZERO, BEANSTALK } from "../../subgraph-core/utils/Constants"; -import { loadSeason } from "./utils/Season"; -import { toDecimal, ZERO_BI } from "../../subgraph-core/utils/Decimals"; -import { loadBeanstalk } from "./utils/Beanstalk"; - -export function handleLegacyTransfer(event: LegacyTransfer): void { - if (event.block.number > BigInt.fromI32(14603000)) { - return; - } - - if (event.block.number > BigInt.fromI32(14602789)) { - let beanstalk = loadBeanstalk(BEANSTALK); - let season = loadSeason(BEANSTALK, BigInt.fromI32(beanstalk.lastSeason)); - season.deltaBeans = ZERO_BI; - season.beans = ZERO_BI; - season.price = BigDecimal.fromString("1.022"); - season.save(); - return; - } - - if (event.params.from == ADDRESS_ZERO || event.params.to == ADDRESS_ZERO) { - let beanstalk = loadBeanstalk(BEANSTALK); - let season = loadSeason(BEANSTALK, BigInt.fromI32(beanstalk.lastSeason)); - - log.debug("\nBeanSupply: ============\nBeanSupply: Starting Supply - {}\n", [season.beans.toString()]); - - if (event.params.from == ADDRESS_ZERO) { - season.deltaBeans = season.deltaBeans.plus(event.params.value); - season.beans = season.beans.plus(event.params.value); - log.debug("\nBeanSupply: Beans Minted - {}\nBeanSupply: Season - {}\nBeanSupply: Total Supply - {}\n", [ - event.params.value.toString(), - season.season.toString(), - season.beans.toString() - ]); - } else { - season.deltaBeans = season.deltaBeans.minus(event.params.value); - season.beans = season.beans.minus(event.params.value); - log.debug("\nBeanSupply: Beans Burned - {}\nBeanSupply: Season - {}\nBeanSupply: Total Supply - {}\n", [ - event.params.value.toString(), - season.season.toString(), - season.beans.toString() - ]); - } - season.save(); - } -} - -export function handleTransfer(event: Transfer): void { - if (event.params.from == ADDRESS_ZERO || event.params.to == ADDRESS_ZERO) { - let beanstalk = loadBeanstalk(BEANSTALK); - let season = loadSeason(BEANSTALK, BigInt.fromI32(beanstalk.lastSeason)); - - log.debug("\nBeanSupply: ============\nBeanSupply: Starting Supply - {}\n", [toDecimal(season.beans).toString()]); - - if (event.params.from == ADDRESS_ZERO) { - season.deltaBeans = season.deltaBeans.plus(event.params.value); - season.beans = season.beans.plus(event.params.value); - log.debug("\nBeanSupply: Beans Minted - {}\nBeanSupply: Season - {}\nBeanSupply: Total Supply - {}\n", [ - toDecimal(event.params.value).toString(), - season.season.toString(), - toDecimal(season.beans).toString() - ]); - } else { - season.deltaBeans = season.deltaBeans.minus(event.params.value); - season.beans = season.beans.minus(event.params.value); - log.debug("\nBeanSupply: Beans Burned - {}\nBeanSupply: Season - {}\nBeanSupply: Total Supply - {}\n", [ - toDecimal(event.params.value).toString(), - season.season.toString(), - toDecimal(season.beans).toString() - ]); - } - season.save(); - } -} diff --git a/projects/subgraph-beanstalk/src/DiamondHandler.ts b/projects/subgraph-beanstalk/src/DiamondHandler.ts deleted file mode 100644 index 338e18afac..0000000000 --- a/projects/subgraph-beanstalk/src/DiamondHandler.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { DiamondCut } from "../generated/Beanstalk-ABIs/PreReplant"; -import { loadBeanstalk } from "./utils/Beanstalk"; - -export function handleDiamondCut(event: DiamondCut): void { - let beanstalk = loadBeanstalk(event.address); - - beanstalk.lastUpgrade = event.block.timestamp; - beanstalk.save(); -} diff --git a/projects/subgraph-beanstalk/src/FarmHandler.ts b/projects/subgraph-beanstalk/src/FarmHandler.ts deleted file mode 100644 index 4ca457deb7..0000000000 --- a/projects/subgraph-beanstalk/src/FarmHandler.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Address, BigInt } from "@graphprotocol/graph-ts"; -import { InternalBalanceChanged } from "../generated/Beanstalk-ABIs/MarketV2"; -import { loadBeanstalk } from "./utils/Beanstalk"; -import { BEANSTALK } from "../../subgraph-core/utils/Constants"; -import { loadSiloAsset, loadSiloAssetDailySnapshot, loadSiloAssetHourlySnapshot } from "./utils/SiloEntities"; -import { loadFarmer } from "./utils/Farmer"; - -export function handleInternalBalanceChanged(event: InternalBalanceChanged): void { - let beanstalk = loadBeanstalk(BEANSTALK); - - loadFarmer(event.params.user); - - updateFarmTotals(BEANSTALK, event.params.token, beanstalk.lastSeason, event.params.delta, event.block.timestamp); - updateFarmTotals(event.params.user, event.params.token, beanstalk.lastSeason, event.params.delta, event.block.timestamp); -} - -function updateFarmTotals(account: Address, token: Address, season: i32, delta: BigInt, timestamp: BigInt): void { - let asset = loadSiloAsset(account, token); - let assetHourly = loadSiloAssetHourlySnapshot(account, token, season, timestamp); - let assetDaily = loadSiloAssetDailySnapshot(account, token, timestamp); - - asset.farmAmount = asset.farmAmount.plus(delta); - asset.save(); - - assetHourly.farmAmount = asset.farmAmount; - assetHourly.deltaFarmAmount = assetHourly.deltaFarmAmount.plus(delta); - assetHourly.updatedAt = timestamp; - assetHourly.save(); - - assetDaily.season = season; - assetDaily.farmAmount = asset.farmAmount; - assetDaily.deltaFarmAmount = assetDaily.deltaFarmAmount.plus(delta); - assetDaily.updatedAt = timestamp; - assetDaily.save(); -} diff --git a/projects/subgraph-beanstalk/src/FertilizerHandler.ts b/projects/subgraph-beanstalk/src/FertilizerHandler.ts deleted file mode 100644 index 3d442411e8..0000000000 --- a/projects/subgraph-beanstalk/src/FertilizerHandler.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Address, BigInt, log } from "@graphprotocol/graph-ts"; -import { TransferSingle, TransferBatch } from "../generated/Beanstalk-ABIs/Fertilizer"; -import { ADDRESS_ZERO, FERTILIZER } from "../../subgraph-core/utils/Constants"; -import { loadFertilizer, loadFertilizerBalance, loadFertilizerToken } from "./utils/Fertilizer"; -import { loadFarmer } from "./utils/Farmer"; - -export function handleTransferSingle(event: TransferSingle): void { - handleTransfer(event.params.from, event.params.to, event.params.id, event.params.value, event.block.number); -} - -export function handleTransferBatch(event: TransferBatch): void { - for (let i = 0; i < event.params.ids.length; i++) { - let id = event.params.ids[i]; - let amount = event.params.values[i]; - handleTransfer(event.params.from, event.params.to, id, amount, event.block.number); - } -} - -function handleTransfer(from: Address, to: Address, id: BigInt, amount: BigInt, blockNumber: BigInt): void { - let fertilizer = loadFertilizer(FERTILIZER); - let fertilizerToken = loadFertilizerToken(fertilizer, id, blockNumber); - log.debug("\nFert Transfer: id – {}\n", [id.toString()]); - if (from != ADDRESS_ZERO) { - let fromFarmer = loadFarmer(from); - let fromFertilizerBalance = loadFertilizerBalance(fertilizerToken, fromFarmer); - fromFertilizerBalance.amount = fromFertilizerBalance.amount.minus(amount); - fromFertilizerBalance.save(); - } else { - fertilizerToken.supply = fertilizerToken.supply.plus(amount); - fertilizer.supply = fertilizer.supply.plus(amount); - fertilizer.save(); - fertilizerToken.save(); - } - - let toFarmer = loadFarmer(to); - let toFertilizerBalance = loadFertilizerBalance(fertilizerToken, toFarmer); - toFertilizerBalance.amount = toFertilizerBalance.amount.plus(amount); - toFertilizerBalance.save(); -} diff --git a/projects/subgraph-beanstalk/src/FieldHandler.ts b/projects/subgraph-beanstalk/src/FieldHandler.ts deleted file mode 100644 index 4f024e9d66..0000000000 --- a/projects/subgraph-beanstalk/src/FieldHandler.ts +++ /dev/null @@ -1,677 +0,0 @@ -import { Address, BigInt, log } from "@graphprotocol/graph-ts"; -import { - FundFundraiser, - Harvest, - PlotTransfer, - Sow, - SupplyDecrease, - SupplyIncrease, - SupplyNeutral, - WeatherChange -} from "../generated/Beanstalk-ABIs/PreReplant"; -import { Harvest as HarvestEntity } from "../generated/schema"; -import { BEANSTALK, BEANSTALK_FARMS } from "../../subgraph-core/utils/Constants"; -import { BI_10, ZERO_BI } from "../../subgraph-core/utils/Decimals"; -import { loadFarmer } from "./utils/Farmer"; -import { handleRateChange, loadField, loadFieldDaily, loadFieldHourly } from "./utils/Field"; -import { loadPlot } from "./utils/Plot"; -import { savePodTransfer } from "./utils/PodTransfer"; -import { getCurrentSeason, getHarvestableIndex, loadSeason } from "./utils/Season"; -import { loadBeanstalk } from "./utils/Beanstalk"; -import { expirePodListingIfExists } from "./utils/PodListing"; - -export function handleWeatherChange(event: WeatherChange): void { - handleRateChange(event.address, event.block, event.params.season, event.params.caseId, event.params.change); -} - -export function handleSow(event: Sow): void { - let beanstalk = loadBeanstalk(event.address); - - let sownBeans = event.params.beans; - - if (event.params.account == BEANSTALK_FARMS) { - let startingField = loadField(event.address); - sownBeans = startingField.soil; - } - - // Update Beanstalk Totals - updateFieldTotals( - event.address, - beanstalk.lastSeason, - ZERO_BI, - sownBeans, - event.params.pods, - ZERO_BI, - ZERO_BI, - ZERO_BI, - event.block.timestamp, - event.block.number - ); - - // Update Farmer Totals - updateFieldTotals( - event.params.account, - beanstalk.lastSeason, - ZERO_BI, - sownBeans, - event.params.pods, - ZERO_BI, - ZERO_BI, - ZERO_BI, - event.block.timestamp, - event.block.number - ); - - let field = loadField(event.address); - loadFarmer(event.params.account); - let plot = loadPlot(event.address, event.params.index); - - let newIndexes = field.plotIndexes; - newIndexes.push(plot.index); - field.plotIndexes = newIndexes; - field.save(); - - plot.farmer = event.params.account.toHexString(); - plot.source = "SOW"; - plot.sourceHash = event.transaction.hash.toHexString(); - plot.season = field.season; - plot.creationHash = event.transaction.hash.toHexString(); - plot.createdAt = event.block.timestamp; - plot.updatedAt = event.block.timestamp; - plot.updatedAtBlock = event.block.number; - plot.pods = event.params.pods; - plot.beansPerPod = event.params.beans.times(BI_10.pow(6)).div(plot.pods); - plot.save(); - - // Increment protocol amounts - incrementSows(event.address, field.season, event.block.timestamp); - - // Increment farmer amounts - incrementSows(event.params.account, field.season, event.block.timestamp); -} - -export function handleHarvest(event: Harvest): void { - let beanstalk = loadBeanstalk(event.address); - let season = loadSeason(event.address, BigInt.fromI32(beanstalk.lastSeason)); - - // Harvest function is only called with a list of plots - - // Update plots and field totals - - let remainingIndex = ZERO_BI; - - for (let i = 0; i < event.params.plots.length; i++) { - // Plot should exist - let plot = loadPlot(event.address, event.params.plots[i]); - - expirePodListingIfExists(event.address, plot.farmer, plot.index, event.block.timestamp); - - let harvestablePods = season.harvestableIndex.minus(plot.index); - - if (harvestablePods >= plot.pods) { - // Plot fully harvests - updateFieldTotals( - event.address, - beanstalk.lastSeason, - ZERO_BI, - ZERO_BI, - ZERO_BI, - ZERO_BI, - ZERO_BI, - plot.pods, - event.block.timestamp, - event.block.number - ); - updateFieldTotals( - event.params.account, - beanstalk.lastSeason, - ZERO_BI, - ZERO_BI, - ZERO_BI, - ZERO_BI, - ZERO_BI, - plot.pods, - event.block.timestamp, - event.block.number - ); - - plot.harvestedPods = plot.pods; - plot.fullyHarvested = true; - plot.save(); - } else { - // Plot partially harvests - - updateFieldTotals( - event.address, - beanstalk.lastSeason, - ZERO_BI, - ZERO_BI, - ZERO_BI, - ZERO_BI, - ZERO_BI, - harvestablePods, - event.block.timestamp, - event.block.number - ); - updateFieldTotals( - event.params.account, - beanstalk.lastSeason, - ZERO_BI, - ZERO_BI, - ZERO_BI, - ZERO_BI, - ZERO_BI, - harvestablePods, - event.block.timestamp, - event.block.number - ); - - remainingIndex = plot.index.plus(harvestablePods); - let remainingPods = plot.pods.minus(harvestablePods); - - let remainingPlot = loadPlot(event.address, remainingIndex); - remainingPlot.farmer = plot.farmer; - remainingPlot.source = plot.source; - remainingPlot.sourceHash = plot.sourceHash; - remainingPlot.season = beanstalk.lastSeason; - remainingPlot.creationHash = event.transaction.hash.toHexString(); - remainingPlot.createdAt = event.block.timestamp; - remainingPlot.updatedAt = event.block.timestamp; - remainingPlot.updatedAtBlock = event.block.number; - remainingPlot.index = remainingIndex; - remainingPlot.pods = remainingPods; - remainingPlot.beansPerPod = plot.beansPerPod; - remainingPlot.save(); - - plot.harvestedPods = harvestablePods; - plot.pods = harvestablePods; - plot.fullyHarvested = true; - plot.save(); - } - } - - // Remove the harvested plot IDs from the field list - let field = loadField(event.address); - let newIndexes = field.plotIndexes; - for (let i = 0; i < event.params.plots.length; i++) { - let plotIndex = newIndexes.indexOf(event.params.plots[i]); - newIndexes.splice(plotIndex, 1); - newIndexes.sort(); - } - if (remainingIndex !== ZERO_BI) { - newIndexes.push(remainingIndex); - } - field.plotIndexes = newIndexes; - field.save(); - - // Save the low level details for the event. - let harvest = new HarvestEntity("harvest-" + event.transaction.hash.toHexString() + "-" + event.transactionLogIndex.toString()); - harvest.hash = event.transaction.hash.toHexString(); - harvest.logIndex = event.transactionLogIndex.toI32(); - harvest.protocol = event.address.toHexString(); - harvest.farmer = event.params.account.toHexString(); - harvest.plots = event.params.plots; - harvest.beans = event.params.beans; - harvest.blockNumber = event.block.number; - harvest.createdAt = event.block.timestamp; - harvest.save(); -} - -export function handlePlotTransfer(event: PlotTransfer): void { - const currentSeason = getCurrentSeason(event.address); - const currentHarvestable = getHarvestableIndex(event.address); - - // Ensure both farmer entites exist - loadFarmer(event.params.from); - loadFarmer(event.params.to); - - // Update farmer field data - updateFieldTotals( - event.params.from, - currentSeason, - ZERO_BI, - ZERO_BI, - ZERO_BI, - ZERO_BI.minus(event.params.pods), - ZERO_BI, - ZERO_BI, - event.block.timestamp, - event.block.number - ); - updateFieldTotals( - event.params.to, - currentSeason, - ZERO_BI, - ZERO_BI, - ZERO_BI, - event.params.pods, - ZERO_BI, - ZERO_BI, - event.block.timestamp, - event.block.number - ); - - let field = loadField(BEANSTALK); - let sortedPlots = field.plotIndexes.sort(); - - let sourceIndex = ZERO_BI; - - for (let i = 0; i < sortedPlots.length; i++) { - // Handle only single comparison for first value of array - if (i == 0) { - if (sortedPlots[i] == event.params.id) { - sourceIndex = sortedPlots[i]; - break; - } else { - continue; - } - } - // Transferred plot matches existing. Start value of zero. - if (sortedPlots[i] == event.params.id) { - sourceIndex = sortedPlots[i]; - break; - } - // Transferred plot is in the middle of existing plot. Non-zero start value. - if (sortedPlots[i - 1] < event.params.id && event.params.id < sortedPlots[i]) { - sourceIndex = sortedPlots[i - 1]; - } - } - - let sourcePlot = loadPlot(event.address, sourceIndex); - let sourceEndIndex = sourceIndex.plus(sourcePlot.pods); - let transferEndIndex = event.params.id.plus(event.params.pods); - - // Determines how many of the pods being transferred are harvestable - const calcHarvestable = (index: BigInt, pods: BigInt, harvestableIndex: BigInt): BigInt => { - let harvestable = harvestableIndex.minus(index); - if (harvestable < ZERO_BI) { - return ZERO_BI; - } else { - return harvestable >= pods ? pods : harvestable; - } - }; - - let transferredHarvestable = calcHarvestable(event.params.id, event.params.pods, currentHarvestable); - - // log.debug("\nPodTransfer: ===================\n", []); - // log.debug("\nPodTransfer: Transfer Season - {}\n", [field.season.toString()]); - // log.debug("\nPodTransfer: Transfer Index - {}\n", [event.params.id.toString()]); - // log.debug("\nPodTransfer: Transfer Pods - {}\n", [event.params.pods.toString()]); - // log.debug("\nPodTransfer: Transfer Harvestable Pods - {}\n", [transferredHarvestable.toString()]); - // log.debug("\nPodTransfer: Transfer Ending Index - {}\n", [event.params.id.plus(event.params.pods).toString()]); - // log.debug("\nPodTransfer: Source Index - {}\n", [sourceIndex.toString()]); - // log.debug("\nPodTransfer: Source Ending Index - {}\n", [sourceIndex.plus(sourcePlot.pods).toString()]); - // log.debug("\nPodTransfer: Source Harvestable Pods - {}\n", [sourcePlot.harvestablePods.toString()]); - // log.debug("\nPodTransfer: Starting Source Pods - {}\n", [sourcePlot.pods.toString()]); - - // Actually transfer the plots - if (sourcePlot.pods == event.params.pods) { - // Sending full plot - const isMarket = sourcePlot.source == "MARKET" && sourcePlot.sourceHash == event.transaction.hash.toHexString(); - if (!isMarket) { - sourcePlot.source = "TRANSFER"; - sourcePlot.sourceHash = event.transaction.hash.toHexString(); - sourcePlot.beansPerPod = sourcePlot.beansPerPod; - } - sourcePlot.farmer = event.params.to.toHexString(); - sourcePlot.updatedAt = event.block.timestamp; - sourcePlot.updatedAtBlock = event.block.number; - sourcePlot.save(); - // log.debug("\nPodTransfer: Sending full plot\n", []); - } else if (sourceIndex == event.params.id) { - // We are only needing to split this plot once to send - // Start value of zero - let remainderIndex = sourceIndex.plus(event.params.pods); - let remainderPlot = loadPlot(event.address, remainderIndex); - sortedPlots.push(remainderIndex); - - const isMarket = sourcePlot.source == "MARKET" && sourcePlot.sourceHash == event.transaction.hash.toHexString(); - if (!isMarket) { - // When sending the start of the plot via market, these cannot be derived from sourcePlot. - remainderPlot.source = sourcePlot.source; - remainderPlot.sourceHash = sourcePlot.sourceHash; - remainderPlot.beansPerPod = sourcePlot.beansPerPod; - - sourcePlot.source = "TRANSFER"; - sourcePlot.sourceHash = event.transaction.hash.toHexString(); - sourcePlot.beansPerPod = sourcePlot.beansPerPod; - } - sourcePlot.farmer = event.params.to.toHexString(); - sourcePlot.updatedAt = event.block.timestamp; - sourcePlot.updatedAtBlock = event.block.number; - sourcePlot.pods = event.params.pods; - sourcePlot.harvestablePods = calcHarvestable(sourcePlot.index, sourcePlot.pods, currentHarvestable); - sourcePlot.save(); - - remainderPlot.farmer = event.params.from.toHexString(); - remainderPlot.season = field.season; - remainderPlot.creationHash = event.transaction.hash.toHexString(); - remainderPlot.createdAt = event.block.timestamp; - remainderPlot.updatedAt = event.block.timestamp; - remainderPlot.updatedAtBlock = event.block.number; - remainderPlot.index = remainderIndex; - remainderPlot.pods = sourceEndIndex.minus(transferEndIndex); - remainderPlot.harvestablePods = calcHarvestable(remainderPlot.index, remainderPlot.pods, currentHarvestable); - remainderPlot.save(); - - // log.debug("\nPodTransfer: sourceIndex == transferIndex\n", []); - // log.debug("\nPodTransfer: Remainder Index - {}\n", [remainderIndex.toString()]); - // log.debug("\nPodTransfer: Source Pods - {}\n", [sourcePlot.pods.toString()]); - // log.debug("\nPodTransfer: Remainder Pods - {}\n", [remainderPlot.pods.toString()]); - } else if (sourceEndIndex == transferEndIndex) { - // We are only needing to split this plot once to send - // Non-zero start value. Sending to end of plot - let toPlot = loadPlot(event.address, event.params.id); - sortedPlots.push(event.params.id); - - sourcePlot.updatedAt = event.block.timestamp; - sourcePlot.updatedAtBlock = event.block.number; - sourcePlot.pods = sourcePlot.pods.minus(event.params.pods); - sourcePlot.harvestablePods = calcHarvestable(sourcePlot.index, sourcePlot.pods, currentHarvestable); - sourcePlot.save(); - - const isMarket = toPlot.source == "MARKET" && toPlot.sourceHash == event.transaction.hash.toHexString(); - if (!isMarket) { - toPlot.source = "TRANSFER"; - toPlot.sourceHash = event.transaction.hash.toHexString(); - toPlot.beansPerPod = sourcePlot.beansPerPod; - } - toPlot.farmer = event.params.to.toHexString(); - toPlot.season = field.season; - toPlot.creationHash = event.transaction.hash.toHexString(); - toPlot.createdAt = event.block.timestamp; - toPlot.updatedAt = event.block.timestamp; - toPlot.updatedAtBlock = event.block.number; - toPlot.index = event.params.id; - toPlot.pods = event.params.pods; - toPlot.harvestablePods = calcHarvestable(toPlot.index, toPlot.pods, currentHarvestable); - toPlot.save(); - - // log.debug("\nPodTransfer: sourceEndIndex == transferEndIndex\n", []); - // log.debug("\nPodTransfer: Updated Source Pods - {}\n", [sourcePlot.pods.toString()]); - } else { - // We have to split this plot twice to send - let remainderIndex = event.params.id.plus(event.params.pods); - let toPlot = loadPlot(event.address, event.params.id); - let remainderPlot = loadPlot(event.address, remainderIndex); - - sortedPlots.push(event.params.id); - sortedPlots.push(remainderIndex); - - sourcePlot.updatedAt = event.block.timestamp; - sourcePlot.updatedAtBlock = event.block.number; - sourcePlot.pods = event.params.id.minus(sourcePlot.index); - sourcePlot.harvestablePods = calcHarvestable(sourcePlot.index, sourcePlot.pods, currentHarvestable); - sourcePlot.save(); - - const isMarket = toPlot.source == "MARKET" && toPlot.sourceHash == event.transaction.hash.toHexString(); - if (!isMarket) { - toPlot.source = "TRANSFER"; - toPlot.sourceHash = event.transaction.hash.toHexString(); - toPlot.beansPerPod = sourcePlot.beansPerPod; - } - toPlot.farmer = event.params.to.toHexString(); - toPlot.season = field.season; - toPlot.creationHash = event.transaction.hash.toHexString(); - toPlot.createdAt = event.block.timestamp; - toPlot.updatedAt = event.block.timestamp; - toPlot.updatedAtBlock = event.block.number; - toPlot.index = event.params.id; - toPlot.pods = event.params.pods; - toPlot.harvestablePods = calcHarvestable(toPlot.index, toPlot.pods, currentHarvestable); - toPlot.save(); - - remainderPlot.farmer = event.params.from.toHexString(); - remainderPlot.source = sourcePlot.source; - remainderPlot.sourceHash = sourcePlot.sourceHash; - remainderPlot.season = field.season; - remainderPlot.creationHash = event.transaction.hash.toHexString(); - remainderPlot.createdAt = event.block.timestamp; - remainderPlot.updatedAt = event.block.timestamp; - remainderPlot.updatedAtBlock = event.block.number; - remainderPlot.index = remainderIndex; - remainderPlot.pods = sourceEndIndex.minus(transferEndIndex); - remainderPlot.harvestablePods = calcHarvestable(remainderPlot.index, remainderPlot.pods, currentHarvestable); - remainderPlot.beansPerPod = sourcePlot.beansPerPod; - remainderPlot.save(); - - // log.debug("\nPodTransfer: split source twice\n", []); - // log.debug("\nPodTransfer: Updated Source Pods - {}\n", [sourcePlot.pods.toString()]); - // log.debug("\nPodTransfer: Transferred Pods - {}\n", [toPlot.pods.toString()]); - // log.debug("\nPodTransfer: Remainder Pods - {}\n", [remainderPlot.pods.toString()]); - } - sortedPlots.sort(); - field.plotIndexes = sortedPlots; - field.save(); - - // Update any harvestable pod amounts - // No need to shift beanstalk field, only the farmer fields. - if (transferredHarvestable != ZERO_BI) { - updateFieldTotals( - event.params.from, - currentSeason, - ZERO_BI, - ZERO_BI, - ZERO_BI, - ZERO_BI, - ZERO_BI.minus(transferredHarvestable), - ZERO_BI, - event.block.timestamp, - event.block.number - ); - updateFieldTotals( - event.params.to, - currentSeason, - ZERO_BI, - ZERO_BI, - ZERO_BI, - ZERO_BI, - transferredHarvestable, - ZERO_BI, - event.block.timestamp, - event.block.number - ); - } - - // Save the raw event data - savePodTransfer(event); -} - -export function handleSupplyIncrease(event: SupplyIncrease): void { - updateFieldTotals( - event.address, - event.params.season.toI32(), - event.params.newSoil, - ZERO_BI, - ZERO_BI, - ZERO_BI, - ZERO_BI, - ZERO_BI, - event.block.timestamp, - event.block.number - ); -} - -export function handleSupplyDecrease(event: SupplyDecrease): void { - updateFieldTotals( - event.address, - event.params.season.toI32(), - event.params.newSoil, - ZERO_BI, - ZERO_BI, - ZERO_BI, - ZERO_BI, - ZERO_BI, - event.block.timestamp, - event.block.number - ); -} - -export function handleSupplyNeutral(event: SupplyNeutral): void { - updateFieldTotals( - event.address, - event.params.season.toI32(), - event.params.newSoil, - ZERO_BI, - ZERO_BI, - ZERO_BI, - ZERO_BI, - ZERO_BI, - event.block.timestamp, - event.block.number - ); -} - -export function handleFundFundraiser(event: FundFundraiser): void { - // Account for the fact thta fundraiser sow using no soil. - let beanstalk = loadBeanstalk(event.address); - updateFieldTotals( - event.address, - beanstalk.lastSeason, - ZERO_BI, - ZERO_BI.minus(event.params.amount), - ZERO_BI, - ZERO_BI, - ZERO_BI, - ZERO_BI, - event.block.timestamp, - event.block.number - ); -} - -function updateFieldTotals( - account: Address, - season: i32, - soil: BigInt, - sownBeans: BigInt, - sownPods: BigInt, - transferredPods: BigInt, - harvestablePods: BigInt, - harvestedPods: BigInt, - timestamp: BigInt, - blockNumber: BigInt -): void { - let field = loadField(account); - let fieldHourly = loadFieldHourly(account, season, timestamp); - let fieldDaily = loadFieldDaily(account, timestamp); - - field.season = season; - field.soil = field.soil.plus(soil).minus(sownBeans); - field.sownBeans = field.sownBeans.plus(sownBeans); - field.unharvestablePods = field.unharvestablePods.plus(sownPods).minus(harvestablePods).plus(transferredPods); - field.harvestablePods = field.harvestablePods.plus(harvestablePods); - field.harvestedPods = field.harvestedPods.plus(harvestedPods); - field.podIndex = field.podIndex.plus(sownPods); - field.save(); - - fieldHourly.soil = field.soil; - fieldHourly.sownBeans = field.sownBeans; - fieldHourly.unharvestablePods = field.unharvestablePods; - fieldHourly.harvestablePods = field.harvestablePods; - fieldHourly.harvestedPods = field.harvestedPods; - fieldHourly.podIndex = field.podIndex; - fieldHourly.issuedSoil = fieldHourly.issuedSoil.plus(soil); - fieldHourly.deltaSownBeans = fieldHourly.deltaSownBeans.plus(sownBeans); - fieldHourly.deltaUnharvestablePods = fieldHourly.deltaUnharvestablePods.plus(sownPods).minus(harvestablePods).plus(transferredPods); - fieldHourly.deltaHarvestablePods = fieldHourly.deltaHarvestablePods.plus(harvestablePods); - fieldHourly.deltaHarvestedPods = fieldHourly.deltaHarvestedPods.plus(harvestedPods); - fieldHourly.blockNumber = fieldHourly.blockNumber == ZERO_BI ? blockNumber : fieldHourly.blockNumber; - fieldHourly.updatedAt = timestamp; - if (field.soil == ZERO_BI) { - fieldHourly.blocksToSoldOutSoil = blockNumber.minus(fieldHourly.blockNumber); - fieldHourly.soilSoldOut = true; - } - fieldHourly.save(); - - fieldDaily.soil = field.soil; - fieldDaily.sownBeans = field.sownBeans; - fieldDaily.unharvestablePods = field.unharvestablePods; - fieldDaily.harvestablePods = field.harvestablePods; - fieldDaily.harvestedPods = field.harvestedPods; - fieldDaily.podIndex = field.podIndex; - fieldDaily.issuedSoil = fieldDaily.issuedSoil.plus(soil); - fieldDaily.deltaSownBeans = fieldDaily.deltaSownBeans.plus(sownBeans); - fieldDaily.deltaUnharvestablePods = fieldDaily.deltaUnharvestablePods.plus(sownPods).minus(harvestablePods).plus(transferredPods); - fieldDaily.deltaHarvestablePods = fieldDaily.deltaHarvestablePods.plus(harvestablePods); - fieldDaily.deltaHarvestedPods = fieldDaily.deltaHarvestedPods.plus(harvestedPods); - fieldDaily.updatedAt = timestamp; - fieldDaily.save(); -} - -export function updateHarvestablePlots(harvestableIndex: BigInt, timestamp: BigInt, blockNumber: BigInt): void { - let field = loadField(BEANSTALK); - let sortedIndexes = field.plotIndexes.sort(); - - for (let i = 0; i < sortedIndexes.length; i++) { - if (sortedIndexes[i] > harvestableIndex) { - break; - } - let plot = loadPlot(BEANSTALK, sortedIndexes[i]); - - // Plot is fully harvestable, but hasn't been harvested yet - if (plot.harvestablePods == plot.pods) { - continue; - } - - let harvestablePods = harvestableIndex.minus(plot.index); - let oldHarvestablePods = plot.harvestablePods; - plot.harvestablePods = harvestablePods >= plot.pods ? plot.pods : harvestablePods; - plot.save(); - - let deltaHarvestablePods = oldHarvestablePods == ZERO_BI ? plot.harvestablePods : plot.harvestablePods.minus(oldHarvestablePods); - - updateFieldTotals(BEANSTALK, field.season, ZERO_BI, ZERO_BI, ZERO_BI, ZERO_BI, deltaHarvestablePods, ZERO_BI, timestamp, blockNumber); - updateFieldTotals( - Address.fromString(plot.farmer), - field.season, - ZERO_BI, - ZERO_BI, - ZERO_BI, - ZERO_BI, - deltaHarvestablePods, - ZERO_BI, - timestamp, - blockNumber - ); - } -} - -function incrementSowers(account: Address, season: i32, timestamp: BigInt): void { - // Increment total number of sowers by one - let field = loadField(account); - let fieldHourly = loadFieldHourly(account, season, timestamp); - let fieldDaily = loadFieldDaily(account, timestamp); - - field.numberOfSowers += 1; - field.save(); - - fieldHourly.numberOfSowers = field.numberOfSowers; - fieldHourly.deltaNumberOfSowers += 1; - fieldHourly.save(); - - fieldDaily.numberOfSowers = field.numberOfSowers; - fieldDaily.deltaNumberOfSowers += 1; - fieldDaily.save(); -} - -function incrementSows(account: Address, season: i32, timestamp: BigInt): void { - // Increment total sows by one - let field = loadField(account); - let fieldHourly = loadFieldHourly(account, season, timestamp); - let fieldDaily = loadFieldDaily(account, timestamp); - - // Add to protocol numberOfSowers if needed - if (account != BEANSTALK && field.numberOfSows == 0) incrementSowers(BEANSTALK, season, timestamp); - - // Update sower counts - field.numberOfSows += 1; - field.save(); - - fieldHourly.numberOfSows = field.numberOfSows; - fieldHourly.deltaNumberOfSows += 1; - fieldHourly.save(); - - fieldDaily.numberOfSows = field.numberOfSows; - fieldDaily.deltaNumberOfSows += 1; - fieldDaily.save(); -} diff --git a/projects/subgraph-beanstalk/src/GaugeHandler.ts b/projects/subgraph-beanstalk/src/GaugeHandler.ts deleted file mode 100644 index c887572ce0..0000000000 --- a/projects/subgraph-beanstalk/src/GaugeHandler.ts +++ /dev/null @@ -1,243 +0,0 @@ -import { - BeanToMaxLpGpPerBdvRatioChange, - GaugePointChange, - TemperatureChange, - UpdateAverageStalkPerBdvPerSeason, - FarmerGerminatingStalkBalanceChanged, - TotalGerminatingBalanceChanged, - UpdateGaugeSettings, - WhitelistToken, - TotalGerminatingStalkChanged, - TotalStalkChangedFromGermination -} from "../generated/Beanstalk-ABIs/SeedGauge"; -import { handleRateChange } from "./utils/Field"; -import { - loadSilo, - loadSiloHourlySnapshot, - loadSiloDailySnapshot, - loadWhitelistTokenSetting, - loadWhitelistTokenDailySnapshot, - loadWhitelistTokenHourlySnapshot, - addToSiloWhitelist -} from "./utils/SiloEntities"; -import { deleteGerminating, loadGerminating, loadOrCreateGerminating } from "./utils/Germinating"; -import { BI_10, ZERO_BI } from "../../subgraph-core/utils/Decimals"; -import { updateStalkBalances } from "./SiloHandler"; -import { getCurrentSeason } from "./utils/Season"; -import { WhitelistToken as WhitelistTokenEntity } from "../generated/schema"; -import { BEAN_WETH_CP2_WELL } from "../../subgraph-core/utils/Constants"; -import { Bytes4_emptyToNull } from "../../subgraph-core/utils/Bytes"; - -export function handleTemperatureChange(event: TemperatureChange): void { - handleRateChange(event.address, event.block, event.params.season, event.params.caseId, event.params.absChange); -} - -// SEED GAUGE SEASONAL ADJUSTMENTS // - -export function handleBeanToMaxLpGpPerBdvRatioChange(event: BeanToMaxLpGpPerBdvRatioChange): void { - let silo = loadSilo(event.address); - - if (silo.beanToMaxLpGpPerBdvRatio === null) { - silo.beanToMaxLpGpPerBdvRatio = event.params.absChange; - } else { - silo.beanToMaxLpGpPerBdvRatio = silo.beanToMaxLpGpPerBdvRatio!.plus(event.params.absChange); - } - silo.save(); - - let siloHourly = loadSiloHourlySnapshot(event.address, event.params.season.toI32(), event.block.timestamp); - let siloDaily = loadSiloDailySnapshot(event.address, event.block.timestamp); - siloHourly.beanToMaxLpGpPerBdvRatio = silo.beanToMaxLpGpPerBdvRatio; - siloHourly.caseId = event.params.caseId; - siloDaily.beanToMaxLpGpPerBdvRatio = silo.beanToMaxLpGpPerBdvRatio; - siloHourly.save(); - siloDaily.save(); -} - -export function handleGaugePointChange(event: GaugePointChange): void { - let siloSettings = loadWhitelistTokenSetting(event.params.token); - siloSettings.gaugePoints = event.params.gaugePoints; - siloSettings.updatedAt = event.block.timestamp; - siloSettings.save(); - - let whitelistHourly = loadWhitelistTokenHourlySnapshot(event.params.token, event.params.season.toI32(), event.block.timestamp); - let whitelistDaily = loadWhitelistTokenDailySnapshot(event.params.token, event.block.timestamp); - whitelistHourly.gaugePoints = event.params.gaugePoints; - whitelistDaily.gaugePoints = event.params.gaugePoints; - whitelistHourly.save(); - whitelistDaily.save(); -} - -export function handleUpdateAverageStalkPerBdvPerSeason(event: UpdateAverageStalkPerBdvPerSeason): void { - let silo = loadSilo(event.address); - - silo.grownStalkPerSeason = silo.depositedBDV.times(event.params.newStalkPerBdvPerSeason); - silo.save(); - let siloHourly = loadSiloHourlySnapshot(event.address, getCurrentSeason(event.address), event.block.timestamp); - let siloDaily = loadSiloDailySnapshot(event.address, event.block.timestamp); - siloHourly.grownStalkPerSeason = silo.grownStalkPerSeason; - siloDaily.grownStalkPerSeason = silo.grownStalkPerSeason; - siloHourly.save(); - siloDaily.save(); - - // Individual asset grown stalk is set by the UpdatedStalkPerBdvPerSeason event in SiloHandler -} - -// GERMINATING STALK // - -// Tracks germinating balances for individual famers -export function handleFarmerGerminatingStalkBalanceChanged(event: FarmerGerminatingStalkBalanceChanged): void { - if (event.params.deltaGerminatingStalk == ZERO_BI) { - return; - } - - const currentSeason = getCurrentSeason(event.address); - - if (event.params.deltaGerminatingStalk > ZERO_BI) { - // Germinating stalk is being added in the current season - let farmerGerminating = loadOrCreateGerminating(event.params.account, currentSeason, true); - farmerGerminating.stalk = farmerGerminating.stalk.plus(event.params.deltaGerminatingStalk); - farmerGerminating.save(); - } else { - // Germinating stalk is being removed. It therefore must have created the entity already - let farmerGerminating = loadGerminating(event.params.account, event.params.germinationState); - farmerGerminating.stalk = farmerGerminating.stalk.plus(event.params.deltaGerminatingStalk); - if (farmerGerminating.stalk == ZERO_BI) { - deleteGerminating(farmerGerminating); - } else { - farmerGerminating.save(); - } - } - - let farmerSilo = loadSilo(event.params.account); - farmerSilo.germinatingStalk = farmerSilo.germinatingStalk.plus(event.params.deltaGerminatingStalk); - farmerSilo.save(); - - let siloHourly = loadSiloHourlySnapshot(event.params.account, currentSeason, event.block.timestamp); - let siloDaily = loadSiloDailySnapshot(event.params.account, event.block.timestamp); - siloHourly.germinatingStalk = farmerSilo.germinatingStalk; - siloHourly.deltaGerminatingStalk = siloHourly.deltaGerminatingStalk.plus(event.params.deltaGerminatingStalk); - siloDaily.germinatingStalk = farmerSilo.germinatingStalk; - siloDaily.deltaGerminatingStalk = siloDaily.deltaGerminatingStalk.plus(event.params.deltaGerminatingStalk); - siloHourly.save(); - siloDaily.save(); -} - -// Tracks the germinating balance on a token level -export function handleTotalGerminatingBalanceChanged(event: TotalGerminatingBalanceChanged): void { - if (event.params.deltaAmount == ZERO_BI && event.params.deltaBdv == ZERO_BI) { - return; - } - - let tokenGerminating = loadOrCreateGerminating(event.params.token, event.params.germinationSeason.toU32(), false); - tokenGerminating.season = event.params.germinationSeason.toU32(); - tokenGerminating.tokenAmount = tokenGerminating.tokenAmount.plus(event.params.deltaAmount); - tokenGerminating.bdv = tokenGerminating.bdv.plus(event.params.deltaBdv); - if (tokenGerminating.tokenAmount == ZERO_BI) { - deleteGerminating(tokenGerminating); - } else { - tokenGerminating.save(); - } -} - -// This occurs at the beanstalk level regardless of whether users mow their own germinating stalk into regular stalk. -export function handleTotalGerminatingStalkChanged(event: TotalGerminatingStalkChanged): void { - if (event.params.deltaGerminatingStalk == ZERO_BI) { - return; - } - - let siloGerminating = loadOrCreateGerminating(event.address, event.params.germinationSeason.toU32(), false); - siloGerminating.season = event.params.germinationSeason.toU32(); - siloGerminating.stalk = siloGerminating.stalk.plus(event.params.deltaGerminatingStalk); - // Don't delete this entity as the overall silo germinating stalk entity is likely to be recreated frequently. - siloGerminating.save(); - - let silo = loadSilo(event.address); - silo.germinatingStalk = silo.germinatingStalk.plus(event.params.deltaGerminatingStalk); - silo.save(); - - let siloHourly = loadSiloHourlySnapshot(event.address, getCurrentSeason(event.address), event.block.timestamp); - let siloDaily = loadSiloDailySnapshot(event.address, event.block.timestamp); - siloHourly.germinatingStalk = silo.germinatingStalk; - siloHourly.deltaGerminatingStalk = siloHourly.deltaGerminatingStalk.plus(event.params.deltaGerminatingStalk); - siloDaily.germinatingStalk = silo.germinatingStalk; - siloDaily.deltaGerminatingStalk = siloDaily.deltaGerminatingStalk.plus(event.params.deltaGerminatingStalk); - siloHourly.save(); - siloDaily.save(); -} - -// Germination completes, germinating stalk turns into stalk. -// The removal of Germinating stalk would have already been handled from a separate emission -export function handleTotalStalkChangedFromGermination(event: TotalStalkChangedFromGermination): void { - updateStalkBalances( - event.address, - getCurrentSeason(event.address), - event.params.deltaStalk, - event.params.deltaRoots, - event.block.timestamp, - event.block.number - ); -} - -// WHITELIST / GAUGE CONFIGURATION SETTINGS // - -export function handleWhitelistToken_BIP45(event: WhitelistToken): void { - addToSiloWhitelist(event.address, event.params.token); - - let siloSettings = loadWhitelistTokenSetting(event.params.token); - - siloSettings.selector = event.params.selector; - siloSettings.stalkEarnedPerSeason = event.params.stalkEarnedPerSeason; - siloSettings.stalkIssuedPerBdv = event.params.stalkIssuedPerBdv; - siloSettings.gaugePoints = event.params.gaugePoints; - siloSettings.gpSelector = Bytes4_emptyToNull(event.params.gpSelector); - siloSettings.lwSelector = Bytes4_emptyToNull(event.params.lwSelector); - siloSettings.optimalPercentDepositedBdv = event.params.optimalPercentDepositedBdv; - siloSettings.updatedAt = event.block.timestamp; - siloSettings.save(); - - loadWhitelistTokenHourlySnapshot(event.params.token, getCurrentSeason(event.address), event.block.timestamp); - loadWhitelistTokenDailySnapshot(event.params.token, event.block.timestamp); - - let id = "whitelistToken-" + event.transaction.hash.toHexString() + "-" + event.logIndex.toString(); - let rawEvent = new WhitelistTokenEntity(id); - rawEvent.hash = event.transaction.hash.toHexString(); - rawEvent.logIndex = event.logIndex.toI32(); - rawEvent.protocol = event.address.toHexString(); - rawEvent.token = event.params.token.toHexString(); - rawEvent.blockNumber = event.block.number; - rawEvent.createdAt = event.block.timestamp; - rawEvent.save(); -} - -export function handleUpdateGaugeSettings(event: UpdateGaugeSettings): void { - let siloSettings = loadWhitelistTokenSetting(event.params.token); - siloSettings.gpSelector = Bytes4_emptyToNull(event.params.gpSelector); - siloSettings.lwSelector = Bytes4_emptyToNull(event.params.lwSelector); - siloSettings.optimalPercentDepositedBdv = event.params.optimalPercentDepositedBdv; - siloSettings.updatedAt = event.block.timestamp; - - let hourly = loadWhitelistTokenHourlySnapshot(event.params.token, getCurrentSeason(event.address), event.block.timestamp); - hourly.gpSelector = siloSettings.gpSelector; - hourly.lwSelector = siloSettings.lwSelector; - hourly.optimalPercentDepositedBdv = siloSettings.optimalPercentDepositedBdv; - hourly.updatedAt = siloSettings.updatedAt; - - let daily = loadWhitelistTokenDailySnapshot(event.params.token, event.block.timestamp); - daily.gpSelector = siloSettings.gpSelector; - daily.lwSelector = siloSettings.lwSelector; - daily.optimalPercentDepositedBdv = siloSettings.optimalPercentDepositedBdv; - daily.updatedAt = siloSettings.updatedAt; - - // On initial gauge deployment, there was no GaugePointChange event emitted. Need to initialize BEANETH here - if ( - event.params.token == BEAN_WETH_CP2_WELL && - event.transaction.hash.toHexString().toLowerCase() == "0x299a4b93b8d19f8587b648ce04e3f5e618ea461426bb2b2337993b5d6677f6a7" - ) { - siloSettings.gaugePoints = BI_10.pow(20); - hourly.gaugePoints = BI_10.pow(20); - daily.gaugePoints = BI_10.pow(20); - } - siloSettings.save(); - hourly.save(); - daily.save(); -} diff --git a/projects/subgraph-beanstalk/src/ReplantHandler.ts b/projects/subgraph-beanstalk/src/ReplantHandler.ts deleted file mode 100644 index 56f90b3393..0000000000 --- a/projects/subgraph-beanstalk/src/ReplantHandler.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Chop as ChopEntity } from "../generated/schema"; -import { Chop } from "../generated/Beanstalk-ABIs/MarketV2"; - -export function handleChop(event: Chop): void { - let id = "chop-" + event.transaction.hash.toHexString() + "-" + event.transactionLogIndex.toString(); - let chop = new ChopEntity(id); - chop.hash = event.transaction.hash.toHexString(); - chop.logIndex = event.transactionLogIndex.toI32(); - chop.protocol = event.address.toHexString(); - chop.farmer = event.params.account.toHexString(); - chop.unripe = event.params.token.toHexString(); - chop.amount = event.params.amount; - chop.underlying = event.params.underlying.toHexString(); - chop.blockNumber = event.block.number; - chop.createdAt = event.block.timestamp; - chop.save(); -} diff --git a/projects/subgraph-beanstalk/src/SeasonHandler.ts b/projects/subgraph-beanstalk/src/SeasonHandler.ts deleted file mode 100644 index bb4efee92e..0000000000 --- a/projects/subgraph-beanstalk/src/SeasonHandler.ts +++ /dev/null @@ -1,251 +0,0 @@ -import { Address, BigDecimal, BigInt } from "@graphprotocol/graph-ts"; -import { MetapoolOracle, Reward, Soil, WellOracle } from "../generated/Beanstalk-ABIs/BasinBip"; -import { CurvePrice } from "../generated/Beanstalk-ABIs/CurvePrice"; -import { SeasonSnapshot, Sunrise, Incentivization, PreReplant } from "../generated/Beanstalk-ABIs/PreReplant"; -import { Incentive } from "../generated/schema"; -import { updateHarvestablePlots } from "./FieldHandler"; -import { loadBeanstalk } from "./utils/Beanstalk"; -import { Reward as RewardEntity, MetapoolOracle as MetapoolOracleEntity, WellOracle as WellOracleEntity } from "../generated/schema"; -import { BEANSTALK, BEAN_ERC20, CURVE_PRICE, GAUGE_BIP45_BLOCK } from "../../subgraph-core/utils/Constants"; -import { toDecimal, ZERO_BD, ZERO_BI } from "../../subgraph-core/utils/Decimals"; -import { loadField, loadFieldDaily, loadFieldHourly } from "./utils/Field"; -import { - loadPodMarketplace, - loadPodMarketplaceDailySnapshot, - loadPodMarketplaceHourlySnapshot, - updateExpiredPlots -} from "./utils/PodMarketplace"; -import { loadSeason } from "./utils/Season"; -import { addDepositToSiloAsset, updateStalkWithCalls } from "./SiloHandler"; -import { updateBeanEMA } from "./YieldHandler"; -import { - loadSilo, - loadSiloDailySnapshot, - loadSiloHourlySnapshot, - loadSiloAssetDailySnapshot, - loadSiloAssetHourlySnapshot -} from "./utils/SiloEntities"; -import { BeanstalkPrice_try_price, getBeanstalkPrice } from "./utils/BeanstalkPrice"; - -export function handleSunrise(event: Sunrise): void { - let currentSeason = event.params.season.toI32(); - let season = loadSeason(event.address, event.params.season); - - // Update any farmers that had silo transfers from the prior season - updateStalkWithCalls(currentSeason - 1, event.block.timestamp, event.block.number); - - // Update season metrics - if (event.params.season == BigInt.fromI32(6075)) { - // Replant oracle initialization - season.price = BigDecimal.fromString("1.07"); - } - season.sunriseBlock = event.block.number; - season.createdAt = event.block.timestamp; - season.save(); - - // Update field metrics - let field = loadField(event.address); - let fieldHourly = loadFieldHourly(event.address, field.season, event.block.timestamp); - let fieldDaily = loadFieldDaily(event.address, event.block.timestamp); - - // -- Field level totals - field.season = currentSeason; - field.podRate = season.beans == ZERO_BI ? ZERO_BD : toDecimal(field.unharvestablePods, 6).div(toDecimal(season.beans, 6)); - fieldHourly.podRate = field.podRate; - fieldDaily.season = currentSeason; - fieldDaily.podRate = field.podRate; - - field.save(); - fieldHourly.save(); - fieldDaily.save(); - - // Marketplace Season Update - - let market = loadPodMarketplace(event.address); - let marketHourly = loadPodMarketplaceHourlySnapshot(event.address, market.season, event.block.timestamp); - let marketDaily = loadPodMarketplaceDailySnapshot(event.address, event.block.timestamp); - market.season = currentSeason; - marketHourly.season = currentSeason; - marketDaily.season = currentSeason; - market.save(); - marketHourly.save(); - marketDaily.save(); - - // Create silo entities for the protocol - let silo = loadSilo(event.address); - loadSiloHourlySnapshot(event.address, currentSeason, event.block.timestamp); - loadSiloDailySnapshot(event.address, event.block.timestamp); - for (let i = 0; i < silo.whitelistedTokens.length; i++) { - loadSiloAssetHourlySnapshot(event.address, Address.fromString(silo.whitelistedTokens[i]), currentSeason, event.block.timestamp); - loadSiloAssetDailySnapshot(event.address, Address.fromString(silo.whitelistedTokens[i]), event.block.timestamp); - } -} - -export function handleSeasonSnapshot(event: SeasonSnapshot): void { - let season = loadSeason(event.address, event.params.season); - season.price = toDecimal(event.params.price, 18); - season.save(); -} - -export function handleReward(event: Reward): void { - let id = "reward-" + event.transaction.hash.toHexString() + "-" + event.transactionLogIndex.toString(); - let reward = new RewardEntity(id); - reward.hash = event.transaction.hash.toHexString(); - reward.logIndex = event.transactionLogIndex.toI32(); - reward.protocol = event.address.toHexString(); - reward.season = event.params.season.toI32(); - reward.toField = event.params.toField; - reward.toSilo = event.params.toSilo; - reward.toFertilizer = event.params.toFertilizer; - reward.blockNumber = event.block.number; - reward.createdAt = event.block.timestamp; - reward.save(); - - let season = loadSeason(event.address, event.params.season); - season.rewardBeans = reward.toField.plus(reward.toSilo).plus(reward.toFertilizer); - season.save(); - - // Add to total Silo Bean mints - - let silo = loadSilo(event.address); - let siloHourly = loadSiloHourlySnapshot(event.address, season.season, event.block.timestamp); - let siloDaily = loadSiloDailySnapshot(event.address, event.block.timestamp); - let newPlantableStalk = event.params.toSilo.times(BigInt.fromI32(10000)); // Stalk has 10 decimals - - silo.beanMints = silo.beanMints.plus(event.params.toSilo); - silo.plantableStalk = silo.plantableStalk.plus(newPlantableStalk); - silo.depositedBDV = silo.depositedBDV.plus(event.params.toSilo); - silo.save(); - - siloHourly.beanMints = silo.beanMints; - siloHourly.plantableStalk = silo.plantableStalk; - siloHourly.depositedBDV = silo.depositedBDV; - siloHourly.deltaBeanMints = siloHourly.deltaBeanMints.plus(event.params.toSilo); - siloHourly.deltaPlantableStalk = siloHourly.deltaPlantableStalk.plus(newPlantableStalk); - siloHourly.deltaDepositedBDV = siloHourly.deltaDepositedBDV.plus(event.params.toSilo); - siloHourly.save(); - - siloDaily.beanMints = silo.beanMints; - siloDaily.plantableStalk = silo.plantableStalk; - siloDaily.depositedBDV = silo.depositedBDV; - siloDaily.deltaBeanMints = siloDaily.deltaBeanMints.plus(event.params.toSilo); - siloDaily.deltaPlantableStalk = siloDaily.deltaPlantableStalk.plus(newPlantableStalk); - siloDaily.deltaDepositedBDV = siloDaily.deltaDepositedBDV.plus(event.params.toSilo); - siloDaily.save(); - - addDepositToSiloAsset( - event.address, - BEAN_ERC20, - event.params.season.toI32(), - event.params.toSilo, - event.params.toSilo, - event.block.timestamp, - event.block.number - ); -} - -export function handleMetapoolOracle(event: MetapoolOracle): void { - let id = "metapoolOracle-" + event.transaction.hash.toHexString() + "-" + event.transactionLogIndex.toString(); - let oracle = new MetapoolOracleEntity(id); - oracle.hash = event.transaction.hash.toHexString(); - oracle.logIndex = event.transactionLogIndex.toI32(); - oracle.protocol = event.address.toHexString(); - oracle.season = event.params.season.toI32(); - oracle.deltaB = event.params.deltaB; - oracle.balanceA = event.params.balances[0]; - oracle.balanceB = event.params.balances[1]; - oracle.blockNumber = event.block.number; - oracle.createdAt = event.block.timestamp; - oracle.save(); - - if (event.block.number < GAUGE_BIP45_BLOCK) { - let season = loadSeason(event.address, event.params.season); - // Attempt to pull from Beanstalk Price contract first - let beanstalkQuery = BeanstalkPrice_try_price(event.address, event.block.number); - if (beanstalkQuery.reverted) { - let curvePrice = CurvePrice.bind(CURVE_PRICE); - season.price = toDecimal(curvePrice.getCurve().price); - } else { - season.price = toDecimal(beanstalkQuery.value.price); - } - season.deltaB = event.params.deltaB; - season.save(); - } -} - -export function handleWellOracle(event: WellOracle): void { - let id = "wellOracle-" + event.transaction.hash.toHexString() + "-" + event.transactionLogIndex.toString(); - let oracle = new WellOracleEntity(id); - oracle.hash = event.transaction.hash.toHexString(); - oracle.logIndex = event.transactionLogIndex.toI32(); - oracle.protocol = event.address.toHexString(); - oracle.season = event.params.season.toI32(); - oracle.deltaB = event.params.deltaB; - oracle.cumulativeReserves = event.params.cumulativeReserves; - oracle.blockNumber = event.block.number; - oracle.createdAt = event.block.timestamp; - oracle.save(); - - let season = loadSeason(event.address, event.params.season); - season.deltaB = season.deltaB.plus(event.params.deltaB); - if (event.block.number >= GAUGE_BIP45_BLOCK && season.price == ZERO_BD) { - let beanstalkPrice = getBeanstalkPrice(event.block.number); - let beanstalkQuery = beanstalkPrice.getConstantProductWell(event.params.well); - season.price = toDecimal(beanstalkQuery.price); - } - season.save(); -} - -export function handleSoil(event: Soil): void { - // Replant sets the soil to the amount every season instead of adding new soil - // to an existing amount. - - let field = loadField(event.address); - let fieldHourly = loadFieldHourly(event.address, event.params.season.toI32(), event.block.timestamp); - let fieldDaily = loadFieldDaily(event.address, event.block.timestamp); - - field.season = event.params.season.toI32(); - field.soil = event.params.soil; - field.save(); - - fieldHourly.soil = field.soil; - fieldHourly.issuedSoil = fieldHourly.issuedSoil.plus(event.params.soil); - fieldHourly.updatedAt = event.block.timestamp; - fieldHourly.save(); - - fieldDaily.soil = field.soil; - fieldDaily.issuedSoil = fieldDaily.issuedSoil.plus(event.params.soil); - fieldDaily.updatedAt = event.block.timestamp; - fieldDaily.save(); - - if (event.params.season.toI32() >= 6075) { - updateBeanEMA(event.params.season.toI32(), event.block.timestamp); - } -} - -export function handleIncentive(event: Incentivization): void { - // This is the final function to be called during sunrise both pre and post replant - let id = "incentive-" + event.transaction.hash.toHexString() + "-" + event.transactionLogIndex.toString(); - let incentive = new Incentive(id); - incentive.hash = event.transaction.hash.toHexString(); - incentive.logIndex = event.transactionLogIndex.toI32(); - incentive.protocol = event.address.toHexString(); - incentive.caller = event.params.account.toHexString(); - incentive.amount = event.params.beans; - incentive.blockNumber = event.block.number; - incentive.createdAt = event.block.timestamp; - incentive.save(); - - // Update market cap for season - let beanstalk = loadBeanstalk(event.address); - let beanstalk_contract = PreReplant.bind(BEANSTALK); - let season = loadSeason(event.address, BigInt.fromI32(beanstalk.lastSeason)); - - season.marketCap = season.price.times(toDecimal(season.beans)); - season.incentiveBeans = event.params.beans; - season.harvestableIndex = beanstalk_contract.harvestableIndex(); - season.save(); - - updateExpiredPlots(season.harvestableIndex, event.address, event.block.timestamp); - updateHarvestablePlots(season.harvestableIndex, event.block.timestamp, event.block.number); -} diff --git a/projects/subgraph-beanstalk/src/SiloHandler.ts b/projects/subgraph-beanstalk/src/SiloHandler.ts deleted file mode 100644 index b41680780a..0000000000 --- a/projects/subgraph-beanstalk/src/SiloHandler.ts +++ /dev/null @@ -1,998 +0,0 @@ -import { Address, BigInt, log } from "@graphprotocol/graph-ts"; -import { - AddDeposit, - StalkBalanceChanged, - SeedsBalanceChanged, - AddWithdrawal, - RemoveDeposit, - RemoveDeposits, - RemoveWithdrawal, - RemoveWithdrawals, - Plant, - WhitelistToken, - DewhitelistToken -} from "../generated/Beanstalk-ABIs/MarketV2"; -import { - AddDeposit as AddDeposit_V3, - RemoveDeposit as RemoveDeposit_V3, - RemoveDeposits as RemoveDeposits_V3, - UpdatedStalkPerBdvPerSeason, - WhitelistToken as WhitelistToken_V3 -} from "../generated/Beanstalk-ABIs/SiloV3"; -import { Replanted, TransferDepositCall, TransferDepositsCall } from "../generated/Beanstalk-ABIs/Replanted"; -import { ZERO_BI } from "../../subgraph-core/utils/Decimals"; -import { loadFarmer } from "./utils/Farmer"; -import { - loadSilo, - loadSiloDailySnapshot, - loadSiloHourlySnapshot, - loadSiloAsset, - loadSiloAssetDailySnapshot, - loadSiloAssetHourlySnapshot, - loadSiloWithdraw, - loadSiloDeposit, - loadSiloDepositV3, - loadWhitelistTokenSetting, - loadWhitelistTokenHourlySnapshot, - loadWhitelistTokenDailySnapshot, - addToSiloWhitelist -} from "./utils/SiloEntities"; -import { - AddDeposit as AddDepositEntity, - RemoveDeposit as RemoveDepositEntity, - WhitelistToken as WhitelistTokenEntity, - DewhitelistToken as DewhitelistTokenEntity, - SeedChange, - StalkChange -} from "../generated/schema"; -import { loadBeanstalk } from "./utils/Beanstalk"; -import { BEANSTALK, BEAN_ERC20 } from "../../subgraph-core/utils/Constants"; -import { getCurrentSeason } from "./utils/Season"; - -/** - * SILO V2 (REPLANT) HANDLERS - */ - -export function handleAddDeposit(event: AddDeposit): void { - let deposit = loadSiloDeposit(event.params.account, event.params.token, event.params.season); - deposit.amount = deposit.amount.plus(event.params.amount); - deposit.depositedAmount = deposit.depositedAmount.plus(event.params.amount); - deposit.bdv = deposit.bdv.plus(event.params.bdv); - deposit.depositedBDV = deposit.depositedBDV.plus(event.params.bdv); - let depositHashes = deposit.hashes; - depositHashes.push(event.transaction.hash.toHexString()); - deposit.hashes = depositHashes; - deposit.createdAt = deposit.createdAt == ZERO_BI ? event.block.timestamp : deposit.createdAt; - deposit.updatedAt = event.block.timestamp; - deposit.save(); - - // Use the current season of beanstalk for updating silo and farmer totals - let beanstalk = loadBeanstalk(event.address); - - // Update overall silo totals - addDepositToSilo( - event.address, - beanstalk.lastSeason, - event.params.bdv, - addDepositToSiloAsset( - event.address, - event.params.token, - beanstalk.lastSeason, - event.params.bdv, - event.params.amount, - event.block.timestamp, - event.block.number - ), - event.block.timestamp, - event.block.number - ); - - // Ensure that a Farmer entity is set up for this account. - loadFarmer(event.params.account); - - // Update farmer silo totals - addDepositToSilo( - event.params.account, - beanstalk.lastSeason, - event.params.bdv, - addDepositToSiloAsset( - event.params.account, - event.params.token, - beanstalk.lastSeason, - event.params.bdv, - event.params.amount, - event.block.timestamp, - event.block.number - ), - event.block.timestamp, - event.block.number - ); - - let id = "addDeposit-" + event.transaction.hash.toHexString() + "-" + event.transactionLogIndex.toString(); - let add = new AddDepositEntity(id); - add.hash = event.transaction.hash.toHexString(); - add.logIndex = event.transactionLogIndex.toI32(); - add.protocol = event.address.toHexString(); - add.account = event.params.account.toHexString(); - add.token = event.params.token.toHexString(); - add.season = event.params.season.toI32(); - add.amount = event.params.amount; - add.bdv = event.params.bdv; - add.blockNumber = event.block.number; - add.createdAt = event.block.timestamp; - add.save(); -} - -export function handleRemoveDeposit(event: RemoveDeposit): void { - let beanstalk = loadBeanstalk(event.address); // get current season - let deposit = loadSiloDeposit(event.params.account, event.params.token, event.params.season); - - let withdrawnBDV = deposit.amount == ZERO_BI ? ZERO_BI : event.params.amount.times(deposit.bdv).div(deposit.amount); - - // Update deposit - deposit.withdrawnBDV = deposit.withdrawnBDV.plus(withdrawnBDV); - deposit.bdv = deposit.bdv.minus(withdrawnBDV); - deposit.withdrawnAmount = deposit.withdrawnAmount.plus(event.params.amount); - deposit.amount = deposit.amount.minus(event.params.amount); - deposit.save(); - - // Update protocol totals - removeDepositFromSilo( - event.address, - beanstalk.lastSeason, - withdrawnBDV, - removeDepositFromSiloAsset( - event.address, - event.params.token, - beanstalk.lastSeason, - withdrawnBDV, - event.params.amount, - event.block.timestamp, - event.block.number - ), - event.block.timestamp, - event.block.number - ); - - // Update farmer totals - removeDepositFromSilo( - event.params.account, - beanstalk.lastSeason, - withdrawnBDV, - removeDepositFromSiloAsset( - event.params.account, - event.params.token, - beanstalk.lastSeason, - withdrawnBDV, - event.params.amount, - event.block.timestamp, - event.block.number - ), - event.block.timestamp, - event.block.number - ); - - let id = "removeDeposit-" + event.transaction.hash.toHexString() + "-" + event.transactionLogIndex.toString(); - let removal = new RemoveDepositEntity(id); - removal.hash = event.transaction.hash.toHexString(); - removal.logIndex = event.transactionLogIndex.toI32(); - removal.protocol = event.address.toHexString(); - removal.account = event.params.account.toHexString(); - removal.token = event.params.token.toHexString(); - removal.season = event.params.season.toI32(); - removal.amount = event.params.amount; - removal.blockNumber = event.block.number; - removal.createdAt = event.block.timestamp; - removal.save(); -} - -export function handleRemoveDeposits(event: RemoveDeposits): void { - let beanstalk = loadBeanstalk(event.address); // get current season - - for (let i = 0; i < event.params.seasons.length; i++) { - let deposit = loadSiloDeposit(event.params.account, event.params.token, event.params.seasons[i]); - - let withdrawnBDV = deposit.amount == ZERO_BI ? ZERO_BI : event.params.amounts[i].times(deposit.bdv).div(deposit.amount); - - // Update deposit - deposit.withdrawnBDV = deposit.withdrawnBDV.plus(withdrawnBDV); - deposit.bdv = deposit.bdv.minus(withdrawnBDV); - deposit.withdrawnAmount = deposit.withdrawnAmount.plus(event.params.amounts[i]); - deposit.amount = deposit.amount.minus(event.params.amounts[i]); - deposit.save(); - - // Update protocol totals - removeDepositFromSilo( - event.address, - beanstalk.lastSeason, - withdrawnBDV, - removeDepositFromSiloAsset( - event.address, - event.params.token, - beanstalk.lastSeason, - withdrawnBDV, - event.params.amounts[i], - event.block.timestamp, - event.block.number - ), - event.block.timestamp, - event.block.number - ); - - // Update farmer totals - removeDepositFromSilo( - event.params.account, - beanstalk.lastSeason, - withdrawnBDV, - removeDepositFromSiloAsset( - event.params.account, - event.params.token, - beanstalk.lastSeason, - withdrawnBDV, - event.params.amounts[i], - event.block.timestamp, - event.block.number - ), - event.block.timestamp, - event.block.number - ); - - let id = "removeDeposit-" + event.transaction.hash.toHexString() + "-" + event.transactionLogIndex.toString() + "-" + i.toString(); - let removal = new RemoveDepositEntity(id); - removal.hash = event.transaction.hash.toHexString(); - removal.logIndex = event.transactionLogIndex.toI32(); - removal.protocol = event.address.toHexString(); - removal.account = event.params.account.toHexString(); - removal.token = event.params.token.toHexString(); - removal.season = event.params.seasons[i].toI32(); - removal.amount = event.params.amounts[i]; - removal.blockNumber = event.block.number; - removal.createdAt = event.block.timestamp; - removal.save(); - } -} - -/** - * SILO V3 HANDLERS - */ - -export function handleAddDeposit_V3(event: AddDeposit_V3): void { - let deposit = loadSiloDepositV3(event.params.account, event.params.token, event.params.stem); - deposit.amount = deposit.amount.plus(event.params.amount); - deposit.depositedAmount = deposit.depositedAmount.plus(event.params.amount); - deposit.bdv = deposit.bdv.plus(event.params.bdv); - deposit.depositedBDV = deposit.depositedBDV.plus(event.params.bdv); - let depositHashes = deposit.hashes; - depositHashes.push(event.transaction.hash.toHexString()); - deposit.hashes = depositHashes; - deposit.createdAt = deposit.createdAt == ZERO_BI ? event.block.timestamp : deposit.createdAt; - deposit.updatedAt = event.block.timestamp; - deposit.save(); - - // Use the current season of beanstalk for updating silo and farmer totals - let beanstalk = loadBeanstalk(event.address); - - // Update overall silo totals - addDepositToSilo( - event.address, - beanstalk.lastSeason, - event.params.bdv, - addDepositToSiloAsset( - event.address, - event.params.token, - beanstalk.lastSeason, - event.params.bdv, - event.params.amount, - event.block.timestamp, - event.block.number - ), - event.block.timestamp, - event.block.number - ); - - // Ensure that a Farmer entity is set up for this account. - loadFarmer(event.params.account); - - // Update farmer silo totals - addDepositToSilo( - event.params.account, - beanstalk.lastSeason, - event.params.bdv, - addDepositToSiloAsset( - event.params.account, - event.params.token, - beanstalk.lastSeason, - event.params.bdv, - event.params.amount, - event.block.timestamp, - event.block.number - ), - event.block.timestamp, - event.block.number - ); - - let id = "addDeposit-" + event.transaction.hash.toHexString() + "-" + event.transactionLogIndex.toString(); - let add = new AddDepositEntity(id); - add.hash = event.transaction.hash.toHexString(); - add.logIndex = event.transactionLogIndex.toI32(); - add.protocol = event.address.toHexString(); - add.account = event.params.account.toHexString(); - add.token = event.params.token.toHexString(); - add.season = beanstalk.lastSeason; - add.stem = event.params.stem; - add.amount = event.params.amount; - add.bdv = event.params.bdv; - add.blockNumber = event.block.number; - add.createdAt = event.block.timestamp; - add.save(); -} - -export function handleRemoveDeposit_V3(event: RemoveDeposit_V3): void { - let beanstalk = loadBeanstalk(event.address); // get current season - let deposit = loadSiloDepositV3(event.params.account, event.params.token, event.params.stem); - - // Update deposit - deposit.withdrawnBDV = deposit.withdrawnBDV.plus(event.params.bdv); - deposit.bdv = deposit.bdv.minus(event.params.bdv); - deposit.withdrawnAmount = deposit.withdrawnAmount.plus(event.params.amount); - deposit.amount = deposit.amount.minus(event.params.amount); - deposit.save(); - - // Update protocol totals - removeDepositFromSilo( - event.address, - beanstalk.lastSeason, - event.params.bdv, - removeDepositFromSiloAsset( - event.address, - event.params.token, - beanstalk.lastSeason, - event.params.bdv, - event.params.amount, - event.block.timestamp, - event.block.number - ), - event.block.timestamp, - event.block.number - ); - - // Update farmer totals - removeDepositFromSilo( - event.params.account, - beanstalk.lastSeason, - event.params.bdv, - removeDepositFromSiloAsset( - event.params.account, - event.params.token, - beanstalk.lastSeason, - event.params.bdv, - event.params.amount, - event.block.timestamp, - event.block.number - ), - event.block.timestamp, - event.block.number - ); - - let id = "removeDeposit-" + event.transaction.hash.toHexString() + "-" + event.transactionLogIndex.toString(); - let removal = new RemoveDepositEntity(id); - removal.hash = event.transaction.hash.toHexString(); - removal.logIndex = event.transactionLogIndex.toI32(); - removal.protocol = event.address.toHexString(); - removal.account = event.params.account.toHexString(); - removal.token = event.params.token.toHexString(); - removal.season = beanstalk.lastSeason; - removal.stem = event.params.stem; - removal.amount = event.params.amount; - removal.bdv = event.params.bdv; - removal.blockNumber = event.block.number; - removal.createdAt = event.block.timestamp; - removal.save(); -} - -export function handleRemoveDeposits_V3(event: RemoveDeposits_V3): void { - let beanstalk = loadBeanstalk(event.address); // get current season - - for (let i = 0; i < event.params.stems.length; i++) { - let deposit = loadSiloDepositV3(event.params.account, event.params.token, event.params.stems[i]); - - // Update deposit - deposit.withdrawnBDV = deposit.withdrawnBDV.plus(event.params.bdvs[i]); - deposit.bdv = deposit.bdv.minus(event.params.bdvs[i]); - deposit.withdrawnAmount = deposit.withdrawnAmount.plus(event.params.amounts[i]); - deposit.amount = deposit.amount.minus(event.params.amounts[i]); - deposit.save(); - - // Update protocol totals - removeDepositFromSilo( - event.address, - beanstalk.lastSeason, - event.params.bdvs[i], - removeDepositFromSiloAsset( - event.address, - event.params.token, - beanstalk.lastSeason, - event.params.bdvs[i], - event.params.amounts[i], - event.block.timestamp, - event.block.number - ), - event.block.timestamp, - event.block.number - ); - - // Update farmer totals - removeDepositFromSilo( - event.params.account, - beanstalk.lastSeason, - event.params.bdvs[i], - removeDepositFromSiloAsset( - event.params.account, - event.params.token, - beanstalk.lastSeason, - event.params.bdvs[i], - event.params.amounts[i], - event.block.timestamp, - event.block.number - ), - event.block.timestamp, - event.block.number - ); - - let id = "removeDeposit-" + event.transaction.hash.toHexString() + "-" + event.transactionLogIndex.toString() + "-" + i.toString(); - let removal = new RemoveDepositEntity(id); - removal.hash = event.transaction.hash.toHexString(); - removal.logIndex = event.transactionLogIndex.toI32(); - removal.protocol = event.address.toHexString(); - removal.account = event.params.account.toHexString(); - removal.token = event.params.token.toHexString(); - removal.season = beanstalk.lastSeason; - removal.stem = event.params.stems[i]; - removal.amount = event.params.amounts[i]; - removal.bdv = event.params.bdvs[i]; - removal.blockNumber = event.block.number; - removal.createdAt = event.block.timestamp; - removal.save(); - } -} - -export function handleAddWithdrawal(event: AddWithdrawal): void { - let withdraw = loadSiloWithdraw(event.params.account, event.params.token, event.params.season.toI32()); - withdraw.amount = withdraw.amount.plus(event.params.amount); - let withdrawHashes = withdraw.hashes; - withdrawHashes.push(event.transaction.hash.toHexString()); - withdraw.hashes = withdrawHashes; - withdraw.createdAt = withdraw.createdAt == ZERO_BI ? event.block.timestamp : withdraw.createdAt; - withdraw.save(); - - addWithdrawToSiloAsset( - event.address, - event.params.token, - event.params.season.toI32(), - event.params.amount, - event.block.timestamp, - event.block.number - ); - addWithdrawToSiloAsset( - event.params.account, - event.params.token, - event.params.season.toI32(), - event.params.amount, - event.block.timestamp, - event.block.number - ); -} - -export function handleRemoveWithdrawal(event: RemoveWithdrawal): void { - updateClaimedWithdraw(event.params.account, event.params.token, event.params.season); -} - -export function handleRemoveWithdrawals(event: RemoveWithdrawals): void { - for (let i = 0; i < event.params.seasons.length; i++) { - updateClaimedWithdraw(event.params.account, event.params.token, event.params.seasons[i]); - } -} - -export function handleStalkBalanceChanged(event: StalkBalanceChanged): void { - // Exclude BIP-24 emission of missed past events - if (event.transaction.hash.toHexString() == "0xa89638aeb0d6c4afb4f367ea7a806a4c8b3b2a6eeac773e8cc4eda10bfa804fc") return; - - let beanstalk = loadBeanstalk(event.address); // get current season - updateStalkBalances( - event.address, - beanstalk.lastSeason, - event.params.delta, - event.params.deltaRoots, - event.block.timestamp, - event.block.number - ); - updateStalkBalances( - event.params.account, - beanstalk.lastSeason, - event.params.delta, - event.params.deltaRoots, - event.block.timestamp, - event.block.number - ); - - let id = "stalkChange-" + event.transaction.hash.toHexString() + "-" + event.transactionLogIndex.toString(); - let removal = new StalkChange(id); - removal.hash = event.transaction.hash.toHexString(); - removal.logIndex = event.transactionLogIndex.toI32(); - removal.protocol = event.address.toHexString(); - removal.account = event.params.account.toHexString(); - removal.delta = event.params.delta; - removal.season = beanstalk.lastSeason; - removal.blockNumber = event.block.number; - removal.createdAt = event.block.timestamp; - removal.save(); -} - -export function handleSeedsBalanceChanged(event: SeedsBalanceChanged): void { - // Exclude BIP-24 emission of missed past events - if (event.transaction.hash.toHexString() == "0xa89638aeb0d6c4afb4f367ea7a806a4c8b3b2a6eeac773e8cc4eda10bfa804fc") return; - - let beanstalk = loadBeanstalk(event.address); // get current season - updateSeedsBalances(event.address, beanstalk.lastSeason, event.params.delta, event.block.timestamp, event.block.number); - updateSeedsBalances(event.params.account, beanstalk.lastSeason, event.params.delta, event.block.timestamp, event.block.number); - - let id = "seedChange-" + event.transaction.hash.toHexString() + "-" + event.transactionLogIndex.toString(); - let removal = new SeedChange(id); - removal.hash = event.transaction.hash.toHexString(); - removal.logIndex = event.transactionLogIndex.toI32(); - removal.protocol = event.address.toHexString(); - removal.account = event.params.account.toHexString(); - removal.delta = event.params.delta; - removal.season = beanstalk.lastSeason; - removal.blockNumber = event.block.number; - removal.createdAt = event.block.timestamp; - removal.save(); -} - -export function handlePlant(event: Plant): void { - // This removes the plantable stalk for planted beans. - // Actual stalk credit for the farmer will be handled under the StalkBalanceChanged event. - - let beanstalk = loadBeanstalk(event.address); - let silo = loadSilo(event.address); - let siloHourly = loadSiloHourlySnapshot(event.address, beanstalk.lastSeason, event.block.timestamp); - let siloDaily = loadSiloDailySnapshot(event.address, event.block.timestamp); - let newPlantableStalk = event.params.beans.times(BigInt.fromI32(10000)); - - silo.plantableStalk = silo.plantableStalk.minus(newPlantableStalk); - silo.depositedBDV = silo.depositedBDV.minus(event.params.beans); - silo.save(); - - siloHourly.plantableStalk = silo.plantableStalk; - siloHourly.depositedBDV = silo.depositedBDV; - siloHourly.deltaPlantableStalk = siloHourly.deltaPlantableStalk.minus(newPlantableStalk); - siloHourly.deltaDepositedBDV = siloHourly.deltaDepositedBDV.minus(event.params.beans); - siloHourly.updatedAt = event.block.timestamp; - siloHourly.save(); - - siloDaily.plantableStalk = silo.plantableStalk; - siloDaily.depositedBDV = silo.depositedBDV; - siloDaily.deltaPlantableStalk = siloDaily.deltaPlantableStalk.minus(newPlantableStalk); - siloDaily.deltaDepositedBDV = siloDaily.deltaDepositedBDV.minus(event.params.beans); - siloDaily.updatedAt = event.block.timestamp; - siloDaily.save(); - - removeDepositFromSiloAsset( - event.address, - BEAN_ERC20, - beanstalk.lastSeason, - event.params.beans, - event.params.beans, - event.block.timestamp, - event.block.number - ); -} - -// These two calls are according to the Replant abi, before stems were included. -// They are not in use anymore and therefore it is unclear whether or not they are actually needed. -export function handleTransferDepositCall(call: TransferDepositCall): void { - let beanstalk = loadBeanstalk(BEANSTALK); - let updateFarmers = beanstalk.farmersToUpdate; - if (updateFarmers.indexOf(call.from.toHexString()) == -1) updateFarmers.push(call.from.toHexString()); - if (updateFarmers.indexOf(call.inputs.recipient.toHexString()) == -1) updateFarmers.push(call.inputs.recipient.toHexString()); - beanstalk.farmersToUpdate = updateFarmers; - beanstalk.save(); -} - -export function handleTransferDepositsCall(call: TransferDepositsCall): void { - let beanstalk = loadBeanstalk(BEANSTALK); - let updateFarmers = beanstalk.farmersToUpdate; - if (updateFarmers.indexOf(call.from.toHexString()) == -1) updateFarmers.push(call.from.toHexString()); - if (updateFarmers.indexOf(call.inputs.recipient.toHexString()) == -1) updateFarmers.push(call.inputs.recipient.toHexString()); - beanstalk.farmersToUpdate = updateFarmers; - beanstalk.save(); -} - -function addDepositToSilo( - account: Address, - season: i32, - bdv: BigInt, - grownStalkPerBDV: BigInt, - timestamp: BigInt, - blockNumber: BigInt -): void { - let silo = loadSilo(account); - let siloHourly = loadSiloHourlySnapshot(account, season, timestamp); - let siloDaily = loadSiloDailySnapshot(account, timestamp); - - silo.depositedBDV = silo.depositedBDV.plus(bdv); - // Individual farmer seeds cannot be directly tracked due to seed gauge - if (account == BEANSTALK) { - silo.grownStalkPerSeason = silo.grownStalkPerSeason.plus(grownStalkPerBDV); - } - silo.save(); - - siloHourly.deltaDepositedBDV = siloHourly.deltaDepositedBDV.plus(bdv); - siloHourly.depositedBDV = silo.depositedBDV; - siloHourly.grownStalkPerSeason = silo.grownStalkPerSeason; - siloHourly.updatedAt = timestamp; - siloHourly.save(); - - siloDaily.season = season; - siloDaily.deltaDepositedBDV = siloDaily.deltaDepositedBDV.plus(bdv); - siloDaily.depositedBDV = silo.depositedBDV; - siloDaily.grownStalkPerSeason = silo.grownStalkPerSeason; - siloDaily.updatedAt = timestamp; - siloDaily.save(); -} - -function removeDepositFromSilo( - account: Address, - season: i32, - bdv: BigInt, - grownStalkPerBDV: BigInt, - timestamp: BigInt, - blockNumber: BigInt -): void { - let silo = loadSilo(account); - let siloHourly = loadSiloHourlySnapshot(account, season, timestamp); - let siloDaily = loadSiloDailySnapshot(account, timestamp); - - silo.depositedBDV = silo.depositedBDV.minus(bdv); - // Individual farmer seeds cannot be directly tracked due to seed gauge - if (account == BEANSTALK) { - silo.grownStalkPerSeason = silo.grownStalkPerSeason.minus(grownStalkPerBDV); - } - silo.save(); - - siloHourly.deltaDepositedBDV = siloHourly.deltaDepositedBDV.minus(bdv); - siloHourly.depositedBDV = silo.depositedBDV; - siloHourly.grownStalkPerSeason = silo.grownStalkPerSeason; - siloHourly.updatedAt = timestamp; - siloHourly.save(); - - siloDaily.season = season; - siloDaily.deltaDepositedBDV = siloDaily.deltaDepositedBDV.minus(bdv); - siloDaily.depositedBDV = silo.depositedBDV; - siloDaily.grownStalkPerSeason = silo.grownStalkPerSeason; - siloDaily.updatedAt = timestamp; - siloDaily.save(); -} - -export function addDepositToSiloAsset( - account: Address, - token: Address, - season: i32, - bdv: BigInt, - amount: BigInt, - timestamp: BigInt, - blockNumber: BigInt -): BigInt { - let asset = loadSiloAsset(account, token); - let assetHourly = loadSiloAssetHourlySnapshot(account, token, season, timestamp); - let assetDaily = loadSiloAssetDailySnapshot(account, token, timestamp); - - let tokenSettings = loadWhitelistTokenSetting(token); - let newGrownStalk = bdv.times(tokenSettings.stalkEarnedPerSeason).div(BigInt.fromI32(1000000)); - - asset.depositedBDV = asset.depositedBDV.plus(bdv); - asset.depositedAmount = asset.depositedAmount.plus(amount); - asset.save(); - - assetHourly.deltaDepositedBDV = assetHourly.deltaDepositedBDV.plus(bdv); - assetHourly.depositedBDV = asset.depositedBDV; - assetHourly.deltaDepositedAmount = assetHourly.deltaDepositedAmount.plus(amount); - assetHourly.depositedAmount = asset.depositedAmount; - assetHourly.updatedAt = timestamp; - assetHourly.save(); - - assetDaily.season = season; - assetDaily.deltaDepositedBDV = assetDaily.deltaDepositedBDV.plus(bdv); - assetDaily.depositedBDV = asset.depositedBDV; - assetDaily.deltaDepositedAmount = assetDaily.deltaDepositedAmount.plus(amount); - assetDaily.depositedAmount = asset.depositedAmount; - assetDaily.updatedAt = timestamp; - assetDaily.save(); - - return newGrownStalk; -} - -function removeDepositFromSiloAsset( - account: Address, - token: Address, - season: i32, - bdv: BigInt, - amount: BigInt, - timestamp: BigInt, - blockNumber: BigInt -): BigInt { - let asset = loadSiloAsset(account, token); - let assetHourly = loadSiloAssetHourlySnapshot(account, token, season, timestamp); - let assetDaily = loadSiloAssetDailySnapshot(account, token, timestamp); - - let tokenSettings = loadWhitelistTokenSetting(token); - let removedGrownStalk = bdv.times(tokenSettings.stalkEarnedPerSeason).div(BigInt.fromI32(1000000)); - - asset.depositedBDV = asset.depositedBDV.minus(bdv); - asset.depositedAmount = asset.depositedAmount.minus(amount); - asset.save(); - - assetHourly.deltaDepositedBDV = assetHourly.deltaDepositedBDV.minus(bdv); - assetHourly.depositedBDV = asset.depositedBDV; - assetHourly.deltaDepositedAmount = assetHourly.deltaDepositedAmount.minus(amount); - assetHourly.depositedAmount = asset.depositedAmount; - assetHourly.updatedAt = timestamp; - assetHourly.save(); - - assetDaily.season = season; - assetDaily.deltaDepositedBDV = assetDaily.deltaDepositedBDV.minus(bdv); - assetDaily.depositedBDV = asset.depositedBDV; - assetDaily.deltaDepositedAmount = assetDaily.deltaDepositedAmount.minus(amount); - assetDaily.depositedAmount = asset.depositedAmount; - assetDaily.updatedAt = timestamp; - assetDaily.save(); - - return removedGrownStalk; -} - -function addWithdrawToSiloAsset( - account: Address, - token: Address, - season: i32, - amount: BigInt, - timestamp: BigInt, - blockNumber: BigInt -): void { - let assetHourly = loadSiloAssetHourlySnapshot(account, token, season, timestamp); - let assetDaily = loadSiloAssetDailySnapshot(account, token, timestamp); - - assetHourly.deltaWithdrawnAmount = assetHourly.deltaWithdrawnAmount.plus(amount); - assetHourly.updatedAt = timestamp; - assetHourly.save(); - - assetDaily.season = season; - assetDaily.deltaWithdrawnAmount = assetDaily.deltaWithdrawnAmount.plus(amount); - assetDaily.updatedAt = timestamp; - assetDaily.save(); -} - -export function updateStalkBalances( - account: Address, - season: i32, - stalk: BigInt, - roots: BigInt, - timestamp: BigInt, - blockNumber: BigInt -): void { - let silo = loadSilo(account); - let siloHourly = loadSiloHourlySnapshot(account, season, timestamp); - let siloDaily = loadSiloDailySnapshot(account, timestamp); - - silo.stalk = silo.stalk.plus(stalk); - silo.roots = silo.roots.plus(roots); - silo.save(); - - siloHourly.stalk = silo.stalk; - siloHourly.roots = silo.roots; - siloHourly.deltaStalk = siloHourly.deltaStalk.plus(stalk); - siloHourly.deltaRoots = siloHourly.deltaRoots.plus(roots); - siloHourly.updatedAt = timestamp; - siloHourly.save(); - - siloDaily.season = season; - siloDaily.stalk = silo.stalk; - siloDaily.roots = silo.roots; - siloDaily.deltaStalk = siloDaily.deltaStalk.plus(stalk); - siloDaily.deltaRoots = siloDaily.deltaRoots.plus(roots); - siloDaily.updatedAt = timestamp; - siloDaily.save(); - - // Add account to active list if needed - if (account !== BEANSTALK) { - let beanstalk = loadBeanstalk(BEANSTALK); - let farmerIndex = beanstalk.activeFarmers.indexOf(account.toHexString()); - if (farmerIndex == -1) { - let newFarmers = beanstalk.activeFarmers; - newFarmers.push(account.toHexString()); - beanstalk.activeFarmers = newFarmers; - beanstalk.save(); - - incrementProtocolFarmers(season, timestamp); - } else if (silo.stalk == ZERO_BI) { - let newFarmers = beanstalk.activeFarmers; - newFarmers.splice(farmerIndex, 1); - beanstalk.activeFarmers = newFarmers; - - decrementProtocolFarmers(season, timestamp); - } - } -} - -function updateSeedsBalances(account: Address, season: i32, seeds: BigInt, timestamp: BigInt, blockNumber: BigInt): void { - let silo = loadSilo(account); - let siloHourly = loadSiloHourlySnapshot(account, season, timestamp); - let siloDaily = loadSiloDailySnapshot(account, timestamp); - - silo.seeds = silo.seeds.plus(seeds); - silo.save(); - - siloHourly.seeds = silo.seeds; - siloHourly.deltaSeeds = siloHourly.deltaSeeds.plus(seeds); - siloHourly.updatedAt = timestamp; - siloHourly.save(); - - siloDaily.season = season; - siloDaily.seeds = silo.seeds; - siloDaily.deltaSeeds = siloDaily.deltaSeeds.plus(seeds); - siloDaily.updatedAt = timestamp; - siloDaily.save(); -} - -function updateClaimedWithdraw(account: Address, token: Address, season: BigInt): void { - let withdraw = loadSiloWithdraw(account, token, season.toI32()); - withdraw.claimed = true; - withdraw.save(); -} - -function incrementProtocolFarmers(season: i32, timestamp: BigInt): void { - let silo = loadSilo(BEANSTALK); - let siloHourly = loadSiloHourlySnapshot(BEANSTALK, season, timestamp); - let siloDaily = loadSiloDailySnapshot(BEANSTALK, timestamp); - - silo.activeFarmers += 1; - siloHourly.activeFarmers += 1; - siloHourly.deltaActiveFarmers += 1; - siloDaily.activeFarmers += 1; - siloDaily.deltaActiveFarmers += 1; - silo.save(); - siloHourly.save(); - siloDaily.save(); -} - -function decrementProtocolFarmers(season: i32, timestamp: BigInt): void { - let silo = loadSilo(BEANSTALK); - let siloHourly = loadSiloHourlySnapshot(BEANSTALK, season, timestamp); - let siloDaily = loadSiloDailySnapshot(BEANSTALK, timestamp); - - silo.activeFarmers -= 1; - siloHourly.activeFarmers -= 1; - siloHourly.deltaActiveFarmers -= 1; - siloDaily.activeFarmers -= 1; - siloDaily.deltaActiveFarmers -= 1; - silo.save(); - siloHourly.save(); - siloDaily.save(); -} - -export function updateStalkWithCalls(season: i32, timestamp: BigInt, blockNumber: BigInt): void { - // This should be run at sunrise for the previous season to update any farmers stalk/seed/roots balances from silo transfers. - - let beanstalk = loadBeanstalk(BEANSTALK); - let beanstalk_call = Replanted.bind(BEANSTALK); - - for (let i = 0; i < beanstalk.farmersToUpdate.length; i++) { - let account = Address.fromString(beanstalk.farmersToUpdate[i]); - let silo = loadSilo(account); - updateStalkBalances( - account, - season, - beanstalk_call.balanceOfStalk(account).minus(silo.stalk), - beanstalk_call.balanceOfRoots(account).minus(silo.roots), - timestamp, - blockNumber - ); - // balanceOfSeeds function was removed in silov2 - updateSeedsBalances(account, season, beanstalk_call.balanceOfSeeds(account).minus(silo.seeds), timestamp, blockNumber); - } - beanstalk.farmersToUpdate = []; - beanstalk.save(); -} - -export function handleWhitelistToken(event: WhitelistToken): void { - addToSiloWhitelist(event.address, event.params.token); - - let setting = loadWhitelistTokenSetting(event.params.token); - setting.selector = event.params.selector; - setting.stalkIssuedPerBdv = BigInt.fromString("10000000000"); - setting.stalkEarnedPerSeason = event.params.stalk.times(BigInt.fromI32(1000000)); - setting.save(); - - loadWhitelistTokenHourlySnapshot(event.params.token, getCurrentSeason(event.address), event.block.timestamp); - loadWhitelistTokenDailySnapshot(event.params.token, event.block.timestamp); - - let id = "whitelistToken-" + event.transaction.hash.toHexString() + "-" + event.logIndex.toString(); - let rawEvent = new WhitelistTokenEntity(id); - rawEvent.hash = event.transaction.hash.toHexString(); - rawEvent.logIndex = event.logIndex.toI32(); - rawEvent.protocol = event.address.toHexString(); - rawEvent.token = event.params.token.toHexString(); - rawEvent.stalk = event.params.stalk; - rawEvent.seeds = event.params.seeds; - rawEvent.selector = event.params.selector.toHexString(); - rawEvent.blockNumber = event.block.number; - rawEvent.createdAt = event.block.timestamp; - rawEvent.save(); -} - -export function handleWhitelistToken_V3(event: WhitelistToken_V3): void { - addToSiloWhitelist(event.address, event.params.token); - - let setting = loadWhitelistTokenSetting(event.params.token); - setting.selector = event.params.selector; - setting.stalkIssuedPerBdv = event.params.stalk.times(BigInt.fromI32(1_000_000)); - setting.stalkEarnedPerSeason = event.params.stalkEarnedPerSeason; - setting.save(); - - loadWhitelistTokenHourlySnapshot(event.params.token, getCurrentSeason(event.address), event.block.timestamp); - loadWhitelistTokenDailySnapshot(event.params.token, event.block.timestamp); - - let id = "whitelistToken-" + event.transaction.hash.toHexString() + "-" + event.logIndex.toString(); - let rawEvent = new WhitelistTokenEntity(id); - rawEvent.hash = event.transaction.hash.toHexString(); - rawEvent.logIndex = event.logIndex.toI32(); - rawEvent.protocol = event.address.toHexString(); - rawEvent.token = event.params.token.toHexString(); - rawEvent.stalk = event.params.stalk; - rawEvent.seeds = ZERO_BI; - rawEvent.stalkPerSeason = event.params.stalkEarnedPerSeason; - rawEvent.selector = event.params.selector.toHexString(); - rawEvent.blockNumber = event.block.number; - rawEvent.createdAt = event.block.timestamp; - rawEvent.save(); -} - -export function handleDewhitelistToken(event: DewhitelistToken): void { - let silo = loadSilo(event.address); - let currentWhitelist = silo.whitelistedTokens; - let currentDewhitelist = silo.dewhitelistedTokens; - let index = currentWhitelist.indexOf(event.params.token.toHexString()); - if (index >= 0) { - currentDewhitelist.push(currentWhitelist.splice(index, 1)[0]); - silo.whitelistedTokens = currentWhitelist; - silo.dewhitelistedTokens = currentDewhitelist; - silo.save(); - } - - let id = "dewhitelistToken-" + event.transaction.hash.toHexString() + "-" + event.logIndex.toString(); - let rawEvent = new DewhitelistTokenEntity(id); - rawEvent.hash = event.transaction.hash.toHexString(); - rawEvent.logIndex = event.logIndex.toI32(); - rawEvent.protocol = event.address.toHexString(); - rawEvent.token = event.params.token.toHexString(); - rawEvent.blockNumber = event.block.number; - rawEvent.createdAt = event.block.timestamp; - rawEvent.save(); -} - -export function handleUpdatedStalkPerBdvPerSeason(event: UpdatedStalkPerBdvPerSeason): void { - let siloSettings = loadWhitelistTokenSetting(event.params.token); - siloSettings.milestoneSeason = event.params.season.toI32(); - siloSettings.stalkEarnedPerSeason = event.params.stalkEarnedPerSeason; - siloSettings.updatedAt = event.block.timestamp; - siloSettings.save(); - - let hourly = loadWhitelistTokenHourlySnapshot(event.params.token, event.params.season.toI32(), event.block.timestamp); - hourly.milestoneSeason = siloSettings.milestoneSeason; - hourly.stalkEarnedPerSeason = siloSettings.stalkEarnedPerSeason; - hourly.save(); - - let daily = loadWhitelistTokenDailySnapshot(event.params.token, event.block.timestamp); - daily.milestoneSeason = siloSettings.milestoneSeason; - daily.stalkEarnedPerSeason = siloSettings.stalkEarnedPerSeason; - daily.save(); -} diff --git a/projects/subgraph-beanstalk/src/entities/Beanstalk.ts b/projects/subgraph-beanstalk/src/entities/Beanstalk.ts new file mode 100644 index 0000000000..e1de2691d8 --- /dev/null +++ b/projects/subgraph-beanstalk/src/entities/Beanstalk.ts @@ -0,0 +1,91 @@ +import { BigInt, Address } from "@graphprotocol/graph-ts"; +import { Beanstalk } from "../../generated/schema"; +import { Farmer } from "../../generated/schema"; +import { Season } from "../../generated/schema"; +import { BI_MAX, ONE_BI, ZERO_BD, ZERO_BI } from "../../../subgraph-core/utils/Decimals"; +import { getProtocolFertilizer, getProtocolToken } from "../utils/Constants"; + +export function loadBeanstalk(protocol: Address): Beanstalk { + let beanstalk = Beanstalk.load(protocol.toHexString()); + if (beanstalk == null) { + beanstalk = new Beanstalk(protocol.toHexString()); + beanstalk.name = "Beanstalk"; + // Pre-replant token currently would not be set + beanstalk.token = getProtocolToken(protocol).toHexString(); + const fert = getProtocolFertilizer(protocol); + if (fert !== null) { + beanstalk.fertilizer1155 = fert.toHexString(); + } + beanstalk.lastSeason = 1; + beanstalk.activeFarmers = []; + beanstalk.farmersToUpdate = []; + beanstalk.save(); + } + return beanstalk as Beanstalk; +} + +export function loadFarmer(account: Address): Farmer { + let farmer = Farmer.load(account.toHexString()); + if (farmer == null) { + farmer = new Farmer(account.toHexString()); + farmer.save(); + } + return farmer; +} + +export function loadSeason(diamondAddress: Address, id: BigInt): Season { + let season = Season.load(id.toString()); + if (season == null) { + season = new Season(id.toString()); + season.beanstalk = diamondAddress.toHexString(); + season.season = id.toI32(); + season.sunriseBlock = ZERO_BI; + season.createdAt = ZERO_BI; + season.price = ZERO_BD; + season.beans = ZERO_BI; + season.marketCap = ZERO_BD; + season.deltaB = ZERO_BI; + season.deltaBeans = ZERO_BI; + season.rewardBeans = ZERO_BI; + season.incentiveBeans = ZERO_BI; + season.harvestableIndex = ZERO_BI; + season.save(); + if (id > ZERO_BI) { + let lastSeason = loadSeason(diamondAddress, id.minus(ONE_BI)); + season.beans = lastSeason.beans; + season.harvestableIndex = lastSeason.harvestableIndex; + season.save(); + } + + // Update beanstalk season + let beanstalk = loadBeanstalk(diamondAddress); + beanstalk.lastSeason = season.season; + beanstalk.save(); + } + return season; +} + +export function getBeanstalkToken(protocol: Address): Address { + let beanstalkEntity = loadBeanstalk(protocol); + return Address.fromString(beanstalkEntity.token); +} + +export function getCurrentSeason(protocol: Address): i32 { + let beanstalkEntity = loadBeanstalk(protocol); + return beanstalkEntity.lastSeason; +} + +// Returns the number of reward beans minted for the requested season +export function getRewardMinted(season: i32): BigInt { + const snapshot = Season.load(season.toString()); + if (snapshot == null) { + return ZERO_BI; + } + return snapshot.rewardBeans; +} + +export function getHarvestableIndex(protocol: Address): BigInt { + let bs = loadBeanstalk(protocol); + let season = loadSeason(protocol, BigInt.fromI32(bs.lastSeason)); + return season.harvestableIndex; +} diff --git a/projects/subgraph-beanstalk/src/utils/Fertilizer.ts b/projects/subgraph-beanstalk/src/entities/Fertilizer.ts similarity index 51% rename from projects/subgraph-beanstalk/src/utils/Fertilizer.ts rename to projects/subgraph-beanstalk/src/entities/Fertilizer.ts index 9e3df51e65..20445b0754 100644 --- a/projects/subgraph-beanstalk/src/utils/Fertilizer.ts +++ b/projects/subgraph-beanstalk/src/entities/Fertilizer.ts @@ -1,13 +1,15 @@ import { Address, BigDecimal, BigInt } from "@graphprotocol/graph-ts"; -import { Farmer, Fertilizer, FertilizerBalance, FertilizerToken } from "../../generated/schema"; -import { ZERO_BI } from "../../../subgraph-core/utils/Decimals"; +import { Farmer, Fertilizer, FertilizerBalance, FertilizerToken, FertilizerYield } from "../../generated/schema"; +import { ZERO_BD, ZERO_BI } from "../../../subgraph-core/utils/Decimals"; import { BEANSTALK } from "../../../subgraph-core/utils/Constants"; -import { MarketV2 } from "../../generated/Beanstalk-ABIs/MarketV2"; +import { SeedGauge } from "../../generated/Beanstalk-ABIs/SeedGauge"; +import { getFertilizerProtocol } from "../utils/Constants"; export function loadFertilizer(fertilizerAddress: Address): Fertilizer { let fertilizer = Fertilizer.load(fertilizerAddress.toHexString()); if (fertilizer == null) { fertilizer = new Fertilizer(fertilizerAddress.toHexString()); + fertilizer.beanstalk = getFertilizerProtocol(fertilizerAddress).toHexString(); fertilizer.supply = ZERO_BI; fertilizer.save(); } @@ -17,13 +19,13 @@ export function loadFertilizer(fertilizerAddress: Address): Fertilizer { export function loadFertilizerToken(fertilizer: Fertilizer, id: BigInt, blockNumber: BigInt): FertilizerToken { let fertilizerToken = FertilizerToken.load(id.toString()); if (fertilizerToken == null) { - let beanstalk = MarketV2.bind(BEANSTALK); + const beanstalkContract = SeedGauge.bind(Address.fromString(fertilizer.beanstalk)); fertilizerToken = new FertilizerToken(id.toString()); fertilizerToken.fertilizer = fertilizer.id; if (blockNumber.gt(BigInt.fromString("15278963"))) { - fertilizerToken.humidity = BigDecimal.fromString(beanstalk.getCurrentHumidity().toString()).div(BigDecimal.fromString("10")); - fertilizerToken.season = beanstalk.season().toI32(); - fertilizerToken.startBpf = beanstalk.beansPerFertilizer(); + fertilizerToken.humidity = BigDecimal.fromString(beanstalkContract.getCurrentHumidity().toString()).div(BigDecimal.fromString("10")); + fertilizerToken.season = beanstalkContract.season().toI32(); + fertilizerToken.startBpf = beanstalkContract.beansPerFertilizer(); } else { fertilizerToken.humidity = BigDecimal.fromString("500"); fertilizerToken.season = 6074; @@ -48,3 +50,28 @@ export function loadFertilizerBalance(fertilizerToken: FertilizerToken, farmer: } return fertilizerBalance; } + +export function loadFertilizerYield(season: i32, window: i32): FertilizerYield { + let fertilizerYield = FertilizerYield.load(season.toString() + "-" + window.toString()); + if (fertilizerYield == null) { + fertilizerYield = new FertilizerYield(season.toString() + "-" + window.toString()); + fertilizerYield.season = season; + fertilizerYield.humidity = ZERO_BD; + fertilizerYield.outstandingFert = ZERO_BI; + fertilizerYield.beansPerSeasonEMA = ZERO_BD; + fertilizerYield.deltaBpf = ZERO_BD; + fertilizerYield.simpleAPY = ZERO_BD; + fertilizerYield.createdAt = ZERO_BI; + + if (window == 24) { + fertilizerYield.emaWindow = "ROLLING_24_HOUR"; + } else if (window == 168) { + fertilizerYield.emaWindow = "ROLLING_7_DAY"; + } else if (window == 720) { + fertilizerYield.emaWindow = "ROLLING_30_DAY"; + } + + fertilizerYield.save(); + } + return fertilizerYield as FertilizerYield; +} diff --git a/projects/subgraph-beanstalk/src/entities/Field.ts b/projects/subgraph-beanstalk/src/entities/Field.ts new file mode 100644 index 0000000000..4bc661c8c9 --- /dev/null +++ b/projects/subgraph-beanstalk/src/entities/Field.ts @@ -0,0 +1,59 @@ +import { Address, BigInt } from "@graphprotocol/graph-ts"; +import { Field, Plot } from "../../generated/schema"; +import { ZERO_BD, ZERO_BI } from "../../../subgraph-core/utils/Decimals"; +import { ADDRESS_ZERO, BEANSTALK, CURVE_PRICE } from "../../../subgraph-core/utils/Constants"; +import { loadBeanstalk, loadSeason } from "./Beanstalk"; + +export function loadField(account: Address): Field { + let field = Field.load(account.toHexString()); + if (field == null) { + field = new Field(account.toHexString()); + field.beanstalk = BEANSTALK.toHexString(); + if (account !== BEANSTALK) { + field.farmer = account.toHexString(); + } + field.season = 1; + field.temperature = 1; + field.realRateOfReturn = ZERO_BD; + field.numberOfSowers = 0; + field.numberOfSows = 0; + field.sownBeans = ZERO_BI; + field.plotIndexes = []; + field.unharvestablePods = ZERO_BI; + field.harvestablePods = ZERO_BI; + field.harvestedPods = ZERO_BI; + field.soil = ZERO_BI; + field.podIndex = ZERO_BI; + field.podRate = ZERO_BD; + field.save(); + } + return field; +} + +export function loadPlot(diamondAddress: Address, index: BigInt): Plot { + let plot = Plot.load(index.toString()); + if (plot == null) { + plot = new Plot(index.toString()); + plot.field = diamondAddress.toHexString(); + plot.farmer = ADDRESS_ZERO.toHexString(); + plot.source = "SOW"; // Should be overwritten in case of a transfer creating a new plot + plot.sourceHash = ""; + plot.season = 0; + plot.creationHash = ""; + plot.createdAt = ZERO_BI; + plot.updatedAt = ZERO_BI; + plot.updatedAtBlock = ZERO_BI; + plot.index = index; + plot.pods = ZERO_BI; + plot.beansPerPod = ZERO_BI; + plot.harvestablePods = ZERO_BI; + plot.harvestedPods = ZERO_BI; + plot.fullyHarvested = false; + plot.save(); + + let field = loadField(diamondAddress); + field.plotIndexes.push(plot.index); + field.save(); + } + return plot; +} diff --git a/projects/subgraph-beanstalk/src/entities/Germinating.ts b/projects/subgraph-beanstalk/src/entities/Germinating.ts new file mode 100644 index 0000000000..ddb2c0b490 --- /dev/null +++ b/projects/subgraph-beanstalk/src/entities/Germinating.ts @@ -0,0 +1,82 @@ +import { Address, BigDecimal, BigInt, ethereum, store } from "@graphprotocol/graph-ts"; +import { ONE_BI, toDecimal, ZERO_BD, ZERO_BI } from "../../../subgraph-core/utils/Decimals"; +import { Germinating, PrevFarmerGerminatingEvent } from "../../generated/schema"; + +export function loadOrCreateGerminating(address: Address, season: i32, isFarmer: boolean): Germinating { + const type = germinationSeasonCategory(season); + const id = address.toHexString() + "-" + type; + let germinating = Germinating.load(id); + if (germinating == null) { + germinating = new Germinating(id); + germinating.address = address.toHexString(); + germinating.type = type; + germinating.isFarmer = isFarmer; + germinating.season = season; + germinating.stalk = ZERO_BI; + germinating.tokenAmount = ZERO_BI; + germinating.bdv = ZERO_BI; + germinating.save(); + } + return germinating as Germinating; +} + +export function loadGerminating(address: Address, enumValue: i32): Germinating { + const id = address.toHexString() + "-" + germinationEnumCategory(enumValue); + let germinating = Germinating.load(id); + return germinating as Germinating; +} + +export function tryLoadBothGerminating(address: Address): Array { + return [Germinating.load(address.toHexString() + "-ODD"), Germinating.load(address.toHexString() + "-EVEN")]; +} + +export function getGerminatingBdvs(address: Address): Array { + const germinatingState = tryLoadBothGerminating(address); + return [ + germinatingState[0] !== null ? toDecimal(germinatingState[0]!.bdv) : ZERO_BD, + germinatingState[1] !== null ? toDecimal(germinatingState[1]!.bdv) : ZERO_BD + ]; +} + +export function deleteGerminating(germinating: Germinating): void { + store.remove("Germinating", germinating.id); +} + +// This is the entity that exists to resolve the issue in LibGerminate when deposits from multiple seasons +// complete their germination (the event emission itself has a bug) +export function loadPrevFarmerGerminatingEvent(account: Address): PrevFarmerGerminatingEvent { + let savedEvent = PrevFarmerGerminatingEvent.load(account); + if (savedEvent == null) { + savedEvent = new PrevFarmerGerminatingEvent(account); + savedEvent.eventBlock = ZERO_BI; + savedEvent.logIndex = ZERO_BI; + savedEvent.deltaGerminatingStalk = ZERO_BI; + // No point in saving it + } + return savedEvent as PrevFarmerGerminatingEvent; +} + +export function savePrevFarmerGerminatingEvent(account: Address, event: ethereum.Event, deltaGerminatingStalk: BigInt): void { + const savedEvent = new PrevFarmerGerminatingEvent(account); + savedEvent.eventBlock = event.block.number; + savedEvent.logIndex = event.logIndex; + savedEvent.deltaGerminatingStalk = deltaGerminatingStalk; + savedEvent.save(); +} + +// Returns the stalk offset that should be applied to the encountered FarmerGerminatingStalkBalanceChanged event. +export function getFarmerGerminatingBugOffset(account: Address, event: ethereum.Event): BigInt { + const prevEvent = loadPrevFarmerGerminatingEvent(account); + if (prevEvent.eventBlock == event.block.number && prevEvent.logIndex == event.logIndex.minus(ONE_BI)) { + return prevEvent.deltaGerminatingStalk.neg(); + } + return ZERO_BI; +} + +export function germinationSeasonCategory(season: i32): string { + return season % 2 == 0 ? "EVEN" : "ODD"; +} + +export function germinationEnumCategory(enumValue: i32): string { + return enumValue == 0 ? "ODD" : "EVEN"; +} diff --git a/projects/subgraph-beanstalk/src/entities/PodMarketplace.ts b/projects/subgraph-beanstalk/src/entities/PodMarketplace.ts new file mode 100644 index 0000000000..96c1c803cd --- /dev/null +++ b/projects/subgraph-beanstalk/src/entities/PodMarketplace.ts @@ -0,0 +1,165 @@ +import { Address, BigInt, Bytes, log } from "@graphprotocol/graph-ts"; +import { loadField } from "./Field"; +import { PodFill, PodListing, PodMarketplace, PodOrder } from "../../generated/schema"; +import { ZERO_BI } from "../../../subgraph-core/utils/Decimals"; +import { BEANSTALK } from "../../../subgraph-core/utils/Constants"; + +export function loadPodMarketplace(protocol: Address): PodMarketplace { + let marketplace = PodMarketplace.load(protocol.toHexString()); + if (marketplace == null) { + let field = loadField(protocol); + marketplace = new PodMarketplace(protocol.toHexString()); + marketplace.season = field.season; + marketplace.activeListings = []; + marketplace.activeOrders = []; + marketplace.listedPods = ZERO_BI; + marketplace.availableListedPods = ZERO_BI; + marketplace.filledListedPods = ZERO_BI; + marketplace.expiredListedPods = ZERO_BI; + marketplace.cancelledListedPods = ZERO_BI; + marketplace.orderBeans = ZERO_BI; + marketplace.availableOrderBeans = ZERO_BI; + marketplace.filledOrderedPods = ZERO_BI; + marketplace.filledOrderBeans = ZERO_BI; + marketplace.cancelledOrderBeans = ZERO_BI; + marketplace.podVolume = ZERO_BI; + marketplace.beanVolume = ZERO_BI; + marketplace.save(); + } + return marketplace; +} + +export function loadPodFill(protocol: Address, index: BigInt, hash: String): PodFill { + let id = protocol.toHexString() + "-" + index.toString() + "-" + hash; + let fill = PodFill.load(id); + if (fill == null) { + fill = new PodFill(id); + fill.podMarketplace = protocol.toHexString(); + fill.createdAt = ZERO_BI; + fill.fromFarmer = ""; + fill.toFarmer = ""; + fill.placeInLine = ZERO_BI; + fill.amount = ZERO_BI; + fill.index = ZERO_BI; + fill.start = ZERO_BI; + fill.costInBeans = ZERO_BI; + fill.save(); + } + return fill; +} + +export function loadPodListing(account: Address, index: BigInt): PodListing { + let id = account.toHexString() + "-" + index.toString(); + let listing = PodListing.load(id); + if (listing == null) { + listing = new PodListing(id); + listing.podMarketplace = BEANSTALK.toHexString(); + listing.historyID = ""; + listing.plot = index.toString(); + listing.farmer = account.toHexString(); + listing.index = index; + listing.start = ZERO_BI; + listing.mode = 0; + listing.maxHarvestableIndex = ZERO_BI; + listing.minFillAmount = ZERO_BI; + listing.pricePerPod = 0; + listing.originalIndex = index; + listing.originalAmount = ZERO_BI; + listing.filled = ZERO_BI; + listing.amount = ZERO_BI; + listing.remainingAmount = ZERO_BI; + listing.filledAmount = ZERO_BI; + listing.status = "ACTIVE"; + listing.createdAt = ZERO_BI; + listing.creationHash = ""; + listing.updatedAt = ZERO_BI; + listing.save(); + } + return listing; +} + +export function createHistoricalPodListing(listing: PodListing): void { + let created = false; + let id = listing.id; + for (let i = 0; !created; i++) { + id = listing.id + "-" + i.toString(); + let newListing = PodListing.load(id); + if (newListing == null) { + newListing = new PodListing(id); + newListing.podMarketplace = listing.podMarketplace; + newListing.historyID = listing.historyID; + newListing.plot = listing.plot; + newListing.farmer = listing.farmer; + newListing.index = listing.index; + newListing.start = listing.start; + newListing.mode = listing.mode; + newListing.maxHarvestableIndex = listing.maxHarvestableIndex; + newListing.minFillAmount = listing.minFillAmount; + newListing.pricePerPod = listing.pricePerPod; + newListing.originalIndex = listing.originalIndex; + newListing.originalAmount = listing.originalAmount; + newListing.filled = listing.filled; + newListing.amount = listing.amount; + newListing.remainingAmount = listing.remainingAmount; + newListing.filledAmount = listing.filledAmount; + newListing.fill = listing.fill; + newListing.status = listing.status; + newListing.createdAt = listing.createdAt; + newListing.updatedAt = listing.updatedAt; + newListing.creationHash = listing.creationHash; + newListing.save(); + created = true; + } + } +} + +export function loadPodOrder(orderID: Bytes): PodOrder { + let order = PodOrder.load(orderID.toHexString()); + if (order == null) { + order = new PodOrder(orderID.toHexString()); + order.podMarketplace = BEANSTALK.toHexString(); + order.historyID = ""; + order.farmer = ""; + order.createdAt = ZERO_BI; + order.updatedAt = ZERO_BI; + order.status = ""; + order.beanAmount = ZERO_BI; + order.podAmountFilled = ZERO_BI; + order.beanAmountFilled = ZERO_BI; + order.minFillAmount = ZERO_BI; + order.maxPlaceInLine = ZERO_BI; + order.pricePerPod = 0; + order.creationHash = ""; + order.fills = []; + order.save(); + } + return order; +} + +export function createHistoricalPodOrder(order: PodOrder): void { + let created = false; + let id = order.id; + for (let i = 0; !created; i++) { + id = order.id + "-" + i.toString(); + let newOrder = PodOrder.load(id); + if (newOrder == null) { + newOrder = new PodOrder(id); + newOrder.podMarketplace = order.podMarketplace; + newOrder.historyID = order.historyID; + newOrder.farmer = order.farmer; + newOrder.createdAt = order.createdAt; + newOrder.updatedAt = order.updatedAt; + newOrder.status = order.status; + newOrder.beanAmount = order.beanAmount; + newOrder.podAmountFilled = order.podAmountFilled; + newOrder.beanAmountFilled = order.beanAmountFilled; + newOrder.minFillAmount = order.minFillAmount; + newOrder.maxPlaceInLine = order.maxPlaceInLine; + newOrder.pricePerPod = order.pricePerPod; + newOrder.creationHash = order.creationHash; + newOrder.fills = order.fills; + newOrder.save(); + created = true; + } + } +} diff --git a/projects/subgraph-beanstalk/src/entities/Silo.ts b/projects/subgraph-beanstalk/src/entities/Silo.ts new file mode 100644 index 0000000000..e53dd4769c --- /dev/null +++ b/projects/subgraph-beanstalk/src/entities/Silo.ts @@ -0,0 +1,240 @@ +import { Address, BigInt, Bytes, ethereum, store, log } from "@graphprotocol/graph-ts"; +import { + Silo, + SiloDeposit, + SiloWithdraw, + SiloYield, + SiloAsset, + WhitelistTokenSetting, + TokenYield, + UnripeToken +} from "../../generated/schema"; +import { BEANSTALK, UNRIPE_BEAN, UNRIPE_LP } from "../../../subgraph-core/utils/Constants"; +import { ZERO_BD, ZERO_BI } from "../../../subgraph-core/utils/Decimals"; +import { getTokenDecimals, getUnripeUnderlying } from "../utils/Constants"; + +/* ===== Base Silo Entities ===== */ + +export function loadSilo(account: Address): Silo { + let silo = Silo.load(account.toHexString()); + if (silo == null) { + silo = new Silo(account.toHexString()); + silo.beanstalk = BEANSTALK.toHexString(); + if (account !== BEANSTALK) { + silo.farmer = account.toHexString(); + } + silo.whitelistedTokens = []; + silo.dewhitelistedTokens = []; + silo.depositedBDV = ZERO_BI; + silo.stalk = ZERO_BI; + silo.plantableStalk = ZERO_BI; + silo.seeds = ZERO_BI; + silo.grownStalkPerSeason = ZERO_BI; + silo.roots = ZERO_BI; + silo.germinatingStalk = ZERO_BI; + silo.beanMints = ZERO_BI; + silo.activeFarmers = 0; + silo.save(); + } + return silo as Silo; +} + +/* ===== Asset Entities ===== */ + +export function loadSiloAsset(account: Address, token: Address): SiloAsset { + let id = account.toHexString() + "-" + token.toHexString(); + let asset = SiloAsset.load(id); + + if (asset == null) { + asset = new SiloAsset(id); + asset.silo = account.toHexString(); + asset.token = token.toHexString(); + asset.depositedBDV = ZERO_BI; + asset.depositedAmount = ZERO_BI; + asset.withdrawnAmount = ZERO_BI; + asset.farmAmount = ZERO_BI; + asset.save(); + } + return asset as SiloAsset; +} + +/* ===== Whitelist Token Settings Entities ===== */ + +export function addToSiloWhitelist(siloAddress: Address, token: Address): void { + let silo = loadSilo(siloAddress); + let currentList = silo.whitelistedTokens; + currentList.push(token.toHexString()); + silo.whitelistedTokens = currentList; + silo.save(); +} + +export function loadWhitelistTokenSetting(token: Address): WhitelistTokenSetting { + let setting = WhitelistTokenSetting.load(token); + if (setting == null) { + setting = new WhitelistTokenSetting(token); + setting.selector = Bytes.empty(); + setting.stalkEarnedPerSeason = ZERO_BI; + setting.stalkIssuedPerBdv = ZERO_BI; + setting.milestoneSeason = 0; + setting.decimals = getTokenDecimals(token); + setting.updatedAt = ZERO_BI; + + // Check token addresses and set replant seeds/stalk for Unripe due to event timing. + if (token == UNRIPE_BEAN) { + setting.stalkIssuedPerBdv = BigInt.fromString("10000000000"); + setting.stalkEarnedPerSeason = BigInt.fromI32(2000000); + } else if (token == UNRIPE_LP) { + setting.stalkIssuedPerBdv = BigInt.fromString("10000000000"); + setting.stalkEarnedPerSeason = BigInt.fromI32(4000000); + } + setting.save(); + } + return setting as WhitelistTokenSetting; +} + +/* ===== Unripe Entities ===== */ + +export function loadUnripeToken(token: Address): UnripeToken { + let unripe = UnripeToken.load(token); + if (unripe == null) { + unripe = new UnripeToken(token); + unripe.underlyingToken = getUnripeUnderlying(token, ZERO_BI); + unripe.totalUnderlying = ZERO_BI; + unripe.amountUnderlyingOne = ZERO_BI; + unripe.bdvUnderlyingOne = ZERO_BI; + unripe.choppableAmountOne = ZERO_BI; + unripe.choppableBdvOne = ZERO_BI; + unripe.chopRate = ZERO_BD; + unripe.recapPercent = ZERO_BD; + unripe.totalChoppedAmount = ZERO_BI; + unripe.totalChoppedBdv = ZERO_BI; + unripe.totalChoppedBdvReceived = ZERO_BI; + unripe.save(); + } + return unripe as UnripeToken; +} + +/* ===== Deposit Entities ===== */ + +class SiloDepositID { + account: Address; + token: Address; + depositVersion: String; + season: BigInt | null; + stem: BigInt | null; +} + +export function loadSiloDeposit(depositId: SiloDepositID): SiloDeposit { + // id: Account - Token Address - Deposit Version - (Season|Stem) + const seasonOrStem = depositId.depositVersion == "season" ? depositId.season! : depositId.stem!; + const id = + depositId.account.toHexString() + "-" + depositId.token.toHexString() + "-" + depositId.depositVersion + "-" + seasonOrStem.toString(); + let deposit = SiloDeposit.load(id); + if (deposit == null) { + deposit = new SiloDeposit(id); + deposit.farmer = depositId.account.toHexString(); + deposit.token = depositId.token.toHexString(); + deposit.depositVersion = depositId.depositVersion.toString(); + if (depositId.season !== null) { + deposit.season = depositId.season!.toU32(); + } + deposit.stem = depositId.stem; + deposit.stemV31 = ZERO_BI; + deposit.depositedAmount = ZERO_BI; + deposit.depositedBDV = ZERO_BI; + deposit.hashes = []; + deposit.createdBlock = ZERO_BI; + deposit.updatedBlock = ZERO_BI; + deposit.createdAt = ZERO_BI; + deposit.updatedAt = ZERO_BI; + deposit.save(); + } + return deposit; +} + +// Updates the given SiloDeposit with new amounts/bdv. If the deposit was fully withdrawn, delete the SiloDeposit. +export function updateDeposit(deposit: SiloDeposit, deltaAmount: BigInt, deltaBdv: BigInt, event: ethereum.Event): SiloDeposit | null { + deposit.depositedAmount = deposit.depositedAmount.plus(deltaAmount); + if (deposit.depositedAmount <= ZERO_BI) { + store.remove("SiloDeposit", deposit.id); + return null; + } + deposit.depositedBDV = deposit.depositedBDV.plus(deltaBdv); + let depositHashes = deposit.hashes; + depositHashes.push(event.transaction.hash.toHexString()); + deposit.hashes = depositHashes; + deposit.createdBlock = deposit.createdBlock == ZERO_BI ? event.block.number : deposit.createdBlock; + deposit.createdAt = deposit.createdAt == ZERO_BI ? event.block.timestamp : deposit.createdAt; + deposit.updatedBlock = event.block.number; + deposit.updatedAt = event.block.timestamp; + return deposit; +} + +/* ===== Withdraw Entities ===== */ + +export function loadSiloWithdraw(account: Address, token: Address, season: i32): SiloWithdraw { + let id = account.toHexString() + "-" + token.toHexString() + "-" + season.toString(); + let withdraw = SiloWithdraw.load(id); + if (withdraw == null) { + withdraw = new SiloWithdraw(id); + withdraw.farmer = account.toHexString(); + withdraw.token = token.toHexString(); + withdraw.withdrawSeason = season; + withdraw.claimableSeason = season + 1; + withdraw.claimed = false; + withdraw.amount = ZERO_BI; + withdraw.createdAt = ZERO_BI; + withdraw.save(); + } + return withdraw as SiloWithdraw; +} + +/* ===== Yield Entities ===== */ + +export function loadSiloYield(season: i32, window: i32): SiloYield { + let siloYield = SiloYield.load(season.toString() + "-" + window.toString()); + if (siloYield == null) { + siloYield = new SiloYield(season.toString() + "-" + window.toString()); + siloYield.season = season; + siloYield.beta = ZERO_BD; + siloYield.u = 0; + siloYield.beansPerSeasonEMA = ZERO_BD; + siloYield.whitelistedTokens = []; + siloYield.createdAt = ZERO_BI; + + if (window == 24) { + siloYield.emaWindow = "ROLLING_24_HOUR"; + } else if (window == 168) { + siloYield.emaWindow = "ROLLING_7_DAY"; + } else if (window == 720) { + siloYield.emaWindow = "ROLLING_30_DAY"; + } + siloYield.save(); + } + return siloYield as SiloYield; +} + +export function loadTokenYield(token: Address, season: i32, window: i32): TokenYield { + let id = token.concatI32(season).concatI32(window); + let tokenYield = TokenYield.load(id); + if (tokenYield == null) { + tokenYield = new TokenYield(id); + tokenYield.token = token; + tokenYield.season = season; + tokenYield.siloYield = season.toString() + "-" + window.toString(); + tokenYield.beanAPY = ZERO_BD; + tokenYield.stalkAPY = ZERO_BD; + tokenYield.createdAt = ZERO_BI; + tokenYield.save(); + } + return tokenYield as TokenYield; +} + +export function SiloAsset_findIndex_token(a: SiloAsset[], targetToken: string): i32 { + for (let j = 0; j < a.length; j++) { + if (a[j].token == targetToken) { + return j; + } + } + return -1; +} diff --git a/projects/subgraph-beanstalk/src/entities/snapshots/Field.ts b/projects/subgraph-beanstalk/src/entities/snapshots/Field.ts new file mode 100644 index 0000000000..d33671c2ed --- /dev/null +++ b/projects/subgraph-beanstalk/src/entities/snapshots/Field.ts @@ -0,0 +1,197 @@ +import { BigInt, Address, log } from "@graphprotocol/graph-ts"; +import { Field, FieldDailySnapshot, FieldHourlySnapshot } from "../../../generated/schema"; +import { getCurrentSeason } from "../Beanstalk"; +import { dayFromTimestamp, hourFromTimestamp } from "../../../../subgraph-core/utils/Dates"; + +export function takeFieldSnapshots(field: Field, protocol: Address, timestamp: BigInt, blockNumber: BigInt): void { + const currentSeason = getCurrentSeason(protocol); + + const hour = BigInt.fromI32(hourFromTimestamp(timestamp)); + const day = BigInt.fromI32(dayFromTimestamp(timestamp)); + + // Load the snapshot for this season/day + const hourlyId = field.id + "-" + currentSeason.toString(); + const dailyId = field.id + "-" + day.toString(); + let baseHourly = FieldHourlySnapshot.load(hourlyId); + let baseDaily = FieldDailySnapshot.load(dailyId); + if (baseHourly == null && field.lastHourlySnapshotSeason !== 0) { + baseHourly = FieldHourlySnapshot.load(field.id + "-" + field.lastHourlySnapshotSeason.toString()); + } + if (baseDaily == null && field.lastDailySnapshotDay !== null) { + baseDaily = FieldDailySnapshot.load(field.id + "-" + field.lastDailySnapshotDay!.toString()); + } + const hourly = new FieldHourlySnapshot(hourlyId); + const daily = new FieldDailySnapshot(dailyId); + + // Set current values + hourly.season = currentSeason; + hourly.field = field.id; + hourly.temperature = field.temperature; + hourly.realRateOfReturn = field.realRateOfReturn; + hourly.numberOfSowers = field.numberOfSowers; + hourly.numberOfSows = field.numberOfSows; + hourly.sownBeans = field.sownBeans; + hourly.unharvestablePods = field.unharvestablePods; + hourly.harvestablePods = field.harvestablePods; + hourly.harvestedPods = field.harvestedPods; + hourly.soil = field.soil; + // issuedSoil set below, on initial snapshot + hourly.podIndex = field.podIndex; + hourly.podRate = field.podRate; + + // Set deltas + if (baseHourly !== null) { + hourly.deltaTemperature = hourly.temperature - baseHourly.temperature; + hourly.deltaRealRateOfReturn = hourly.realRateOfReturn.minus(baseHourly.realRateOfReturn); + hourly.deltaNumberOfSowers = hourly.numberOfSowers - baseHourly.numberOfSowers; + hourly.deltaNumberOfSows = hourly.numberOfSows - baseHourly.numberOfSows; + hourly.deltaSownBeans = hourly.sownBeans.minus(baseHourly.sownBeans); + hourly.deltaUnharvestablePods = hourly.unharvestablePods.minus(baseHourly.unharvestablePods); + hourly.deltaHarvestablePods = hourly.harvestablePods.minus(baseHourly.harvestablePods); + hourly.deltaHarvestedPods = hourly.harvestedPods.minus(baseHourly.harvestedPods); + hourly.deltaSoil = hourly.soil.minus(baseHourly.soil); + // deltaIssuedSoil set below, on initial snapshot + hourly.deltaPodIndex = hourly.podIndex.minus(baseHourly.podIndex); + hourly.deltaPodRate = hourly.podRate.minus(baseHourly.podRate); + + if (hourly.id == baseHourly.id) { + // Add existing deltas + hourly.deltaTemperature = hourly.deltaTemperature + baseHourly.deltaTemperature; + hourly.deltaRealRateOfReturn = hourly.deltaRealRateOfReturn.plus(baseHourly.deltaRealRateOfReturn); + hourly.deltaNumberOfSowers = hourly.deltaNumberOfSowers + baseHourly.deltaNumberOfSowers; + hourly.deltaNumberOfSows = hourly.deltaNumberOfSows + baseHourly.deltaNumberOfSows; + hourly.deltaSownBeans = hourly.deltaSownBeans.plus(baseHourly.deltaSownBeans); + hourly.deltaUnharvestablePods = hourly.deltaUnharvestablePods.plus(baseHourly.deltaUnharvestablePods); + hourly.deltaHarvestablePods = hourly.deltaHarvestablePods.plus(baseHourly.deltaHarvestablePods); + hourly.deltaHarvestedPods = hourly.deltaHarvestedPods.plus(baseHourly.deltaHarvestedPods); + hourly.deltaSoil = hourly.deltaSoil.plus(baseHourly.deltaSoil); + hourly.deltaPodIndex = hourly.deltaPodIndex.plus(baseHourly.deltaPodIndex); + hourly.deltaPodRate = hourly.deltaPodRate.plus(baseHourly.deltaPodRate); + // Carry over unset values that would otherwise get erased + hourly.issuedSoil = baseHourly.issuedSoil; + hourly.deltaIssuedSoil = baseHourly.deltaIssuedSoil; + hourly.seasonBlock = baseHourly.seasonBlock; + hourly.caseId = baseHourly.caseId; + hourly.soilSoldOut = baseHourly.soilSoldOut; + hourly.blocksToSoldOutSoil = baseHourly.blocksToSoldOutSoil; + } else { + // Sets initial creation values + hourly.issuedSoil = field.soil; + hourly.deltaIssuedSoil = field.soil.minus(baseHourly.issuedSoil); + hourly.seasonBlock = blockNumber; + hourly.soilSoldOut = false; + } + } else { + hourly.deltaTemperature = hourly.temperature; + hourly.deltaRealRateOfReturn = hourly.realRateOfReturn; + hourly.deltaNumberOfSowers = hourly.numberOfSowers; + hourly.deltaNumberOfSows = hourly.numberOfSows; + hourly.deltaSownBeans = hourly.sownBeans; + hourly.deltaUnharvestablePods = hourly.unharvestablePods; + hourly.deltaHarvestablePods = hourly.harvestablePods; + hourly.deltaHarvestedPods = hourly.harvestedPods; + hourly.deltaSoil = hourly.soil; + hourly.deltaPodIndex = hourly.podIndex; + hourly.deltaPodRate = hourly.podRate; + + // Sets initial creation values + hourly.issuedSoil = field.soil; + hourly.deltaIssuedSoil = field.soil; + hourly.seasonBlock = blockNumber; + hourly.soilSoldOut = false; + } + hourly.createdAt = hour; + hourly.updatedAt = timestamp; + hourly.save(); + + // Repeat for daily snapshot. + // Duplicate code is preferred to type coercion, the codegen doesnt provide a common interface. + + daily.season = currentSeason; + daily.field = field.id; + daily.temperature = field.temperature; + daily.realRateOfReturn = field.realRateOfReturn; + daily.numberOfSowers = field.numberOfSowers; + daily.numberOfSows = field.numberOfSows; + daily.sownBeans = field.sownBeans; + daily.unharvestablePods = field.unharvestablePods; + daily.harvestablePods = field.harvestablePods; + daily.harvestedPods = field.harvestedPods; + daily.soil = field.soil; + // issuedSoil set below, on initial snapshot + daily.podIndex = field.podIndex; + daily.podRate = field.podRate; + if (baseDaily !== null) { + daily.deltaTemperature = daily.temperature - baseDaily.temperature; + daily.deltaRealRateOfReturn = daily.realRateOfReturn.minus(baseDaily.realRateOfReturn); + daily.deltaNumberOfSowers = daily.numberOfSowers - baseDaily.numberOfSowers; + daily.deltaNumberOfSows = daily.numberOfSows - baseDaily.numberOfSows; + daily.deltaSownBeans = daily.sownBeans.minus(baseDaily.sownBeans); + daily.deltaUnharvestablePods = daily.unharvestablePods.minus(baseDaily.unharvestablePods); + daily.deltaHarvestablePods = daily.harvestablePods.minus(baseDaily.harvestablePods); + daily.deltaHarvestedPods = daily.harvestedPods.minus(baseDaily.harvestedPods); + daily.deltaSoil = daily.soil.minus(baseDaily.soil); + // deltaIssuedSoil set below, on initial snapshot + daily.deltaPodIndex = daily.podIndex.minus(baseDaily.podIndex); + daily.deltaPodRate = daily.podRate.minus(baseDaily.podRate); + + if (daily.id == baseDaily.id) { + // Add existing deltas + daily.deltaTemperature = daily.deltaTemperature + baseDaily.deltaTemperature; + daily.deltaRealRateOfReturn = daily.deltaRealRateOfReturn.plus(baseDaily.deltaRealRateOfReturn); + daily.deltaNumberOfSowers = daily.deltaNumberOfSowers + baseDaily.deltaNumberOfSowers; + daily.deltaNumberOfSows = daily.deltaNumberOfSows + baseDaily.deltaNumberOfSows; + daily.deltaSownBeans = daily.deltaSownBeans.plus(baseDaily.deltaSownBeans); + daily.deltaUnharvestablePods = daily.deltaUnharvestablePods.plus(baseDaily.deltaUnharvestablePods); + daily.deltaHarvestablePods = daily.deltaHarvestablePods.plus(baseDaily.deltaHarvestablePods); + daily.deltaHarvestedPods = daily.deltaHarvestedPods.plus(baseDaily.deltaHarvestedPods); + daily.deltaSoil = daily.deltaSoil.plus(baseDaily.deltaSoil); + daily.deltaPodIndex = daily.deltaPodIndex.plus(baseDaily.deltaPodIndex); + daily.deltaPodRate = daily.deltaPodRate.plus(baseDaily.deltaPodRate); + // Carry over existing values + daily.issuedSoil = baseDaily.issuedSoil; + daily.deltaIssuedSoil = baseDaily.deltaIssuedSoil; + } else { + // Sets issued soil here since this is the initial creation + daily.issuedSoil = field.soil; + daily.deltaIssuedSoil = field.soil.minus(baseDaily.issuedSoil); + } + } else { + daily.deltaTemperature = daily.temperature; + daily.deltaRealRateOfReturn = daily.realRateOfReturn; + daily.deltaNumberOfSowers = daily.numberOfSowers; + daily.deltaNumberOfSows = daily.numberOfSows; + daily.deltaSownBeans = daily.sownBeans; + daily.deltaUnharvestablePods = daily.unharvestablePods; + daily.deltaHarvestablePods = daily.harvestablePods; + daily.deltaHarvestedPods = daily.harvestedPods; + daily.deltaSoil = daily.soil; + daily.deltaPodIndex = daily.podIndex; + daily.deltaPodRate = daily.podRate; + + // Sets issued soil here since this is the initial creation + daily.issuedSoil = field.soil; + daily.deltaIssuedSoil = field.soil; + } + daily.createdAt = day; + daily.updatedAt = timestamp; + daily.save(); + + field.lastHourlySnapshotSeason = currentSeason; + field.lastDailySnapshotDay = day; +} + +// Set case id on hourly. Snapshot must have already been created. +export function setFieldHourlyCaseId(caseId: BigInt, field: Field): void { + const hourly = FieldHourlySnapshot.load(field.id + "-" + field.lastHourlySnapshotSeason.toString())!; + hourly.caseId = caseId; + hourly.save(); +} + +// Set soil sold out info on the hourly. Snapshot must have already been created. +export function setHourlySoilSoldOut(soldOutBlock: BigInt, field: Field): void { + const hourly = FieldHourlySnapshot.load(field.id + "-" + field.lastHourlySnapshotSeason.toString())!; + hourly.blocksToSoldOutSoil = soldOutBlock.minus(hourly.seasonBlock); + hourly.soilSoldOut = true; + hourly.save(); +} diff --git a/projects/subgraph-beanstalk/src/entities/snapshots/Marketplace.ts b/projects/subgraph-beanstalk/src/entities/snapshots/Marketplace.ts new file mode 100644 index 0000000000..d3203079f3 --- /dev/null +++ b/projects/subgraph-beanstalk/src/entities/snapshots/Marketplace.ts @@ -0,0 +1,156 @@ +import { BigInt, Address, log } from "@graphprotocol/graph-ts"; +import { PodMarketplace, PodMarketplaceDailySnapshot, PodMarketplaceHourlySnapshot } from "../../../generated/schema"; +import { getCurrentSeason } from "../Beanstalk"; +import { dayFromTimestamp, hourFromTimestamp } from "../../../../subgraph-core/utils/Dates"; + +export function takeMarketSnapshots(market: PodMarketplace, protocol: Address, timestamp: BigInt): void { + const currentSeason = getCurrentSeason(protocol); + + const hour = BigInt.fromI32(hourFromTimestamp(timestamp)); + const day = BigInt.fromI32(dayFromTimestamp(timestamp)); + + // Load the snapshot for this season/day + const hourlyId = market.id + "-" + currentSeason.toString(); + const dailyId = market.id + "-" + day.toString(); + let baseHourly = PodMarketplaceHourlySnapshot.load(hourlyId); + let baseDaily = PodMarketplaceDailySnapshot.load(dailyId); + if (baseHourly == null && market.lastHourlySnapshotSeason !== 0) { + baseHourly = PodMarketplaceHourlySnapshot.load(market.id + "-" + market.lastHourlySnapshotSeason.toString()); + } + if (baseDaily == null && market.lastDailySnapshotDay !== null) { + baseDaily = PodMarketplaceDailySnapshot.load(market.id + "-" + market.lastDailySnapshotDay!.toString()); + } + const hourly = new PodMarketplaceHourlySnapshot(hourlyId); + const daily = new PodMarketplaceDailySnapshot(dailyId); + + // Set current values + hourly.season = currentSeason; + hourly.podMarketplace = market.id; + hourly.listedPods = market.listedPods; + hourly.availableListedPods = market.availableListedPods; + hourly.filledListedPods = market.filledListedPods; + hourly.expiredListedPods = market.expiredListedPods; + hourly.cancelledListedPods = market.cancelledListedPods; + hourly.orderBeans = market.orderBeans; + hourly.availableOrderBeans = market.availableOrderBeans; + hourly.filledOrderBeans = market.filledOrderBeans; + hourly.filledOrderedPods = market.filledOrderedPods; + hourly.cancelledOrderBeans = market.cancelledOrderBeans; + hourly.podVolume = market.podVolume; + hourly.beanVolume = market.beanVolume; + + // Set deltas + if (baseHourly !== null) { + hourly.deltaListedPods = hourly.listedPods.minus(baseHourly.listedPods); + hourly.deltaAvailableListedPods = hourly.availableListedPods.minus(baseHourly.availableListedPods); + hourly.deltaFilledListedPods = hourly.filledListedPods.minus(baseHourly.filledListedPods); + hourly.deltaExpiredListedPods = hourly.expiredListedPods.minus(baseHourly.expiredListedPods); + hourly.deltaCancelledListedPods = hourly.cancelledListedPods.minus(baseHourly.cancelledListedPods); + hourly.deltaOrderBeans = hourly.orderBeans.minus(baseHourly.orderBeans); + hourly.deltaAvailableOrderBeans = hourly.availableOrderBeans.minus(baseHourly.availableOrderBeans); + hourly.deltaFilledOrderBeans = hourly.filledOrderBeans.minus(baseHourly.filledOrderBeans); + hourly.deltaFilledOrderedPods = hourly.filledOrderedPods.minus(baseHourly.filledOrderedPods); + hourly.deltaCancelledOrderBeans = hourly.cancelledOrderBeans.minus(baseHourly.cancelledOrderBeans); + hourly.deltaPodVolume = hourly.podVolume.minus(baseHourly.podVolume); + hourly.deltaBeanVolume = hourly.beanVolume.minus(baseHourly.beanVolume); + + if (hourly.id == baseHourly.id) { + // Add existing deltas + hourly.deltaListedPods = hourly.deltaListedPods.plus(baseHourly.deltaListedPods); + hourly.deltaAvailableListedPods = hourly.deltaAvailableListedPods.plus(baseHourly.deltaAvailableListedPods); + hourly.deltaFilledListedPods = hourly.deltaFilledListedPods.plus(baseHourly.deltaFilledListedPods); + hourly.deltaExpiredListedPods = hourly.deltaExpiredListedPods.plus(baseHourly.deltaExpiredListedPods); + hourly.deltaCancelledListedPods = hourly.deltaCancelledListedPods.plus(baseHourly.deltaCancelledListedPods); + hourly.deltaOrderBeans = hourly.deltaOrderBeans.plus(baseHourly.deltaOrderBeans); + hourly.deltaAvailableOrderBeans = hourly.deltaAvailableOrderBeans.plus(baseHourly.deltaAvailableOrderBeans); + hourly.deltaFilledOrderBeans = hourly.deltaFilledOrderBeans.plus(baseHourly.deltaFilledOrderBeans); + hourly.deltaFilledOrderedPods = hourly.deltaFilledOrderedPods.plus(baseHourly.deltaFilledOrderedPods); + hourly.deltaCancelledOrderBeans = hourly.deltaCancelledOrderBeans.plus(baseHourly.deltaCancelledOrderBeans); + hourly.deltaPodVolume = hourly.deltaPodVolume.plus(baseHourly.deltaPodVolume); + hourly.deltaBeanVolume = hourly.deltaBeanVolume.plus(baseHourly.deltaBeanVolume); + } + } else { + hourly.deltaListedPods = hourly.listedPods; + hourly.deltaAvailableListedPods = hourly.availableListedPods; + hourly.deltaFilledListedPods = hourly.filledListedPods; + hourly.deltaExpiredListedPods = hourly.expiredListedPods; + hourly.deltaCancelledListedPods = hourly.cancelledListedPods; + hourly.deltaOrderBeans = hourly.orderBeans; + hourly.deltaAvailableOrderBeans = hourly.availableOrderBeans; + hourly.deltaFilledOrderBeans = hourly.filledOrderBeans; + hourly.deltaFilledOrderedPods = hourly.filledOrderedPods; + hourly.deltaCancelledOrderBeans = hourly.cancelledOrderBeans; + hourly.deltaPodVolume = hourly.podVolume; + hourly.deltaBeanVolume = hourly.beanVolume; + } + hourly.createdAt = hour; + hourly.updatedAt = timestamp; + hourly.save(); + + // Repeat for daily snapshot. + // Duplicate code is preferred to type coercion, the codegen doesnt provide a common interface. + + daily.season = currentSeason; + daily.podMarketplace = market.id; + daily.listedPods = market.listedPods; + daily.availableListedPods = market.availableListedPods; + daily.filledListedPods = market.filledListedPods; + daily.expiredListedPods = market.expiredListedPods; + daily.cancelledListedPods = market.cancelledListedPods; + daily.orderBeans = market.orderBeans; + daily.availableOrderBeans = market.availableOrderBeans; + daily.filledOrderBeans = market.filledOrderBeans; + daily.filledOrderedPods = market.filledOrderedPods; + daily.cancelledOrderBeans = market.cancelledOrderBeans; + daily.podVolume = market.podVolume; + daily.beanVolume = market.beanVolume; + if (baseDaily !== null) { + daily.deltaListedPods = daily.listedPods.minus(baseDaily.listedPods); + daily.deltaAvailableListedPods = daily.availableListedPods.minus(baseDaily.availableListedPods); + daily.deltaFilledListedPods = daily.filledListedPods.minus(baseDaily.filledListedPods); + daily.deltaExpiredListedPods = daily.expiredListedPods.minus(baseDaily.expiredListedPods); + daily.deltaCancelledListedPods = daily.cancelledListedPods.minus(baseDaily.cancelledListedPods); + daily.deltaOrderBeans = daily.orderBeans.minus(baseDaily.orderBeans); + daily.deltaAvailableOrderBeans = daily.availableOrderBeans.minus(baseDaily.availableOrderBeans); + daily.deltaFilledOrderBeans = daily.filledOrderBeans.minus(baseDaily.filledOrderBeans); + daily.deltaFilledOrderedPods = daily.filledOrderedPods.minus(baseDaily.filledOrderedPods); + daily.deltaCancelledOrderBeans = daily.cancelledOrderBeans.minus(baseDaily.cancelledOrderBeans); + daily.deltaPodVolume = daily.podVolume.minus(baseDaily.podVolume); + daily.deltaBeanVolume = daily.beanVolume.minus(baseDaily.beanVolume); + + if (daily.id == baseDaily.id) { + // Add existing deltas + daily.deltaListedPods = daily.deltaListedPods.plus(baseDaily.deltaListedPods); + daily.deltaAvailableListedPods = daily.deltaAvailableListedPods.plus(baseDaily.deltaAvailableListedPods); + daily.deltaFilledListedPods = daily.deltaFilledListedPods.plus(baseDaily.deltaFilledListedPods); + daily.deltaExpiredListedPods = daily.deltaExpiredListedPods.plus(baseDaily.deltaExpiredListedPods); + daily.deltaCancelledListedPods = daily.deltaCancelledListedPods.plus(baseDaily.deltaCancelledListedPods); + daily.deltaOrderBeans = daily.deltaOrderBeans.plus(baseDaily.deltaOrderBeans); + daily.deltaAvailableOrderBeans = daily.deltaAvailableOrderBeans.plus(baseDaily.deltaAvailableOrderBeans); + daily.deltaFilledOrderBeans = daily.deltaFilledOrderBeans.plus(baseDaily.deltaFilledOrderBeans); + daily.deltaFilledOrderedPods = daily.deltaFilledOrderedPods.plus(baseDaily.deltaFilledOrderedPods); + daily.deltaCancelledOrderBeans = daily.deltaCancelledOrderBeans.plus(baseDaily.deltaCancelledOrderBeans); + daily.deltaPodVolume = daily.deltaPodVolume.plus(baseDaily.deltaPodVolume); + daily.deltaBeanVolume = daily.deltaBeanVolume.plus(baseDaily.deltaBeanVolume); + } + } else { + daily.deltaListedPods = daily.listedPods; + daily.deltaAvailableListedPods = daily.availableListedPods; + daily.deltaFilledListedPods = daily.filledListedPods; + daily.deltaExpiredListedPods = daily.expiredListedPods; + daily.deltaCancelledListedPods = daily.cancelledListedPods; + daily.deltaOrderBeans = daily.orderBeans; + daily.deltaAvailableOrderBeans = daily.availableOrderBeans; + daily.deltaFilledOrderBeans = daily.filledOrderBeans; + daily.deltaFilledOrderedPods = daily.filledOrderedPods; + daily.deltaCancelledOrderBeans = daily.cancelledOrderBeans; + daily.deltaPodVolume = daily.podVolume; + daily.deltaBeanVolume = daily.beanVolume; + } + daily.createdAt = day; + daily.updatedAt = timestamp; + daily.save(); + + market.lastHourlySnapshotSeason = currentSeason; + market.lastDailySnapshotDay = day; +} diff --git a/projects/subgraph-beanstalk/src/entities/snapshots/Silo.ts b/projects/subgraph-beanstalk/src/entities/snapshots/Silo.ts new file mode 100644 index 0000000000..a0d06279f9 --- /dev/null +++ b/projects/subgraph-beanstalk/src/entities/snapshots/Silo.ts @@ -0,0 +1,141 @@ +import { BigInt, Address, log } from "@graphprotocol/graph-ts"; +import { Silo, SiloDailySnapshot, SiloHourlySnapshot } from "../../../generated/schema"; +import { getCurrentSeason } from "../Beanstalk"; +import { dayFromTimestamp, hourFromTimestamp } from "../../../../subgraph-core/utils/Dates"; + +export function takeSiloSnapshots(silo: Silo, protocol: Address, timestamp: BigInt): void { + const currentSeason = getCurrentSeason(protocol); + + const hour = BigInt.fromI32(hourFromTimestamp(timestamp)); + const day = BigInt.fromI32(dayFromTimestamp(timestamp)); + + // Load the snapshot for this season/day + const hourlyId = silo.id + "-" + currentSeason.toString(); + const dailyId = silo.id + "-" + day.toString(); + let baseHourly = SiloHourlySnapshot.load(hourlyId); + let baseDaily = SiloDailySnapshot.load(dailyId); + if (baseHourly == null && silo.lastHourlySnapshotSeason !== 0) { + baseHourly = SiloHourlySnapshot.load(silo.id + "-" + silo.lastHourlySnapshotSeason.toString()); + } + if (baseDaily == null && silo.lastDailySnapshotDay !== null) { + baseDaily = SiloDailySnapshot.load(silo.id + "-" + silo.lastDailySnapshotDay!.toString()); + } + const hourly = new SiloHourlySnapshot(hourlyId); + const daily = new SiloDailySnapshot(dailyId); + + // Set current values + hourly.season = currentSeason; + hourly.silo = silo.id; + hourly.depositedBDV = silo.depositedBDV; + hourly.stalk = silo.stalk; + hourly.plantableStalk = silo.plantableStalk; + hourly.seeds = silo.seeds; + hourly.grownStalkPerSeason = silo.grownStalkPerSeason; + hourly.roots = silo.roots; + hourly.germinatingStalk = silo.germinatingStalk; + hourly.beanToMaxLpGpPerBdvRatio = silo.beanToMaxLpGpPerBdvRatio; + hourly.beanMints = silo.beanMints; + hourly.activeFarmers = silo.activeFarmers; + + // Set deltas + if (baseHourly !== null) { + hourly.deltaDepositedBDV = hourly.depositedBDV.minus(baseHourly.depositedBDV); + hourly.deltaStalk = hourly.stalk.minus(baseHourly.stalk); + hourly.deltaPlantableStalk = hourly.plantableStalk.minus(baseHourly.plantableStalk); + hourly.deltaSeeds = hourly.seeds.minus(baseHourly.seeds); + hourly.deltaRoots = hourly.roots.minus(baseHourly.roots); + hourly.deltaGerminatingStalk = hourly.germinatingStalk.minus(baseHourly.germinatingStalk); + // NOTE: missing beanToMaxLpGpPerBdvRatio + hourly.deltaBeanMints = hourly.beanMints.minus(baseHourly.beanMints); + hourly.deltaActiveFarmers = hourly.activeFarmers - baseHourly.activeFarmers; + if (hourly.id == baseHourly.id) { + // Add existing deltas + hourly.deltaDepositedBDV = hourly.deltaDepositedBDV.plus(baseHourly.deltaDepositedBDV); + hourly.deltaStalk = hourly.deltaStalk.plus(baseHourly.deltaStalk); + hourly.deltaPlantableStalk = hourly.deltaPlantableStalk.plus(baseHourly.deltaPlantableStalk); + hourly.deltaSeeds = hourly.deltaSeeds.plus(baseHourly.deltaSeeds); + hourly.deltaRoots = hourly.deltaRoots.plus(baseHourly.deltaRoots); + hourly.deltaGerminatingStalk = hourly.deltaGerminatingStalk.plus(baseHourly.deltaGerminatingStalk); + // NOTE: missing beanToMaxLpGpPerBdvRatio + hourly.deltaBeanMints = hourly.deltaBeanMints.plus(baseHourly.deltaBeanMints); + hourly.deltaActiveFarmers = hourly.deltaActiveFarmers + baseHourly.deltaActiveFarmers; + // Carry over unset values that would otherwise get erased + hourly.caseId = baseHourly.caseId; + } + } else { + hourly.deltaDepositedBDV = hourly.depositedBDV; + hourly.deltaStalk = hourly.stalk; + hourly.deltaPlantableStalk = hourly.plantableStalk; + hourly.deltaSeeds = hourly.seeds; + hourly.deltaRoots = hourly.roots; + hourly.deltaGerminatingStalk = hourly.germinatingStalk; + // NOTE: missing beanToMaxLpGpPerBdvRatio + hourly.deltaBeanMints = hourly.beanMints; + hourly.deltaActiveFarmers = hourly.activeFarmers; + } + hourly.createdAt = hour; + hourly.updatedAt = timestamp; + hourly.save(); + + // Repeat for daily snapshot. + // Duplicate code is preferred to type coercion, the codegen doesnt provide a common interface. + + daily.season = currentSeason; + daily.silo = silo.id; + daily.depositedBDV = silo.depositedBDV; + daily.stalk = silo.stalk; + daily.plantableStalk = silo.plantableStalk; + daily.seeds = silo.seeds; + daily.grownStalkPerSeason = silo.grownStalkPerSeason; + daily.roots = silo.roots; + daily.germinatingStalk = silo.germinatingStalk; + daily.beanToMaxLpGpPerBdvRatio = silo.beanToMaxLpGpPerBdvRatio; + daily.beanMints = silo.beanMints; + daily.activeFarmers = silo.activeFarmers; + if (baseDaily !== null) { + daily.deltaDepositedBDV = daily.depositedBDV.minus(baseDaily.depositedBDV); + daily.deltaStalk = daily.stalk.minus(baseDaily.stalk); + daily.deltaPlantableStalk = daily.plantableStalk.minus(baseDaily.plantableStalk); + daily.deltaSeeds = daily.seeds.minus(baseDaily.seeds); + daily.deltaRoots = daily.roots.minus(baseDaily.roots); + daily.deltaGerminatingStalk = daily.germinatingStalk.minus(baseDaily.germinatingStalk); + // NOTE: missing beanToMaxLpGpPerBdvRatio + daily.deltaBeanMints = daily.beanMints.minus(baseDaily.beanMints); + daily.deltaActiveFarmers = daily.activeFarmers - baseDaily.activeFarmers; + if (daily.id == baseDaily.id) { + // Add existing deltas + daily.deltaDepositedBDV = daily.deltaDepositedBDV.plus(baseDaily.deltaDepositedBDV); + daily.deltaStalk = daily.deltaStalk.plus(baseDaily.deltaStalk); + daily.deltaPlantableStalk = daily.deltaPlantableStalk.plus(baseDaily.deltaPlantableStalk); + daily.deltaSeeds = daily.deltaSeeds.plus(baseDaily.deltaSeeds); + daily.deltaRoots = daily.deltaRoots.plus(baseDaily.deltaRoots); + daily.deltaGerminatingStalk = daily.deltaGerminatingStalk.plus(baseDaily.deltaGerminatingStalk); + // NOTE: missing beanToMaxLpGpPerBdvRatio + daily.deltaBeanMints = daily.deltaBeanMints.plus(baseDaily.deltaBeanMints); + daily.deltaActiveFarmers = daily.deltaActiveFarmers + baseDaily.deltaActiveFarmers; + } + } else { + daily.deltaDepositedBDV = daily.depositedBDV; + daily.deltaStalk = daily.stalk; + daily.deltaPlantableStalk = daily.plantableStalk; + daily.deltaSeeds = daily.seeds; + daily.deltaRoots = daily.roots; + daily.deltaGerminatingStalk = daily.germinatingStalk; + // NOTE: missing beanToMaxLpGpPerBdvRatio + daily.deltaBeanMints = daily.beanMints; + daily.deltaActiveFarmers = daily.activeFarmers; + } + daily.createdAt = day; + daily.updatedAt = timestamp; + daily.save(); + + silo.lastHourlySnapshotSeason = currentSeason; + silo.lastDailySnapshotDay = day; +} + +// Set case id on hourly snapshot. Snapshot must have already been created. +export function setSiloHourlyCaseId(caseId: BigInt, silo: Silo): void { + const hourly = SiloHourlySnapshot.load(silo.id + "-" + silo.lastHourlySnapshotSeason.toString())!; + hourly.caseId = caseId; + hourly.save(); +} diff --git a/projects/subgraph-beanstalk/src/entities/snapshots/SiloAsset.ts b/projects/subgraph-beanstalk/src/entities/snapshots/SiloAsset.ts new file mode 100644 index 0000000000..e9f9e72d10 --- /dev/null +++ b/projects/subgraph-beanstalk/src/entities/snapshots/SiloAsset.ts @@ -0,0 +1,92 @@ +import { BigInt, Address, log } from "@graphprotocol/graph-ts"; +import { SiloAsset, SiloAssetDailySnapshot, SiloAssetHourlySnapshot } from "../../../generated/schema"; +import { dayFromTimestamp, hourFromTimestamp } from "../../../../subgraph-core/utils/Dates"; +import { getCurrentSeason } from "../Beanstalk"; + +export function takeSiloAssetSnapshots(siloAsset: SiloAsset, protocol: Address, timestamp: BigInt): void { + const currentSeason = getCurrentSeason(protocol); + + const hour = BigInt.fromI32(hourFromTimestamp(timestamp)); + const day = BigInt.fromI32(dayFromTimestamp(timestamp)); + + // Load the snapshot for this season/day + const hourlyId = siloAsset.id + "-" + currentSeason.toString(); + const dailyId = siloAsset.id + "-" + day.toString(); + let baseHourly = SiloAssetHourlySnapshot.load(hourlyId); + let baseDaily = SiloAssetDailySnapshot.load(dailyId); + if (baseHourly == null && siloAsset.lastHourlySnapshotSeason !== 0) { + baseHourly = SiloAssetHourlySnapshot.load(siloAsset.id + "-" + siloAsset.lastHourlySnapshotSeason.toString()); + } + if (baseDaily == null && siloAsset.lastDailySnapshotDay !== null) { + baseDaily = SiloAssetDailySnapshot.load(siloAsset.id + "-" + siloAsset.lastDailySnapshotDay!.toString()); + } + const hourly = new SiloAssetHourlySnapshot(hourlyId); + const daily = new SiloAssetDailySnapshot(dailyId); + + // Set current values + hourly.season = currentSeason; + hourly.siloAsset = siloAsset.id; + hourly.depositedAmount = siloAsset.depositedAmount; + hourly.depositedBDV = siloAsset.depositedBDV; + hourly.withdrawnAmount = siloAsset.withdrawnAmount; + hourly.farmAmount = siloAsset.farmAmount; + + // Set deltas + if (baseHourly !== null) { + hourly.deltaDepositedAmount = hourly.depositedAmount.minus(baseHourly.depositedAmount); + hourly.deltaDepositedBDV = hourly.depositedBDV.minus(baseHourly.depositedBDV); + hourly.deltaWithdrawnAmount = hourly.withdrawnAmount.minus(baseHourly.withdrawnAmount); + hourly.deltaFarmAmount = hourly.farmAmount.minus(baseHourly.farmAmount); + + if (hourly.id == baseHourly.id) { + // Add existing deltas + hourly.deltaDepositedAmount = hourly.deltaDepositedAmount.plus(baseHourly.deltaDepositedAmount); + hourly.deltaDepositedBDV = hourly.deltaDepositedBDV.plus(baseHourly.deltaDepositedBDV); + hourly.deltaWithdrawnAmount = hourly.deltaWithdrawnAmount.plus(baseHourly.deltaWithdrawnAmount); + hourly.deltaFarmAmount = hourly.deltaFarmAmount.plus(baseHourly.deltaFarmAmount); + } + } else { + hourly.deltaDepositedAmount = hourly.depositedAmount; + hourly.deltaDepositedBDV = hourly.depositedBDV; + hourly.deltaWithdrawnAmount = hourly.withdrawnAmount; + hourly.deltaFarmAmount = hourly.farmAmount; + } + hourly.createdAt = hour; + hourly.updatedAt = timestamp; + hourly.save(); + + // Repeat for daily snapshot. + // Duplicate code is preferred to type coercion, the codegen doesnt provide a common interface. + + daily.season = currentSeason; + daily.siloAsset = siloAsset.id; + daily.depositedAmount = siloAsset.depositedAmount; + daily.depositedBDV = siloAsset.depositedBDV; + daily.withdrawnAmount = siloAsset.withdrawnAmount; + daily.farmAmount = siloAsset.farmAmount; + if (baseDaily !== null) { + daily.deltaDepositedAmount = daily.depositedAmount.minus(baseDaily.depositedAmount); + daily.deltaDepositedBDV = daily.depositedBDV.minus(baseDaily.depositedBDV); + daily.deltaWithdrawnAmount = daily.withdrawnAmount.minus(baseDaily.withdrawnAmount); + daily.deltaFarmAmount = daily.farmAmount.minus(baseDaily.farmAmount); + + if (daily.id == baseDaily.id) { + // Add existing deltas + daily.deltaDepositedAmount = daily.deltaDepositedAmount.plus(baseDaily.deltaDepositedAmount); + daily.deltaDepositedBDV = daily.deltaDepositedBDV.plus(baseDaily.deltaDepositedBDV); + daily.deltaWithdrawnAmount = daily.deltaWithdrawnAmount.plus(baseDaily.deltaWithdrawnAmount); + daily.deltaFarmAmount = daily.deltaFarmAmount.plus(baseDaily.deltaFarmAmount); + } + } else { + daily.deltaDepositedAmount = daily.depositedAmount; + daily.deltaDepositedBDV = daily.depositedBDV; + daily.deltaWithdrawnAmount = daily.withdrawnAmount; + daily.deltaFarmAmount = daily.farmAmount; + } + daily.createdAt = day; + daily.updatedAt = timestamp; + daily.save(); + + siloAsset.lastHourlySnapshotSeason = currentSeason; + siloAsset.lastDailySnapshotDay = day; +} diff --git a/projects/subgraph-beanstalk/src/entities/snapshots/UnripeToken.ts b/projects/subgraph-beanstalk/src/entities/snapshots/UnripeToken.ts new file mode 100644 index 0000000000..084599bc06 --- /dev/null +++ b/projects/subgraph-beanstalk/src/entities/snapshots/UnripeToken.ts @@ -0,0 +1,148 @@ +import { BigInt, Address, log } from "@graphprotocol/graph-ts"; +import { UnripeToken, UnripeTokenDailySnapshot, UnripeTokenHourlySnapshot } from "../../../generated/schema"; +import { getCurrentSeason } from "../Beanstalk"; +import { dayFromTimestamp, hourFromTimestamp } from "../../../../subgraph-core/utils/Dates"; + +export function takeUnripeTokenSnapshots(unripeToken: UnripeToken, protocol: Address, timestamp: BigInt): void { + const currentSeason = getCurrentSeason(protocol); + + const hour = BigInt.fromI32(hourFromTimestamp(timestamp)); + const day = BigInt.fromI32(dayFromTimestamp(timestamp)); + + // Load the snapshot for this season/day + const hourlyId = unripeToken.id.toHexString() + "-" + currentSeason.toString(); + const dailyId = unripeToken.id.toHexString() + "-" + day.toString(); + let baseHourly = UnripeTokenHourlySnapshot.load(hourlyId); + let baseDaily = UnripeTokenDailySnapshot.load(dailyId); + if (baseHourly == null && unripeToken.lastHourlySnapshotSeason !== 0) { + baseHourly = UnripeTokenHourlySnapshot.load(unripeToken.id.toHexString() + "-" + unripeToken.lastHourlySnapshotSeason.toString()); + } + if (baseDaily == null && unripeToken.lastDailySnapshotDay !== null) { + baseDaily = UnripeTokenDailySnapshot.load(unripeToken.id.toHexString() + "-" + unripeToken.lastDailySnapshotDay!.toString()); + } + const hourly = new UnripeTokenHourlySnapshot(hourlyId); + const daily = new UnripeTokenDailySnapshot(dailyId); + + // Set current values + hourly.season = currentSeason; + hourly.unripeToken = unripeToken.id; + hourly.underlyingToken = unripeToken.underlyingToken; + hourly.totalUnderlying = unripeToken.totalUnderlying; + hourly.amountUnderlyingOne = unripeToken.amountUnderlyingOne; + hourly.bdvUnderlyingOne = unripeToken.bdvUnderlyingOne; + hourly.choppableAmountOne = unripeToken.choppableAmountOne; + hourly.choppableBdvOne = unripeToken.choppableBdvOne; + hourly.chopRate = unripeToken.chopRate; + hourly.recapPercent = unripeToken.recapPercent; + hourly.totalChoppedAmount = unripeToken.totalChoppedAmount; + hourly.totalChoppedBdv = unripeToken.totalChoppedBdv; + hourly.totalChoppedBdvReceived = unripeToken.totalChoppedBdvReceived; + + // Set deltas + if (baseHourly !== null) { + hourly.deltaUnderlyingToken = hourly.underlyingToken != baseHourly.underlyingToken; + hourly.deltaTotalUnderlying = hourly.totalUnderlying.minus(baseHourly.totalUnderlying); + hourly.deltaAmountUnderlyingOne = hourly.amountUnderlyingOne.minus(baseHourly.amountUnderlyingOne); + hourly.deltaBdvUnderlyingOne = hourly.bdvUnderlyingOne.minus(baseHourly.bdvUnderlyingOne); + hourly.deltaChoppableAmountOne = hourly.choppableAmountOne.minus(baseHourly.choppableAmountOne); + hourly.deltaChoppableBdvOne = hourly.choppableBdvOne.minus(baseHourly.choppableBdvOne); + hourly.deltaChopRate = hourly.chopRate.minus(baseHourly.chopRate); + hourly.deltaRecapPercent = hourly.recapPercent.minus(baseHourly.recapPercent); + hourly.deltaTotalChoppedAmount = hourly.totalChoppedAmount.minus(baseHourly.totalChoppedAmount); + hourly.deltaTotalChoppedBdv = hourly.totalChoppedBdv.minus(baseHourly.totalChoppedBdv); + hourly.deltaTotalChoppedBdvReceived = hourly.totalChoppedBdvReceived.minus(baseHourly.totalChoppedBdvReceived); + + if (hourly.id == baseHourly.id) { + // Add existing deltas + hourly.deltaUnderlyingToken = hourly.deltaUnderlyingToken || baseHourly.deltaUnderlyingToken; + hourly.deltaTotalUnderlying = hourly.deltaTotalUnderlying.plus(baseHourly.deltaTotalUnderlying); + hourly.deltaAmountUnderlyingOne = hourly.deltaAmountUnderlyingOne.plus(baseHourly.deltaAmountUnderlyingOne); + hourly.deltaBdvUnderlyingOne = hourly.deltaBdvUnderlyingOne.plus(baseHourly.deltaBdvUnderlyingOne); + hourly.deltaChoppableAmountOne = hourly.deltaChoppableAmountOne.plus(baseHourly.deltaChoppableAmountOne); + hourly.deltaChoppableBdvOne = hourly.deltaChoppableBdvOne.plus(baseHourly.deltaChoppableBdvOne); + hourly.deltaChopRate = hourly.deltaChopRate.plus(baseHourly.deltaChopRate); + hourly.deltaRecapPercent = hourly.deltaRecapPercent.plus(baseHourly.deltaRecapPercent); + hourly.deltaTotalChoppedAmount = hourly.deltaTotalChoppedAmount.plus(baseHourly.deltaTotalChoppedAmount); + hourly.deltaTotalChoppedBdv = hourly.deltaTotalChoppedBdv.plus(baseHourly.deltaTotalChoppedBdv); + hourly.deltaTotalChoppedBdvReceived = hourly.deltaTotalChoppedBdvReceived.plus(baseHourly.deltaTotalChoppedBdvReceived); + } + } else { + hourly.deltaUnderlyingToken = false; + hourly.deltaTotalUnderlying = hourly.totalUnderlying; + hourly.deltaAmountUnderlyingOne = hourly.amountUnderlyingOne; + hourly.deltaBdvUnderlyingOne = hourly.bdvUnderlyingOne; + hourly.deltaChoppableAmountOne = hourly.choppableAmountOne; + hourly.deltaChoppableBdvOne = hourly.choppableBdvOne; + hourly.deltaChopRate = hourly.chopRate; + hourly.deltaRecapPercent = hourly.recapPercent; + hourly.deltaTotalChoppedAmount = hourly.totalChoppedAmount; + hourly.deltaTotalChoppedBdv = hourly.totalChoppedBdv; + hourly.deltaTotalChoppedBdvReceived = hourly.totalChoppedBdvReceived; + } + hourly.createdAt = hour; + hourly.updatedAt = timestamp; + hourly.save(); + + // Repeat for daily snapshot. + // Duplicate code is preferred to type coercion, the codegen doesnt provide a common interface. + + daily.season = currentSeason; + daily.unripeToken = unripeToken.id; + daily.underlyingToken = unripeToken.underlyingToken; + daily.totalUnderlying = unripeToken.totalUnderlying; + daily.amountUnderlyingOne = unripeToken.amountUnderlyingOne; + daily.bdvUnderlyingOne = unripeToken.bdvUnderlyingOne; + daily.choppableAmountOne = unripeToken.choppableAmountOne; + daily.choppableBdvOne = unripeToken.choppableBdvOne; + daily.chopRate = unripeToken.chopRate; + daily.recapPercent = unripeToken.recapPercent; + daily.totalChoppedAmount = unripeToken.totalChoppedAmount; + daily.totalChoppedBdv = unripeToken.totalChoppedBdv; + daily.totalChoppedBdvReceived = unripeToken.totalChoppedBdvReceived; + if (baseDaily !== null) { + daily.deltaUnderlyingToken = daily.underlyingToken != baseDaily.underlyingToken; + daily.deltaTotalUnderlying = daily.totalUnderlying.minus(baseDaily.totalUnderlying); + daily.deltaAmountUnderlyingOne = daily.amountUnderlyingOne.minus(baseDaily.amountUnderlyingOne); + daily.deltaBdvUnderlyingOne = daily.bdvUnderlyingOne.minus(baseDaily.bdvUnderlyingOne); + daily.deltaChoppableAmountOne = daily.choppableAmountOne.minus(baseDaily.choppableAmountOne); + daily.deltaChoppableBdvOne = daily.choppableBdvOne.minus(baseDaily.choppableBdvOne); + daily.deltaChopRate = daily.chopRate.minus(baseDaily.chopRate); + daily.deltaRecapPercent = daily.recapPercent.minus(baseDaily.recapPercent); + daily.deltaTotalChoppedAmount = daily.totalChoppedAmount.minus(baseDaily.totalChoppedAmount); + daily.deltaTotalChoppedBdv = daily.totalChoppedBdv.minus(baseDaily.totalChoppedBdv); + daily.deltaTotalChoppedBdvReceived = daily.totalChoppedBdvReceived.minus(baseDaily.totalChoppedBdvReceived); + + if (daily.id == baseDaily.id) { + // Add existing deltas + daily.deltaUnderlyingToken = daily.deltaUnderlyingToken || baseDaily.deltaUnderlyingToken; + daily.deltaTotalUnderlying = daily.deltaTotalUnderlying.plus(baseDaily.deltaTotalUnderlying); + daily.deltaAmountUnderlyingOne = daily.deltaAmountUnderlyingOne.plus(baseDaily.deltaAmountUnderlyingOne); + daily.deltaBdvUnderlyingOne = daily.deltaBdvUnderlyingOne.plus(baseDaily.deltaBdvUnderlyingOne); + daily.deltaChoppableAmountOne = daily.deltaChoppableAmountOne.plus(baseDaily.deltaChoppableAmountOne); + daily.deltaChoppableBdvOne = daily.deltaChoppableBdvOne.plus(baseDaily.deltaChoppableBdvOne); + daily.deltaChopRate = daily.deltaChopRate.plus(baseDaily.deltaChopRate); + daily.deltaRecapPercent = daily.deltaRecapPercent.plus(baseDaily.deltaRecapPercent); + daily.deltaTotalChoppedAmount = daily.deltaTotalChoppedAmount.plus(baseDaily.deltaTotalChoppedAmount); + daily.deltaTotalChoppedBdv = daily.deltaTotalChoppedBdv.plus(baseDaily.deltaTotalChoppedBdv); + daily.deltaTotalChoppedBdvReceived = daily.deltaTotalChoppedBdvReceived.plus(baseDaily.deltaTotalChoppedBdvReceived); + } + } else { + daily.deltaUnderlyingToken = false; + daily.deltaTotalUnderlying = daily.totalUnderlying; + daily.deltaAmountUnderlyingOne = daily.amountUnderlyingOne; + daily.deltaBdvUnderlyingOne = daily.bdvUnderlyingOne; + daily.deltaChoppableAmountOne = daily.choppableAmountOne; + daily.deltaChoppableBdvOne = daily.choppableBdvOne; + daily.deltaChopRate = daily.chopRate; + daily.deltaRecapPercent = daily.recapPercent; + daily.deltaTotalChoppedAmount = daily.totalChoppedAmount; + daily.deltaTotalChoppedBdv = daily.totalChoppedBdv; + daily.deltaTotalChoppedBdvReceived = daily.totalChoppedBdvReceived; + } + daily.createdAt = day; + daily.updatedAt = timestamp; + daily.save(); + + unripeToken.lastHourlySnapshotSeason = currentSeason; + unripeToken.lastDailySnapshotDay = day; +} diff --git a/projects/subgraph-beanstalk/src/entities/snapshots/WhitelistTokenSetting.ts b/projects/subgraph-beanstalk/src/entities/snapshots/WhitelistTokenSetting.ts new file mode 100644 index 0000000000..2b08a6831e --- /dev/null +++ b/projects/subgraph-beanstalk/src/entities/snapshots/WhitelistTokenSetting.ts @@ -0,0 +1,191 @@ +import { BigInt, Address, log } from "@graphprotocol/graph-ts"; +import { WhitelistTokenSetting, WhitelistTokenHourlySnapshot, WhitelistTokenDailySnapshot } from "../../../generated/schema"; +import { getCurrentSeason } from "../Beanstalk"; +import { dayFromTimestamp, hourFromTimestamp } from "../../../../subgraph-core/utils/Dates"; + +export function takeWhitelistTokenSettingSnapshots( + whitelistTokenSetting: WhitelistTokenSetting, + protocol: Address, + timestamp: BigInt +): void { + const currentSeason = getCurrentSeason(protocol); + + const hour = BigInt.fromI32(hourFromTimestamp(timestamp)); + const day = BigInt.fromI32(dayFromTimestamp(timestamp)); + + // Load the snapshot for this season/day + const hourlyId = whitelistTokenSetting.id.toHexString() + "-" + currentSeason.toString(); + const dailyId = whitelistTokenSetting.id.toHexString() + "-" + day.toString(); + let baseHourly = WhitelistTokenHourlySnapshot.load(hourlyId); + let baseDaily = WhitelistTokenDailySnapshot.load(dailyId); + if (baseHourly == null && whitelistTokenSetting.lastHourlySnapshotSeason !== 0) { + baseHourly = WhitelistTokenHourlySnapshot.load( + whitelistTokenSetting.id.toHexString() + "-" + whitelistTokenSetting.lastHourlySnapshotSeason.toString() + ); + } + if (baseDaily == null && whitelistTokenSetting.lastDailySnapshotDay !== null) { + baseDaily = WhitelistTokenDailySnapshot.load( + whitelistTokenSetting.id.toHexString() + "-" + whitelistTokenSetting.lastDailySnapshotDay!.toString() + ); + } + const hourly = new WhitelistTokenHourlySnapshot(hourlyId); + const daily = new WhitelistTokenDailySnapshot(dailyId); + + // Set current values + hourly.season = currentSeason; + hourly.token = whitelistTokenSetting.id; + hourly.selector = whitelistTokenSetting.selector; + hourly.gpSelector = whitelistTokenSetting.gpSelector; + hourly.lwSelector = whitelistTokenSetting.lwSelector; + hourly.stalkEarnedPerSeason = whitelistTokenSetting.stalkEarnedPerSeason; + hourly.stalkIssuedPerBdv = whitelistTokenSetting.stalkIssuedPerBdv; + hourly.milestoneSeason = whitelistTokenSetting.milestoneSeason; + hourly.gaugePoints = whitelistTokenSetting.gaugePoints; + hourly.optimalPercentDepositedBdv = whitelistTokenSetting.optimalPercentDepositedBdv; + + // Set deltas + if (baseHourly !== null) { + hourly.deltaStalkEarnedPerSeason = hourly.stalkEarnedPerSeason.minus(baseHourly.stalkEarnedPerSeason); + hourly.deltaStalkIssuedPerBdv = hourly.stalkIssuedPerBdv.minus(baseHourly.stalkIssuedPerBdv); + hourly.deltaMilestoneSeason = hourly.milestoneSeason - baseHourly.milestoneSeason; + if (hourly.gaugePoints !== null) { + if (baseHourly.gaugePoints !== null) { + hourly.deltaGaugePoints = hourly.gaugePoints!.minus(baseHourly.gaugePoints!); + } else { + hourly.deltaGaugePoints = hourly.gaugePoints; + } + } + if (hourly.optimalPercentDepositedBdv !== null) { + if (baseHourly.optimalPercentDepositedBdv !== null) { + hourly.deltaOptimalPercentDepositedBdv = hourly.optimalPercentDepositedBdv!.minus(baseHourly.optimalPercentDepositedBdv!); + } else { + hourly.deltaOptimalPercentDepositedBdv = hourly.optimalPercentDepositedBdv; + } + } + + if (hourly.id == baseHourly.id) { + // Add existing deltas + hourly.deltaStalkEarnedPerSeason = hourly.deltaStalkEarnedPerSeason.plus(baseHourly.deltaStalkEarnedPerSeason); + hourly.deltaStalkIssuedPerBdv = hourly.deltaStalkIssuedPerBdv.plus(baseHourly.deltaStalkIssuedPerBdv); + hourly.deltaMilestoneSeason = hourly.deltaMilestoneSeason + baseHourly.deltaMilestoneSeason; + if (hourly.deltaGaugePoints !== null && baseHourly.deltaGaugePoints !== null) { + hourly.deltaGaugePoints = hourly.deltaGaugePoints!.plus(baseHourly.deltaGaugePoints!); + } + if (hourly.deltaOptimalPercentDepositedBdv !== null && baseHourly.deltaOptimalPercentDepositedBdv !== null) { + hourly.deltaOptimalPercentDepositedBdv = hourly.deltaOptimalPercentDepositedBdv!.plus(baseHourly.deltaOptimalPercentDepositedBdv!); + } + } + } else { + hourly.deltaStalkEarnedPerSeason = hourly.stalkEarnedPerSeason; + hourly.deltaStalkIssuedPerBdv = hourly.stalkIssuedPerBdv; + hourly.deltaMilestoneSeason = hourly.milestoneSeason; + hourly.deltaGaugePoints = hourly.gaugePoints; + hourly.deltaOptimalPercentDepositedBdv = hourly.optimalPercentDepositedBdv; + } + hourly.createdAt = hour; + hourly.updatedAt = timestamp; + hourly.save(); + + // Repeat for daily snapshot. + // Duplicate code is preferred to type coercion, the codegen doesnt provide a common interface. + + daily.season = currentSeason; + daily.token = whitelistTokenSetting.id; + daily.selector = whitelistTokenSetting.selector; + daily.gpSelector = whitelistTokenSetting.gpSelector; + daily.lwSelector = whitelistTokenSetting.lwSelector; + daily.stalkEarnedPerSeason = whitelistTokenSetting.stalkEarnedPerSeason; + daily.stalkIssuedPerBdv = whitelistTokenSetting.stalkIssuedPerBdv; + daily.milestoneSeason = whitelistTokenSetting.milestoneSeason; + daily.gaugePoints = whitelistTokenSetting.gaugePoints; + daily.optimalPercentDepositedBdv = whitelistTokenSetting.optimalPercentDepositedBdv; + if (baseDaily !== null) { + daily.deltaStalkEarnedPerSeason = daily.stalkEarnedPerSeason.minus(baseDaily.stalkEarnedPerSeason); + daily.deltaStalkIssuedPerBdv = daily.stalkIssuedPerBdv.minus(baseDaily.stalkIssuedPerBdv); + daily.deltaMilestoneSeason = daily.milestoneSeason - baseDaily.milestoneSeason; + if (daily.gaugePoints !== null) { + if (baseDaily.gaugePoints !== null) { + daily.deltaGaugePoints = daily.gaugePoints!.minus(baseDaily.gaugePoints!); + } else { + daily.deltaGaugePoints = daily.gaugePoints; + } + } + if (daily.optimalPercentDepositedBdv !== null) { + if (baseDaily.optimalPercentDepositedBdv !== null) { + daily.deltaOptimalPercentDepositedBdv = daily.optimalPercentDepositedBdv!.minus(baseDaily.optimalPercentDepositedBdv!); + } else { + daily.deltaOptimalPercentDepositedBdv = daily.optimalPercentDepositedBdv; + } + } + + if (daily.id == baseDaily.id) { + // Add existing deltas + daily.deltaStalkEarnedPerSeason = daily.deltaStalkEarnedPerSeason.plus(baseDaily.deltaStalkEarnedPerSeason); + daily.deltaStalkIssuedPerBdv = daily.deltaStalkIssuedPerBdv.plus(baseDaily.deltaStalkIssuedPerBdv); + daily.deltaMilestoneSeason = daily.deltaMilestoneSeason + baseDaily.deltaMilestoneSeason; + if (daily.deltaGaugePoints !== null && baseDaily.deltaGaugePoints !== null) { + daily.deltaGaugePoints = daily.deltaGaugePoints!.plus(baseDaily.deltaGaugePoints!); + } + if (daily.deltaOptimalPercentDepositedBdv !== null && baseDaily.deltaOptimalPercentDepositedBdv !== null) { + daily.deltaOptimalPercentDepositedBdv = daily.deltaOptimalPercentDepositedBdv!.plus(baseDaily.deltaOptimalPercentDepositedBdv!); + } + } + } else { + daily.deltaStalkEarnedPerSeason = daily.stalkEarnedPerSeason; + daily.deltaStalkIssuedPerBdv = daily.stalkIssuedPerBdv; + daily.deltaMilestoneSeason = daily.milestoneSeason; + daily.deltaGaugePoints = daily.gaugePoints; + daily.deltaOptimalPercentDepositedBdv = daily.optimalPercentDepositedBdv; + } + daily.createdAt = day; + daily.updatedAt = timestamp; + daily.save(); + + whitelistTokenSetting.lastHourlySnapshotSeason = currentSeason; + whitelistTokenSetting.lastDailySnapshotDay = day; +} + +// Set bdv on hourly and daily. Snapshots must have already been created. +export function setBdv(bdv: BigInt, whitelistTokenSetting: WhitelistTokenSetting): void { + const hourly = WhitelistTokenHourlySnapshot.load( + whitelistTokenSetting.id.toHexString() + "-" + whitelistTokenSetting.lastHourlySnapshotSeason.toString() + )!; + const daily = WhitelistTokenDailySnapshot.load( + whitelistTokenSetting.id.toHexString() + "-" + whitelistTokenSetting.lastDailySnapshotDay!.toString() + )!; + + hourly.bdv = bdv; + daily.bdv = bdv; + + // Delta cannot be managed by the default snapshot method because the bdv is unsuitable for calculation during that + // method (contract call). Previous season's snapshots can be accessed by subtracting one + // (the current season snapshots were already created) + const prevHourly = WhitelistTokenHourlySnapshot.load( + whitelistTokenSetting.id.toHexString() + "-" + (whitelistTokenSetting.lastHourlySnapshotSeason - 1).toString() + ); + const prevDaily = WhitelistTokenDailySnapshot.load( + whitelistTokenSetting.id.toHexString() + "-" + (whitelistTokenSetting.lastDailySnapshotDay!.toI32() - 1).toString() + ); + + if (prevHourly != null && prevHourly.bdv !== null) { + hourly.deltaBdv = hourly.bdv!.minus(prevHourly.bdv!); + } else { + hourly.deltaBdv = hourly.bdv; + } + if (prevDaily != null && prevDaily.bdv !== null) { + daily.deltaBdv = daily.bdv!.minus(prevDaily.bdv!); + } else { + daily.deltaBdv = daily.bdv; + } + + hourly.save(); + daily.save(); +} + +// Returns the latest hourly bdv for the requested token. Can be null if bdv function isnt implemented onchain yet. +export function getLatestBdv(whitelistTokenSetting: WhitelistTokenSetting): BigInt | null { + const hourly = WhitelistTokenHourlySnapshot.load( + whitelistTokenSetting.id.toHexString() + "-" + whitelistTokenSetting.lastHourlySnapshotSeason.toString() + )!; + return hourly.bdv; +} diff --git a/projects/subgraph-beanstalk/src/handlers/BarnHandler.ts b/projects/subgraph-beanstalk/src/handlers/BarnHandler.ts new file mode 100644 index 0000000000..c8f61efb89 --- /dev/null +++ b/projects/subgraph-beanstalk/src/handlers/BarnHandler.ts @@ -0,0 +1,36 @@ +import { Address, BigInt, log } from "@graphprotocol/graph-ts"; +import { ChangeUnderlying, Chop } from "../../generated/Beanstalk-ABIs/SeedGauge"; +import { TransferSingle, TransferBatch } from "../../generated/Beanstalk-ABIs/Fertilizer"; +import { loadUnripeToken } from "../entities/Silo"; +import { transfer, unripeChopped, updateUnripeStats } from "../utils/Barn"; + +export function handleTransferSingle(event: TransferSingle): void { + transfer(event.address, event.params.from, event.params.to, event.params.id, event.params.value, event.block.number); +} + +export function handleTransferBatch(event: TransferBatch): void { + for (let i = 0; i < event.params.ids.length; i++) { + let id = event.params.ids[i]; + let amount = event.params.values[i]; + transfer(event.address, event.params.from, event.params.to, id, amount, event.block.number); + } +} + +export function handleChangeUnderlying(event: ChangeUnderlying): void { + const unripe = loadUnripeToken(event.params.token); + unripe.totalUnderlying = unripe.totalUnderlying.plus(event.params.underlying); + unripe.save(); + + updateUnripeStats(Address.fromBytes(unripe.id), event.address, event.block); +} + +export function handleChop(event: Chop): void { + unripeChopped({ + event, + type: "chop", + account: event.params.account, + unripeToken: event.params.token, + unripeAmount: event.params.amount, + underlyingAmount: event.params.underlying + }); +} diff --git a/projects/subgraph-beanstalk/src/handlers/BeanHandler.ts b/projects/subgraph-beanstalk/src/handlers/BeanHandler.ts new file mode 100644 index 0000000000..1569946103 --- /dev/null +++ b/projects/subgraph-beanstalk/src/handlers/BeanHandler.ts @@ -0,0 +1,32 @@ +import { BigDecimal, BigInt, ethereum, log } from "@graphprotocol/graph-ts"; +import { Transfer } from "../../generated/Beanstalk-ABIs/ERC20"; +import { ADDRESS_ZERO, BEANSTALK } from "../../../subgraph-core/utils/Constants"; +import { loadBeanstalk, loadSeason } from "../entities/Beanstalk"; +import { getTokenProtocol } from "../utils/Constants"; +import { ZERO_BI } from "../../../subgraph-core/utils/Decimals"; + +export function handleTransfer(event: Transfer): void { + if (event.params.from == ADDRESS_ZERO || event.params.to == ADDRESS_ZERO) { + let beanstalk = loadBeanstalk(getTokenProtocol(event.address)); + let season = loadSeason(getTokenProtocol(event.address), BigInt.fromI32(beanstalk.lastSeason)); + + if (event.params.from == ADDRESS_ZERO) { + season.deltaBeans = season.deltaBeans.plus(event.params.value); + season.beans = season.beans.plus(event.params.value); + } else { + season.deltaBeans = season.deltaBeans.minus(event.params.value); + season.beans = season.beans.minus(event.params.value); + } + season.save(); + } +} + +export function handleExploit(block: ethereum.Block): void { + let beanstalk = loadBeanstalk(BEANSTALK); + let season = loadSeason(BEANSTALK, BigInt.fromI32(beanstalk.lastSeason)); + season.deltaBeans = ZERO_BI; + season.beans = ZERO_BI; + season.price = BigDecimal.fromString("1.022"); + season.save(); + return; +} diff --git a/projects/subgraph-beanstalk/src/handlers/FarmHandler.ts b/projects/subgraph-beanstalk/src/handlers/FarmHandler.ts new file mode 100644 index 0000000000..16d443c2e8 --- /dev/null +++ b/projects/subgraph-beanstalk/src/handlers/FarmHandler.ts @@ -0,0 +1,27 @@ +import { Address, BigInt } from "@graphprotocol/graph-ts"; +import { InternalBalanceChanged } from "../../generated/Beanstalk-ABIs/SeedGauge"; +import { takeSiloAssetSnapshots } from "../entities/snapshots/SiloAsset"; +import { loadSiloAsset } from "../entities/Silo"; +import { loadFarmer } from "../entities/Beanstalk"; + +export function handleInternalBalanceChanged(event: InternalBalanceChanged): void { + loadFarmer(event.params.user); + updateFarmTotals(event.address, event.params.user, event.params.token, event.params.delta, event.block.timestamp); +} + +function updateFarmTotals( + protocol: Address, + account: Address, + token: Address, + deltaAmount: BigInt, + timestamp: BigInt, + recursive: boolean = true +): void { + if (recursive && account != protocol) { + updateFarmTotals(protocol, protocol, token, deltaAmount, timestamp); + } + let asset = loadSiloAsset(account, token); + asset.farmAmount = asset.farmAmount.plus(deltaAmount); + takeSiloAssetSnapshots(asset, protocol, timestamp); + asset.save(); +} diff --git a/projects/subgraph-beanstalk/src/handlers/FieldHandler.ts b/projects/subgraph-beanstalk/src/handlers/FieldHandler.ts new file mode 100644 index 0000000000..6477e27923 --- /dev/null +++ b/projects/subgraph-beanstalk/src/handlers/FieldHandler.ts @@ -0,0 +1,51 @@ +import { BigInt, log } from "@graphprotocol/graph-ts"; +import { BEANSTALK_FARMS } from "../../../subgraph-core/utils/Constants"; +import { loadField } from "../entities/Field"; +import { harvest, plotTransfer, sow, temperatureChanged } from "../utils/Field"; +import { Sow, Harvest, PlotTransfer, TemperatureChange } from "../../generated/Beanstalk-ABIs/SeedGauge"; + +export function handleSow(event: Sow): void { + let sownOverride: BigInt | null = null; + if (event.params.account == BEANSTALK_FARMS) { + let startingField = loadField(event.address); + sownOverride = startingField.soil; + } + sow({ + event, + account: event.params.account, + fieldId: null, + index: event.params.index, + beans: sownOverride !== null ? sownOverride : event.params.beans, + pods: event.params.pods + }); +} + +export function handleHarvest(event: Harvest): void { + harvest({ + event, + account: event.params.account, + fieldId: null, + plots: event.params.plots, + beans: event.params.beans + }); +} + +export function handlePlotTransfer(event: PlotTransfer): void { + plotTransfer({ + event, + from: event.params.from, + to: event.params.to, + fieldId: null, + index: event.params.id, + amount: event.params.pods + }); +} + +export function handleTemperatureChange(event: TemperatureChange): void { + temperatureChanged({ + event, + season: event.params.season, + caseId: event.params.caseId, + absChange: event.params.absChange + }); +} diff --git a/projects/subgraph-beanstalk/src/handlers/GaugeHandler.ts b/projects/subgraph-beanstalk/src/handlers/GaugeHandler.ts new file mode 100644 index 0000000000..a40d231f0c --- /dev/null +++ b/projects/subgraph-beanstalk/src/handlers/GaugeHandler.ts @@ -0,0 +1,182 @@ +import { + BeanToMaxLpGpPerBdvRatioChange, + GaugePointChange, + UpdateAverageStalkPerBdvPerSeason, + FarmerGerminatingStalkBalanceChanged, + TotalGerminatingBalanceChanged, + UpdateGaugeSettings, + TotalGerminatingStalkChanged, + TotalStalkChangedFromGermination +} from "../../generated/Beanstalk-ABIs/SeedGauge"; +import { + deleteGerminating, + germinationEnumCategory, + germinationSeasonCategory, + getFarmerGerminatingBugOffset, + loadGerminating, + loadOrCreateGerminating, + savePrevFarmerGerminatingEvent +} from "../entities/Germinating"; +import { BI_10, ZERO_BI } from "../../../subgraph-core/utils/Decimals"; +import { BEAN_WETH_CP2_WELL } from "../../../subgraph-core/utils/Constants"; +import { Bytes4_emptyToNull } from "../../../subgraph-core/utils/Bytes"; +import { setSiloHourlyCaseId, takeSiloSnapshots } from "../entities/snapshots/Silo"; +import { loadSilo, loadWhitelistTokenSetting } from "../entities/Silo"; +import { takeWhitelistTokenSettingSnapshots } from "../entities/snapshots/WhitelistTokenSetting"; +import { getCurrentSeason } from "../entities/Beanstalk"; +import { updateStalkBalances } from "../utils/Silo"; + +// SEED GAUGE SEASONAL ADJUSTMENTS // + +export function handleBeanToMaxLpGpPerBdvRatioChange(event: BeanToMaxLpGpPerBdvRatioChange): void { + let silo = loadSilo(event.address); + + if (silo.beanToMaxLpGpPerBdvRatio === null) { + silo.beanToMaxLpGpPerBdvRatio = event.params.absChange; + } else { + silo.beanToMaxLpGpPerBdvRatio = silo.beanToMaxLpGpPerBdvRatio!.plus(event.params.absChange); + } + takeSiloSnapshots(silo, event.address, event.block.timestamp); + setSiloHourlyCaseId(event.params.caseId, silo); + silo.save(); +} + +export function handleGaugePointChange(event: GaugePointChange): void { + let siloSettings = loadWhitelistTokenSetting(event.params.token); + siloSettings.gaugePoints = event.params.gaugePoints; + siloSettings.updatedAt = event.block.timestamp; + + takeWhitelistTokenSettingSnapshots(siloSettings, event.address, event.block.timestamp); + siloSettings.save(); +} + +export function handleUpdateAverageStalkPerBdvPerSeason(event: UpdateAverageStalkPerBdvPerSeason): void { + let silo = loadSilo(event.address); + + // This is not exactly accurate, the value in this event is pertaining to gauge only and does not include unripe. + // In practice, seed values for non-gauge assets are negligible. + // The correct approach is iterating whitelisted assets each season, multipying bdv and seeds + silo.grownStalkPerSeason = silo.depositedBDV.times(event.params.newStalkPerBdvPerSeason); + takeSiloSnapshots(silo, event.address, event.block.timestamp); + silo.save(); + + // Individual asset grown stalk is set by the UpdatedStalkPerBdvPerSeason event. +} + +// GERMINATING STALK // + +// Tracks germinating balances for individual famers +export function handleFarmerGerminatingStalkBalanceChanged(event: FarmerGerminatingStalkBalanceChanged): void { + if (event.params.deltaGerminatingStalk == ZERO_BI) { + return; + } + + const currentSeason = getCurrentSeason(event.address); + + if (event.params.deltaGerminatingStalk > ZERO_BI) { + // Germinating stalk is added. It is possible to begin germination in the prior season rather than the + // current season when converting. See ConvertFacet._depositTokensForConvert for more information. + // If the event's germinationState doesnt match with the current season, use the prior season. + const germinatingSeason = + germinationSeasonCategory(currentSeason) === germinationEnumCategory(event.params.germinationState) + ? currentSeason + : currentSeason - 1; + + let farmerGerminating = loadOrCreateGerminating(event.params.account, germinatingSeason, true); + farmerGerminating.stalk = farmerGerminating.stalk.plus(event.params.deltaGerminatingStalk); + farmerGerminating.save(); + } else { + // Adjusts for the event's inherent bug when both even/odd germination complete in the same txn + const bugfixStalkOffset = getFarmerGerminatingBugOffset(event.params.account, event); + const actualDeltaGerminatingStalk = event.params.deltaGerminatingStalk.plus(bugfixStalkOffset); + + // Germinating stalk is being removed. It therefore must have created the entity already + let farmerGerminating = loadGerminating(event.params.account, event.params.germinationState); + farmerGerminating.stalk = farmerGerminating.stalk.plus(actualDeltaGerminatingStalk); + if (farmerGerminating.stalk == ZERO_BI) { + deleteGerminating(farmerGerminating); + } else { + farmerGerminating.save(); + } + + if (currentSeason >= farmerGerminating.season + 2) { + // If germination finished, need to subtract stalk from system silo. This stalk was already added + // into system stalk upon sunrise for season - 2. + let systemSilo = loadSilo(event.address); + systemSilo.stalk = systemSilo.stalk.plus(actualDeltaGerminatingStalk); + takeSiloSnapshots(systemSilo, event.address, event.block.timestamp); + systemSilo.save(); + } + + // Also for the event bug adjustment + savePrevFarmerGerminatingEvent(event.params.account, event, event.params.deltaGerminatingStalk); + } + + let farmerSilo = loadSilo(event.params.account); + farmerSilo.germinatingStalk = farmerSilo.germinatingStalk.plus(event.params.deltaGerminatingStalk); + takeSiloSnapshots(farmerSilo, event.address, event.block.timestamp); + farmerSilo.save(); +} + +// Tracks the germinating balance on a token level +export function handleTotalGerminatingBalanceChanged(event: TotalGerminatingBalanceChanged): void { + if (event.params.deltaAmount == ZERO_BI && event.params.deltaBdv == ZERO_BI) { + return; + } + + let tokenGerminating = loadOrCreateGerminating(event.params.token, event.params.germinationSeason.toU32(), false); + tokenGerminating.season = event.params.germinationSeason.toU32(); + tokenGerminating.tokenAmount = tokenGerminating.tokenAmount.plus(event.params.deltaAmount); + tokenGerminating.bdv = tokenGerminating.bdv.plus(event.params.deltaBdv); + if (tokenGerminating.tokenAmount == ZERO_BI) { + deleteGerminating(tokenGerminating); + } else { + tokenGerminating.save(); + } +} + +// This occurs at the beanstalk level regardless of whether users mow their own germinating stalk into regular stalk. +// It can also occur if a user withdraws early, before the germinating period completes. +export function handleTotalGerminatingStalkChanged(event: TotalGerminatingStalkChanged): void { + if (event.params.deltaGerminatingStalk == ZERO_BI) { + return; + } + + let siloGerminating = loadOrCreateGerminating(event.address, event.params.germinationSeason.toU32(), false); + siloGerminating.season = event.params.germinationSeason.toU32(); + siloGerminating.stalk = siloGerminating.stalk.plus(event.params.deltaGerminatingStalk); + // Don't delete this entity as the overall silo germinating stalk entity is likely to be recreated frequently. + siloGerminating.save(); + + let silo = loadSilo(event.address); + silo.germinatingStalk = silo.germinatingStalk.plus(event.params.deltaGerminatingStalk); + takeSiloSnapshots(silo, event.address, event.block.timestamp); + silo.save(); +} + +// Germination completes, germinating stalk turns into stalk. +// The removal of Germinating stalk would have already been handled from a separate emission +export function handleTotalStalkChangedFromGermination(event: TotalStalkChangedFromGermination): void { + updateStalkBalances(event.address, event.address, event.params.deltaStalk, event.params.deltaRoots, event.block.timestamp); +} + +// GAUGE CONFIGURATION SETTINGS // + +export function handleUpdateGaugeSettings(event: UpdateGaugeSettings): void { + let siloSettings = loadWhitelistTokenSetting(event.params.token); + siloSettings.gpSelector = Bytes4_emptyToNull(event.params.gpSelector); + siloSettings.lwSelector = Bytes4_emptyToNull(event.params.lwSelector); + siloSettings.optimalPercentDepositedBdv = event.params.optimalPercentDepositedBdv; + siloSettings.updatedAt = event.block.timestamp; + + // On initial gauge deployment, there was no GaugePointChange event emitted. Need to initialize BEANETH here + if ( + event.params.token == BEAN_WETH_CP2_WELL && + event.transaction.hash.toHexString().toLowerCase() == "0x299a4b93b8d19f8587b648ce04e3f5e618ea461426bb2b2337993b5d6677f6a7" + ) { + siloSettings.gaugePoints = BI_10.pow(20); + } + + takeWhitelistTokenSettingSnapshots(siloSettings, event.address, event.block.timestamp); + siloSettings.save(); +} diff --git a/projects/subgraph-beanstalk/src/handlers/MarketplaceHandler.ts b/projects/subgraph-beanstalk/src/handlers/MarketplaceHandler.ts new file mode 100644 index 0000000000..95b5c624be --- /dev/null +++ b/projects/subgraph-beanstalk/src/handlers/MarketplaceHandler.ts @@ -0,0 +1,102 @@ +import { Address, BigInt, Bytes, ethereum, log } from "@graphprotocol/graph-ts"; +import { + PodListingCreated, + PodListingFilled, + PodOrderCreated, + PodOrderFilled, + PodListingCancelled, + PodOrderCancelled +} from "../../generated/Beanstalk-ABIs/SeedGauge"; +import { + PodListingCancelled as PodListingCancelledEvent, + PodOrderCancelled as PodOrderCancelledEvent, + PodOrder, + PodListing +} from "../../generated/schema"; +import { ZERO_BI } from "../../../subgraph-core/utils/Decimals"; +import { + MarketplaceAction, + podListingCancelled, + podListingCreated, + podListingFilled, + podOrderCancelled, + podOrderCreated, + podOrderFilled, + updateActiveListings, + updateActiveOrders, + updateMarketListingBalances, + updateMarketOrderBalances +} from "../utils/Marketplace"; +import { getHarvestableIndex } from "../entities/Beanstalk"; + +export function handlePodListingCreated(event: PodListingCreated): void { + podListingCreated({ + event: event, + account: event.params.account, + index: event.params.index, + start: event.params.start, + amount: event.params.amount, + pricePerPod: event.params.pricePerPod, + maxHarvestableIndex: event.params.maxHarvestableIndex, + mode: event.params.mode, + minFillAmount: event.params.minFillAmount, + pricingFunction: event.params.pricingFunction, + pricingType: event.params.pricingType + }); +} + +export function handlePodListingFilled(event: PodListingFilled): void { + podListingFilled({ + event: event, + from: event.params.from, + to: event.params.to, + id: null, + index: event.params.index, + start: event.params.start, + amount: event.params.amount, + costInBeans: event.params.costInBeans + }); +} + +export function handlePodOrderCreated(event: PodOrderCreated): void { + podOrderCreated({ + event: event, + account: event.params.account, + id: event.params.id, + beanAmount: event.params.amount, + pricePerPod: event.params.pricePerPod, + maxPlaceInLine: event.params.maxPlaceInLine, + minFillAmount: event.params.minFillAmount, + pricingFunction: event.params.pricingFunction, + pricingType: event.params.priceType + }); +} + +export function handlePodOrderFilled(event: PodOrderFilled): void { + podOrderFilled({ + event: event, + from: event.params.from, + to: event.params.to, + id: event.params.id, + index: event.params.index, + start: event.params.start, + amount: event.params.amount, + costInBeans: event.params.costInBeans + }); +} + +export function handlePodListingCancelled(event: PodListingCancelled): void { + podListingCancelled({ + event, + account: event.params.account, + index: event.params.index + }); +} + +export function handlePodOrderCancelled(event: PodOrderCancelled): void { + podOrderCancelled({ + event, + account: event.params.account, + id: event.params.id + }); +} diff --git a/projects/subgraph-beanstalk/src/handlers/SeasonHandler.ts b/projects/subgraph-beanstalk/src/handlers/SeasonHandler.ts new file mode 100644 index 0000000000..16f005acd2 --- /dev/null +++ b/projects/subgraph-beanstalk/src/handlers/SeasonHandler.ts @@ -0,0 +1,96 @@ +import { Address, BigInt } from "@graphprotocol/graph-ts"; +import { Reward, Soil, WellOracle, Sunrise, Incentivization, SeedGauge } from "../../generated/Beanstalk-ABIs/SeedGauge"; +import { BEANSTALK, GAUGE_BIP45_BLOCK, REPLANT_SEASON } from "../../../subgraph-core/utils/Constants"; +import { toDecimal, ZERO_BD } from "../../../subgraph-core/utils/Decimals"; +import { updateStalkWithCalls } from "../utils/legacy/LegacySilo"; +import { loadBeanstalk, loadSeason } from "../entities/Beanstalk"; +import { loadSilo } from "../entities/Silo"; +import { takeSiloSnapshots } from "../entities/snapshots/Silo"; +import { updateDepositInSiloAsset } from "../utils/Silo"; +import { getBeanstalkPrice } from "../utils/contracts/BeanstalkPrice"; +import { takeFieldSnapshots } from "../entities/snapshots/Field"; +import { loadField } from "../entities/Field"; +import { updateBeanEMA } from "../utils/Yield"; +import { updateExpiredPlots } from "../utils/Marketplace"; +import { updateHarvestablePlots } from "../utils/Field"; +import { getProtocolToken } from "../utils/Constants"; +import { sunrise } from "../utils/Season"; + +export function handleSunrise(event: Sunrise): void { + // (Legacy) Update any farmers that had silo transfers from the prior season. + // This is intentionally done before beanstalk.lastSeason gets updated + updateStalkWithCalls(event.address, event.block.timestamp); + + sunrise(event.address, event.params.season, event.block); +} + +export function handleReward(event: Reward): void { + let season = loadSeason(event.address, event.params.season); + season.rewardBeans = event.params.toField.plus(event.params.toSilo).plus(event.params.toFertilizer); + season.save(); + + // Add to total Silo Bean mints + + let silo = loadSilo(event.address); + let newPlantableStalk = event.params.toSilo.times(BigInt.fromI32(10000)); // Stalk has 10 decimals + + silo.beanMints = silo.beanMints.plus(event.params.toSilo); + silo.stalk = silo.stalk.plus(newPlantableStalk); + silo.plantableStalk = silo.plantableStalk.plus(newPlantableStalk); + silo.depositedBDV = silo.depositedBDV.plus(event.params.toSilo); + + takeSiloSnapshots(silo, event.address, event.block.timestamp); + silo.save(); + + updateDepositInSiloAsset( + event.address, + event.address, + getProtocolToken(event.address), + event.params.toSilo, + event.params.toSilo, + event.block.timestamp + ); +} + +export function handleWellOracle(event: WellOracle): void { + let season = loadSeason(event.address, event.params.season); + season.deltaB = season.deltaB.plus(event.params.deltaB); + if (event.block.number >= GAUGE_BIP45_BLOCK && season.price == ZERO_BD) { + let beanstalkPrice = getBeanstalkPrice(event.block.number); + let beanstalkQuery = beanstalkPrice.getConstantProductWell(event.params.well); + season.price = toDecimal(beanstalkQuery.price); + } + season.save(); +} + +export function handleSoil(event: Soil): void { + // Replant sets the soil to the amount every season instead of adding new soil + // to an existing amount. + + let field = loadField(event.address); + field.season = event.params.season.toI32(); + field.soil = event.params.soil; + + takeFieldSnapshots(field, event.address, event.block.timestamp, event.block.number); + field.save(); + + if (event.params.season >= REPLANT_SEASON) { + updateBeanEMA(event.address, event.block.timestamp); + } +} + +// This is the final function to be called during sunrise both pre and post replant +export function handleIncentive(event: Incentivization): void { + // Update market cap for season + let beanstalk = loadBeanstalk(event.address); + let beanstalk_contract = SeedGauge.bind(BEANSTALK); + let season = loadSeason(event.address, BigInt.fromI32(beanstalk.lastSeason)); + + season.marketCap = season.price.times(toDecimal(season.beans)); + season.incentiveBeans = event.params.beans; + season.harvestableIndex = beanstalk_contract.harvestableIndex(); + season.save(); + + updateExpiredPlots(event.address, season.harvestableIndex, event.block.timestamp); + updateHarvestablePlots(event.address, season.harvestableIndex, event.block.timestamp, event.block.number); +} diff --git a/projects/subgraph-beanstalk/src/handlers/SiloHandler.ts b/projects/subgraph-beanstalk/src/handlers/SiloHandler.ts new file mode 100644 index 0000000000..2a2787c556 --- /dev/null +++ b/projects/subgraph-beanstalk/src/handlers/SiloHandler.ts @@ -0,0 +1,172 @@ +import { BigInt, log } from "@graphprotocol/graph-ts"; +import { addDeposits, removeDeposits, updateDepositInSiloAsset, updateSeedsBalances, updateStalkBalances } from "../utils/Silo"; +import { addToSiloWhitelist, loadSilo, loadWhitelistTokenSetting } from "../entities/Silo"; +import { takeSiloSnapshots } from "../entities/snapshots/Silo"; +import { takeWhitelistTokenSettingSnapshots } from "../entities/snapshots/WhitelistTokenSetting"; +import { Bytes4_emptyToNull } from "../../../subgraph-core/utils/Bytes"; +import { + AddDeposit, + Convert, + DewhitelistToken, + Plant, + RemoveDeposit, + RemoveDeposits, + RemoveWithdrawal, + RemoveWithdrawals, + SeedsBalanceChanged, + StalkBalanceChanged, + UpdatedStalkPerBdvPerSeason, + WhitelistToken +} from "../../generated/Beanstalk-ABIs/SeedGauge"; +import { updateClaimedWithdraw } from "../utils/legacy/LegacySilo"; +import { getProtocolToken, isUnripe } from "../utils/Constants"; +import { unripeChopped } from "../utils/Barn"; + +export function handleAddDeposit(event: AddDeposit): void { + addDeposits({ + event, + account: event.params.account, + token: event.params.token, + seasons: null, + stems: [event.params.stem], + amounts: [event.params.amount], + bdvs: [event.params.bdv], + depositVersion: "stem" + }); +} + +export function handleRemoveDeposit(event: RemoveDeposit): void { + removeDeposits({ + event, + account: event.params.account, + token: event.params.token, + seasons: null, + stems: [event.params.stem], + amounts: [event.params.amount], + bdvs: [event.params.bdv], + depositVersion: "stem" + }); +} + +export function handleRemoveDeposits(event: RemoveDeposits): void { + removeDeposits({ + event, + account: event.params.account, + token: event.params.token, + seasons: null, + stems: event.params.stems, + amounts: event.params.amounts, + bdvs: event.params.bdvs, + depositVersion: "stem" + }); +} + +export function handleConvert(event: Convert): void { + if (isUnripe(event.params.fromToken) && !isUnripe(event.params.toToken)) { + unripeChopped({ + event, + type: "convert", + account: event.params.account, + unripeToken: event.params.fromToken, + unripeAmount: event.params.fromAmount, + underlyingAmount: event.params.toAmount + }); + } +} + +export function handleStalkBalanceChanged(event: StalkBalanceChanged): void { + // Exclude BIP-24 emission of missed past events + if (event.transaction.hash.toHexString() == "0xa89638aeb0d6c4afb4f367ea7a806a4c8b3b2a6eeac773e8cc4eda10bfa804fc") { + return; + } + + updateStalkBalances(event.address, event.params.account, event.params.delta, event.params.deltaRoots, event.block.timestamp); +} + +export function handleSeedsBalanceChanged(event: SeedsBalanceChanged): void { + // Exclude BIP-24 emission of missed past events + if (event.transaction.hash.toHexString() == "0xa89638aeb0d6c4afb4f367ea7a806a4c8b3b2a6eeac773e8cc4eda10bfa804fc") { + return; + } + + updateSeedsBalances(event.address, event.params.account, event.params.delta, event.block.timestamp); +} + +export function handlePlant(event: Plant): void { + // This removes the plantable stalk for planted beans. + // Actual stalk credit for the farmer will be handled under the StalkBalanceChanged event. + + let silo = loadSilo(event.address); + let newPlantableStalk = event.params.beans.times(BigInt.fromI32(10000)); + + // Subtract stalk since it was already added in Reward, and is about to get re-added in StalkBalanceChanged. + silo.stalk = silo.stalk.minus(newPlantableStalk); + silo.plantableStalk = silo.plantableStalk.minus(newPlantableStalk); + silo.depositedBDV = silo.depositedBDV.minus(event.params.beans); + + takeSiloSnapshots(silo, event.address, event.block.timestamp); + silo.save(); + + // Remove the asset-only amount that got added in Reward event handler. + // Will be immediately re-credited to the user/system in AddDeposit + updateDepositInSiloAsset( + event.address, + event.address, + getProtocolToken(event.address), + event.params.beans, + event.params.beans, + event.block.timestamp + ); +} + +export function handleWhitelistToken(event: WhitelistToken): void { + addToSiloWhitelist(event.address, event.params.token); + + let siloSettings = loadWhitelistTokenSetting(event.params.token); + + siloSettings.selector = event.params.selector; + siloSettings.stalkEarnedPerSeason = event.params.stalkEarnedPerSeason; + siloSettings.stalkIssuedPerBdv = event.params.stalkIssuedPerBdv; + siloSettings.gaugePoints = event.params.gaugePoints; + siloSettings.gpSelector = Bytes4_emptyToNull(event.params.gpSelector); + siloSettings.lwSelector = Bytes4_emptyToNull(event.params.lwSelector); + siloSettings.optimalPercentDepositedBdv = event.params.optimalPercentDepositedBdv; + siloSettings.updatedAt = event.block.timestamp; + + takeWhitelistTokenSettingSnapshots(siloSettings, event.address, event.block.timestamp); + siloSettings.save(); +} + +export function handleDewhitelistToken(event: DewhitelistToken): void { + let silo = loadSilo(event.address); + let currentWhitelist = silo.whitelistedTokens; + let currentDewhitelist = silo.dewhitelistedTokens; + let index = currentWhitelist.indexOf(event.params.token.toHexString()); + if (index >= 0) { + currentDewhitelist.push(currentWhitelist.splice(index, 1)[0]); + silo.whitelistedTokens = currentWhitelist; + silo.dewhitelistedTokens = currentDewhitelist; + silo.save(); + } +} + +export function handleUpdatedStalkPerBdvPerSeason(event: UpdatedStalkPerBdvPerSeason): void { + let siloSettings = loadWhitelistTokenSetting(event.params.token); + siloSettings.milestoneSeason = event.params.season.toI32(); + siloSettings.stalkEarnedPerSeason = event.params.stalkEarnedPerSeason; + siloSettings.updatedAt = event.block.timestamp; + + takeWhitelistTokenSettingSnapshots(siloSettings, event.address, event.block.timestamp); + siloSettings.save(); +} + +// Withdrawal is a legacy feature from replant, but these events are still present +export function handleRemoveWithdrawal(event: RemoveWithdrawal): void { + updateClaimedWithdraw(event.address, event.params.account, event.params.token, event.params.season, event.block.timestamp); +} + +export function handleRemoveWithdrawals(event: RemoveWithdrawals): void { + for (let i = 0; i < event.params.seasons.length; i++) { + updateClaimedWithdraw(event.address, event.params.account, event.params.token, event.params.seasons[i], event.block.timestamp); + } +} diff --git a/projects/subgraph-beanstalk/src/handlers/legacy/LegacyFieldHandler.ts b/projects/subgraph-beanstalk/src/handlers/legacy/LegacyFieldHandler.ts new file mode 100644 index 0000000000..f2d969a43c --- /dev/null +++ b/projects/subgraph-beanstalk/src/handlers/legacy/LegacyFieldHandler.ts @@ -0,0 +1,78 @@ +import { ZERO_BI } from "../../../../subgraph-core/utils/Decimals"; +import { WeatherChange, SupplyIncrease, SupplyDecrease, SupplyNeutral, FundFundraiser } from "../../../generated/Beanstalk-ABIs/PreReplant"; +import { temperatureChanged, updateFieldTotals } from "../../utils/Field"; + +// PreReplant -> SeedGauge +export function handleWeatherChange(event: WeatherChange): void { + temperatureChanged({ + event, + season: event.params.season, + caseId: event.params.caseId, + absChange: event.params.change + }); +} + +// PreReplant -> Replanted +export function handleSupplyIncrease(event: SupplyIncrease): void { + updateFieldTotals( + event.address, + event.address, + event.params.newSoil, + ZERO_BI, + ZERO_BI, + ZERO_BI, + ZERO_BI, + ZERO_BI, + event.block.timestamp, + event.block.number + ); +} + +// PreReplant -> Replanted +export function handleSupplyDecrease(event: SupplyDecrease): void { + updateFieldTotals( + event.address, + event.address, + event.params.newSoil, + ZERO_BI, + ZERO_BI, + ZERO_BI, + ZERO_BI, + ZERO_BI, + event.block.timestamp, + event.block.number + ); +} + +// PreReplant -> Replanted +export function handleSupplyNeutral(event: SupplyNeutral): void { + updateFieldTotals( + event.address, + event.address, + event.params.newSoil, + ZERO_BI, + ZERO_BI, + ZERO_BI, + ZERO_BI, + ZERO_BI, + event.block.timestamp, + event.block.number + ); +} + +// PreReplant -> Replanted +export function handleFundFundraiser(event: FundFundraiser): void { + // Account for the fact that fundraiser sow using no soil. + updateFieldTotals( + event.address, + event.address, + ZERO_BI, + ZERO_BI.minus(event.params.amount), + ZERO_BI, + ZERO_BI, + ZERO_BI, + ZERO_BI, + event.block.timestamp, + event.block.number + ); +} diff --git a/projects/subgraph-beanstalk/src/handlers/legacy/LegacyMarketplaceHandler.ts b/projects/subgraph-beanstalk/src/handlers/legacy/LegacyMarketplaceHandler.ts new file mode 100644 index 0000000000..a14326439b --- /dev/null +++ b/projects/subgraph-beanstalk/src/handlers/legacy/LegacyMarketplaceHandler.ts @@ -0,0 +1,107 @@ +import { Address, BigInt, Bytes, ethereum, log } from "@graphprotocol/graph-ts"; +import { + PodListingCreated as PodListingCreated_v1, + PodListingFilled as PodListingFilled_v1, + PodOrderCreated as PodOrderCreated_v1, + PodOrderFilled as PodOrderFilled_v1, + PodListingCancelled as PodListingCancelled_indexed +} from "../../../generated/Beanstalk-ABIs/PreReplant"; +import { PodListingCreated as PodListingCreated_v1_1 } from "../../../generated/Beanstalk-ABIs/Replanted"; +import { podListingCancelled, podListingCreated, podListingFilled, podOrderCreated, podOrderFilled } from "../../utils/Marketplace"; +import { ZERO_BI } from "../../../../subgraph-core/utils/Decimals"; +import { loadPodListing, loadPodOrder } from "../../entities/PodMarketplace"; + +// PreReplant -> Replanted +export function handlePodListingCreated_v1(event: PodListingCreated_v1): void { + podListingCreated({ + event: event, + account: event.params.account, + index: event.params.index, + start: event.params.start, + amount: event.params.amount, + pricePerPod: event.params.pricePerPod, + maxHarvestableIndex: event.params.maxHarvestableIndex, + mode: event.params.toWallet ? 0 : 1, + minFillAmount: ZERO_BI, + pricingFunction: null, + pricingType: 0 + }); +} + +// Replanted -> MarketV2 +// When Beanstalk was Replanted, event.params.mode was changed from bool to uint8 +export function handlePodListingCreated_v1_1(event: PodListingCreated_v1_1): void { + podListingCreated({ + event: event, + account: event.params.account, + index: event.params.index, + start: event.params.start, + amount: event.params.amount, + pricePerPod: event.params.pricePerPod, + maxHarvestableIndex: event.params.maxHarvestableIndex, + mode: event.params.mode, + minFillAmount: ZERO_BI, + pricingFunction: null, + pricingType: 0 + }); +} + +// PreReplant -> MarketV2 +export function handlePodListingFilled_v1(event: PodListingFilled_v1): void { + let listing = loadPodListing(event.params.from, event.params.index); + const beanAmount = BigInt.fromI32(listing.pricePerPod).times(event.params.amount).div(BigInt.fromI32(1000000)); + + podListingFilled({ + event: event, + from: event.params.from, + to: event.params.to, + id: null, + index: event.params.index, + start: event.params.start, + amount: event.params.amount, + costInBeans: beanAmount + }); +} + +// Pre-Replant -> Replanted (but also emitted during the Replant in WTP-3) +// This event has a variety where the second parameter is indexed. Otherwise this event is identical to the other. +export function handlePodListingCancelled_indexed(event: PodListingCancelled_indexed): void { + podListingCancelled({ + event, + account: event.params.account, + index: event.params.index + }); +} + +// PreReplant -> MarketV2 +export function handlePodOrderCreated_v1(event: PodOrderCreated_v1): void { + const beanAmount = event.params.amount.times(BigInt.fromI32(event.params.pricePerPod)).div(BigInt.fromString("1000000")); + podOrderCreated({ + event: event, + account: event.params.account, + id: event.params.id, + beanAmount: beanAmount, + pricePerPod: event.params.pricePerPod, + maxPlaceInLine: event.params.maxPlaceInLine, + minFillAmount: ZERO_BI, + pricingFunction: null, + pricingType: 0 + }); +} + +// PreReplant -> MarketV2 +export function handlePodOrderFilled_v1(event: PodOrderFilled_v1): void { + let order = loadPodOrder(event.params.id); + let beanAmount = BigInt.fromI32(order.pricePerPod).times(event.params.amount).div(BigInt.fromI32(1000000)); + + podOrderFilled({ + event: event, + from: event.params.from, + to: event.params.to, + id: event.params.id, + index: event.params.index, + start: event.params.start, + amount: event.params.amount, + costInBeans: beanAmount + }); +} diff --git a/projects/subgraph-beanstalk/src/handlers/legacy/LegacySeasonHandler.ts b/projects/subgraph-beanstalk/src/handlers/legacy/LegacySeasonHandler.ts new file mode 100644 index 0000000000..f320b1d12e --- /dev/null +++ b/projects/subgraph-beanstalk/src/handlers/legacy/LegacySeasonHandler.ts @@ -0,0 +1,48 @@ +import { BigInt, BigDecimal } from "@graphprotocol/graph-ts"; +import { CURVE_PRICE, REPLANT_SEASON } from "../../../../subgraph-core/utils/Constants"; +import { toDecimal } from "../../../../subgraph-core/utils/Decimals"; +import { CurvePrice } from "../../../generated/Beanstalk-ABIs/CurvePrice"; +import { SeasonSnapshot, Sunrise } from "../../../generated/Beanstalk-ABIs/PreReplant"; +import { MetapoolOracle } from "../../../generated/Beanstalk-ABIs/Replanted"; +import { BeanstalkPrice_try_price } from "../../utils/contracts/BeanstalkPrice"; +import { loadSeason } from "../../entities/Beanstalk"; +import { updateStalkWithCalls } from "../../utils/legacy/LegacySilo"; +import { sunrise } from "../../utils/Season"; + +// Replanted -> SiloV3 +export function handleReplantSunrise(event: Sunrise): void { + // Update any farmers that had silo transfers from the prior season. + // This is intentionally done before beanstalk.lastSeason gets updated + updateStalkWithCalls(event.address, event.block.timestamp); + + // Replant oracle initialization + if (event.params.season == REPLANT_SEASON) { + let seasonEntity = loadSeason(event.address, event.params.season); + seasonEntity.price = BigDecimal.fromString("1.07"); + seasonEntity.save(); + } + + sunrise(event.address, event.params.season, event.block); +} + +// PreReplant -> Replanted +export function handleSeasonSnapshot(event: SeasonSnapshot): void { + let season = loadSeason(event.address, event.params.season); + season.price = toDecimal(event.params.price, 18); + season.save(); +} + +// Replanted -> SeedGauge +export function handleMetapoolOracle(event: MetapoolOracle): void { + let season = loadSeason(event.address, event.params.season); + // Attempt to pull from Beanstalk Price contract first + let beanstalkQuery = BeanstalkPrice_try_price(event.address, event.block.number); + if (beanstalkQuery.reverted) { + let curvePrice = CurvePrice.bind(CURVE_PRICE); + season.price = toDecimal(curvePrice.getCurve().price); + } else { + season.price = toDecimal(beanstalkQuery.value.price); + } + season.deltaB = event.params.deltaB; + season.save(); +} diff --git a/projects/subgraph-beanstalk/src/handlers/legacy/LegacySiloHandler.ts b/projects/subgraph-beanstalk/src/handlers/legacy/LegacySiloHandler.ts new file mode 100644 index 0000000000..75ced01685 --- /dev/null +++ b/projects/subgraph-beanstalk/src/handlers/legacy/LegacySiloHandler.ts @@ -0,0 +1,130 @@ +import { Address, BigInt, Bytes, ethereum, log } from "@graphprotocol/graph-ts"; +import { ZERO_BI } from "../../../../subgraph-core/utils/Decimals"; +import { + AddWithdrawal, + TransferDepositsCall, + TransferDepositCall, + WhitelistToken as WhitelistToken_v2, + AddDeposit as AddDeposit_v2, + RemoveDeposit as RemoveDeposit_v2, + RemoveDeposits as RemoveDeposits_v2 +} from "../../../generated/Beanstalk-ABIs/Replanted"; +import { loadBeanstalk } from "../../entities/Beanstalk"; +import { addToSiloWhitelist, loadSiloWithdraw, loadWhitelistTokenSetting } from "../../entities/Silo"; +import { addDeposits, addWithdrawToSiloAsset, removeDeposits } from "../../utils/Silo"; +import { takeWhitelistTokenSettingSnapshots } from "../../entities/snapshots/WhitelistTokenSetting"; +import { WhitelistToken as WhitelistToken_v3 } from "../../../generated/Beanstalk-ABIs/SiloV3"; + +// Note: No silo v1 (pre-replant) handlers have been developed. + +// Replant -> SiloV3 +export function handleAddDeposit_v2(event: AddDeposit_v2): void { + if (event.params.amount == ZERO_BI && event.params.bdv == ZERO_BI) { + // During replant there is at least one such event which should be ignored + return; + } + addDeposits({ + event, + account: event.params.account, + token: event.params.token, + seasons: [event.params.season], + stems: null, + amounts: [event.params.amount], + bdvs: [event.params.bdv], + depositVersion: "season" + }); +} + +// Replant -> SiloV3 +export function handleRemoveDeposit_v2(event: RemoveDeposit_v2): void { + removeDeposits({ + event, + account: event.params.account, + token: event.params.token, + seasons: [event.params.season], + stems: null, + amounts: [event.params.amount], + bdvs: null, + depositVersion: "season" + }); +} + +// Replant -> SiloV3 +export function handleRemoveDeposits_v2(event: RemoveDeposits_v2): void { + removeDeposits({ + event, + account: event.params.account, + token: event.params.token, + seasons: event.params.seasons, + stems: null, + amounts: event.params.amounts, + bdvs: null, + depositVersion: "season" + }); +} + +// Replant -> SiloV3 +export function handleAddWithdrawal(event: AddWithdrawal): void { + let withdraw = loadSiloWithdraw(event.params.account, event.params.token, event.params.season.toI32()); + withdraw.amount = withdraw.amount.plus(event.params.amount); + withdraw.createdAt = withdraw.createdAt == ZERO_BI ? event.block.timestamp : withdraw.createdAt; + withdraw.save(); + + addWithdrawToSiloAsset(event.address, event.params.account, event.params.token, event.params.amount, event.block.timestamp); +} + +// Note: Legacy removals are still possible today, and are therefore not Legacy handlers. + +// Replant -> SiloV3 +export function handleTransferDepositCall(call: TransferDepositCall): void { + let beanstalk = loadBeanstalk(call.to); + let updateFarmers = beanstalk.farmersToUpdate; + if (updateFarmers.indexOf(call.from.toHexString()) == -1) { + updateFarmers.push(call.from.toHexString()); + } + if (updateFarmers.indexOf(call.inputs.recipient.toHexString()) == -1) { + updateFarmers.push(call.inputs.recipient.toHexString()); + } + beanstalk.farmersToUpdate = updateFarmers; + beanstalk.save(); +} + +// Replant -> SiloV3 +export function handleTransferDepositsCall(call: TransferDepositsCall): void { + let beanstalk = loadBeanstalk(call.to); + let updateFarmers = beanstalk.farmersToUpdate; + if (updateFarmers.indexOf(call.from.toHexString()) == -1) { + updateFarmers.push(call.from.toHexString()); + } + if (updateFarmers.indexOf(call.inputs.recipient.toHexString()) == -1) { + updateFarmers.push(call.inputs.recipient.toHexString()); + } + beanstalk.farmersToUpdate = updateFarmers; + beanstalk.save(); +} + +// Replant -> SiloV3 +export function handleWhitelistToken_v2(event: WhitelistToken_v2): void { + addToSiloWhitelist(event.address, event.params.token); + + let setting = loadWhitelistTokenSetting(event.params.token); + setting.selector = event.params.selector; + setting.stalkIssuedPerBdv = BigInt.fromString("10000000000"); + setting.stalkEarnedPerSeason = event.params.stalk.times(BigInt.fromI32(1000000)); + + takeWhitelistTokenSettingSnapshots(setting, event.address, event.block.timestamp); + setting.save(); +} + +// SiloV3 -> SeedGauge +export function handleWhitelistToken_v3(event: WhitelistToken_v3): void { + addToSiloWhitelist(event.address, event.params.token); + + let setting = loadWhitelistTokenSetting(event.params.token); + setting.selector = event.params.selector; + setting.stalkIssuedPerBdv = event.params.stalk.times(BigInt.fromI32(1_000_000)); + setting.stalkEarnedPerSeason = event.params.stalkEarnedPerSeason; + + takeWhitelistTokenSettingSnapshots(setting, event.address, event.block.timestamp); + setting.save(); +} diff --git a/projects/subgraph-beanstalk/src/utils/Barn.ts b/projects/subgraph-beanstalk/src/utils/Barn.ts new file mode 100644 index 0000000000..cbd7528c52 --- /dev/null +++ b/projects/subgraph-beanstalk/src/utils/Barn.ts @@ -0,0 +1,92 @@ +import { Address, BigInt, ethereum, log } from "@graphprotocol/graph-ts"; +import { Chop as ChopEntity } from "../../generated/schema"; +import { loadFertilizer, loadFertilizerBalance, loadFertilizerToken } from "../entities/Fertilizer"; +import { ADDRESS_ZERO } from "../../../subgraph-core/utils/Constants"; +import { loadFarmer } from "../entities/Beanstalk"; +import { SeedGauge } from "../../generated/Beanstalk-ABIs/SeedGauge"; +import { loadUnripeToken, loadWhitelistTokenSetting } from "../entities/Silo"; +import { takeUnripeTokenSnapshots } from "../entities/snapshots/UnripeToken"; +import { getUnripeUnderlying } from "./Constants"; +import { toDecimal } from "../../../subgraph-core/utils/Decimals"; +import { getLatestBdv } from "../entities/snapshots/WhitelistTokenSetting"; + +class ChopParams { + event: ethereum.Event; + type: String; + account: Address; + unripeToken: Address; + unripeAmount: BigInt; + underlyingAmount: BigInt; +} + +export function transfer(fertilizer1155: Address, from: Address, to: Address, id: BigInt, amount: BigInt, blockNumber: BigInt): void { + let fertilizer = loadFertilizer(fertilizer1155); + let fertilizerToken = loadFertilizerToken(fertilizer, id, blockNumber); + if (from != ADDRESS_ZERO) { + let fromFarmer = loadFarmer(from); + let fromFertilizerBalance = loadFertilizerBalance(fertilizerToken, fromFarmer); + fromFertilizerBalance.amount = fromFertilizerBalance.amount.minus(amount); + fromFertilizerBalance.save(); + } else { + fertilizerToken.supply = fertilizerToken.supply.plus(amount); + fertilizer.supply = fertilizer.supply.plus(amount); + fertilizer.save(); + fertilizerToken.save(); + } + + let toFarmer = loadFarmer(to); + let toFertilizerBalance = loadFertilizerBalance(fertilizerToken, toFarmer); + toFertilizerBalance.amount = toFertilizerBalance.amount.plus(amount); + toFertilizerBalance.save(); +} + +export function unripeChopped(params: ChopParams): void { + const unripe = loadUnripeToken(params.unripeToken); + const unripeBdvOne = getLatestBdv(loadWhitelistTokenSetting(Address.fromBytes(unripe.id)))!; + const underlyingBdvOne = getLatestBdv(loadWhitelistTokenSetting(Address.fromBytes(unripe.underlyingToken)))!; + + let id = params.type + "-" + params.event.transaction.hash.toHexString() + "-" + params.event.transactionLogIndex.toString(); + let chop = new ChopEntity(id); + chop.farmer = params.account.toHexString(); + chop.unripeToken = unripe.id; + chop.unripeAmount = params.unripeAmount; + chop.unripeBdv = params.unripeAmount.times(unripeBdvOne); + chop.underlyingToken = unripe.underlyingToken; + chop.underlyingAmount = params.underlyingAmount; + chop.underlyingBdv = params.underlyingAmount.times(underlyingBdvOne); + chop.chopRate = unripe.chopRate; + chop.hash = params.event.transaction.hash.toHexString(); + chop.blockNumber = params.event.block.number; + chop.createdAt = params.event.block.timestamp; + chop.save(); + + unripe.totalChoppedAmount = unripe.totalChoppedAmount.plus(chop.unripeAmount); + unripe.totalChoppedBdv = unripe.totalChoppedBdv.plus(chop.unripeBdv); + unripe.totalChoppedBdvReceived = unripe.totalChoppedBdvReceived.plus(chop.underlyingBdv); + unripe.save(); + + updateUnripeStats(Address.fromBytes(unripe.id), params.event.address, params.event.block); +} + +// Update the status for this unripe token using protocol getters. These values fluctuate without related events. +export function updateUnripeStats(unripe: Address, protocol: Address, block: ethereum.Block): void { + const beanstalk_call = SeedGauge.bind(protocol); + let unripeToken = loadUnripeToken(unripe); + + // Contract values + unripeToken.amountUnderlyingOne = beanstalk_call.getUnderlyingPerUnripeToken(unripe); + unripeToken.choppableAmountOne = beanstalk_call.getPenalty(unripe); + unripeToken.chopRate = toDecimal(beanstalk_call.getPercentPenalty(unripe)); + unripeToken.recapPercent = toDecimal(beanstalk_call.getRecapFundedPercent(unripe)); + + // Further calculated values + unripeToken.underlyingToken = getUnripeUnderlying(unripe, block.number); + const underlyingBdvOne = getLatestBdv(loadWhitelistTokenSetting(Address.fromBytes(unripeToken.underlyingToken))); + if (underlyingBdvOne !== null) { + unripeToken.bdvUnderlyingOne = unripeToken.amountUnderlyingOne.times(underlyingBdvOne); + unripeToken.choppableBdvOne = unripeToken.choppableAmountOne.times(underlyingBdvOne); + } + + takeUnripeTokenSnapshots(unripeToken, protocol, block.timestamp); + unripeToken.save(); +} diff --git a/projects/subgraph-beanstalk/src/utils/Beanstalk.ts b/projects/subgraph-beanstalk/src/utils/Beanstalk.ts deleted file mode 100644 index 185ef0f0d7..0000000000 --- a/projects/subgraph-beanstalk/src/utils/Beanstalk.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Address } from "@graphprotocol/graph-ts"; -import { Beanstalk } from "../../generated/schema"; -import { ZERO_BI } from "../../../subgraph-core/utils/Decimals"; - -export function loadBeanstalk(protocol: Address): Beanstalk { - let beanstalk = Beanstalk.load(protocol.toHexString()); - if (beanstalk == null) { - beanstalk = new Beanstalk(protocol.toHexString()); - beanstalk.name = "Beanstalk"; - beanstalk.slug = "beanstalk"; - beanstalk.schemaVersion = "2.3.1"; - beanstalk.subgraphVersion = "2.3.1"; - beanstalk.methodologyVersion = "2.3.1"; - beanstalk.lastUpgrade = ZERO_BI; - beanstalk.lastSeason = 1; - beanstalk.activeFarmers = []; - beanstalk.farmersToUpdate = []; - beanstalk.save(); - } - return beanstalk as Beanstalk; -} diff --git a/projects/subgraph-beanstalk/src/utils/Constants.ts b/projects/subgraph-beanstalk/src/utils/Constants.ts new file mode 100644 index 0000000000..182a1d6806 --- /dev/null +++ b/projects/subgraph-beanstalk/src/utils/Constants.ts @@ -0,0 +1,97 @@ +import { Address, BigInt } from "@graphprotocol/graph-ts"; +import { + BEAN_3CRV, + BEAN_3CRV_V1, + BEAN_ERC20, + BEAN_ERC20_V1, + BEAN_LUSD_V1, + BEAN_WETH_CP2_WELL, + BEAN_WETH_UNRIPE_MIGRATION_BLOCK, + BEAN_WETH_V1, + BEAN_WSTETH_CP2_WELL, + BEAN_WSTETH_UNRIPE_MIGRATION_BLOCK, + BEANSTALK, + FERTILIZER, + UNRIPE_BEAN, + UNRIPE_LP +} from "../../../subgraph-core/utils/Constants"; + +export function getProtocolToken(protocol: Address): Address { + if (protocol == BEANSTALK) { + return BEAN_ERC20; + } + throw new Error("Unsupported protocol"); +} + +export function getTokenProtocol(token: Address): Address { + if (token == BEAN_ERC20) { + return BEANSTALK; + } else if (token == BEAN_ERC20_V1) { + return BEANSTALK; + } + throw new Error("Unsupported protocol token"); +} + +export function getProtocolFertilizer(protocol: Address): Address | null { + if (protocol == BEANSTALK) { + return FERTILIZER; + } + throw new Error("Unsupported protocol"); +} + +export function getFertilizerProtocol(fertilizer: Address): Address { + if (fertilizer == FERTILIZER) { + return BEANSTALK; + } + throw new Error("Unsupported fertilizer"); +} + +export function getUnripeUnderlying(unripeToken: Address, blockNumber: BigInt): Address { + if (unripeToken == UNRIPE_BEAN) { + return BEAN_ERC20; + } else if (unripeToken == UNRIPE_LP) { + if (blockNumber < BEAN_WETH_UNRIPE_MIGRATION_BLOCK) { + return BEAN_3CRV; + } else if (blockNumber < BEAN_WSTETH_UNRIPE_MIGRATION_BLOCK) { + return BEAN_WETH_CP2_WELL; + } else { + return BEAN_WSTETH_CP2_WELL; + } + } + throw new Error("Unsupported unripe token"); +} + +export function getTokenDecimals(token: Address): i32 { + if (token == BEAN_ERC20) { + return 6; + } else if (token == UNRIPE_BEAN) { + return 6; + } else if (token == UNRIPE_LP) { + return 6; + } else if (token == BEAN_3CRV) { + return 18; + } else if (token == BEAN_WETH_CP2_WELL) { + return 18; + } else if (token == BEAN_WSTETH_CP2_WELL) { + return 18; + } else if (token == BEAN_ERC20_V1) { + return 6; + } else if (token == BEAN_WETH_V1) { + return 18; + } else if (token == BEAN_3CRV_V1) { + return 18; + } else if (token == BEAN_LUSD_V1) { + return 18; + } + throw new Error("Unsupported token"); +} + +export function isUnripe(token: Address): boolean { + const unripeTokens = [UNRIPE_BEAN, UNRIPE_LP]; + for (let i = 0; i < unripeTokens.length; ++i) { + if (unripeTokens[i] == token) { + return true; + } + } + return false; +} diff --git a/projects/subgraph-beanstalk/src/utils/Dates.ts b/projects/subgraph-beanstalk/src/utils/Dates.ts deleted file mode 100644 index 220eab8fee..0000000000 --- a/projects/subgraph-beanstalk/src/utils/Dates.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { BigInt } from "@graphprotocol/graph-ts"; - -export function dayFromTimestamp(timestamp: BigInt): string { - let day_ts = timestamp.toI32() - (timestamp.toI32() % 86400); - return day_ts.toString(); -} - -export function hourFromTimestamp(timestamp: BigInt): string { - let day_ts = timestamp.toI32() - (timestamp.toI32() % 3600); - return day_ts.toString(); -} diff --git a/projects/subgraph-beanstalk/src/utils/Farmer.ts b/projects/subgraph-beanstalk/src/utils/Farmer.ts deleted file mode 100644 index 3b28658a39..0000000000 --- a/projects/subgraph-beanstalk/src/utils/Farmer.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Address } from "@graphprotocol/graph-ts"; -import { Farmer } from "../../generated/schema"; - -export function loadFarmer(account: Address): Farmer { - let farmer = Farmer.load(account.toHexString()); - if (farmer == null) { - farmer = new Farmer(account.toHexString()); - farmer.save(); - } - return farmer; -} diff --git a/projects/subgraph-beanstalk/src/utils/FertilizerYield.ts b/projects/subgraph-beanstalk/src/utils/FertilizerYield.ts deleted file mode 100644 index 46e067067f..0000000000 --- a/projects/subgraph-beanstalk/src/utils/FertilizerYield.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { FertilizerYield } from "../../generated/schema"; -import { ZERO_BD, ZERO_BI } from "../../../subgraph-core/utils/Decimals"; - -export function loadFertilizerYield(season: i32, window: i32): FertilizerYield { - let fertilizerYield = FertilizerYield.load(season.toString() + "-" + window.toString()); - if (fertilizerYield == null) { - fertilizerYield = new FertilizerYield(season.toString() + "-" + window.toString()); - fertilizerYield.season = season; - fertilizerYield.humidity = ZERO_BD; - fertilizerYield.outstandingFert = ZERO_BI; - fertilizerYield.beansPerSeasonEMA = ZERO_BD; - fertilizerYield.deltaBpf = ZERO_BD; - fertilizerYield.simpleAPY = ZERO_BD; - fertilizerYield.createdAt = ZERO_BI; - - if (window == 24) { - fertilizerYield.emaWindow = "ROLLING_24_HOUR"; - } else if (window == 168) { - fertilizerYield.emaWindow = "ROLLING_7_DAY"; - } else if (window == 720) { - fertilizerYield.emaWindow = "ROLLING_30_DAY"; - } - - fertilizerYield.save(); - } - return fertilizerYield as FertilizerYield; -} diff --git a/projects/subgraph-beanstalk/src/utils/Field.ts b/projects/subgraph-beanstalk/src/utils/Field.ts index 5fffd2728d..da920707d7 100644 --- a/projects/subgraph-beanstalk/src/utils/Field.ts +++ b/projects/subgraph-beanstalk/src/utils/Field.ts @@ -1,35 +1,423 @@ import { Address, BigInt, BigDecimal, ethereum } from "@graphprotocol/graph-ts"; -import { Field, FieldDailySnapshot, FieldHourlySnapshot } from "../../generated/schema"; -import { dayFromTimestamp } from "./Dates"; -import { BI_MAX, ONE_BD, toDecimal, ZERO_BD, ZERO_BI } from "../../../subgraph-core/utils/Decimals"; -import { BEANSTALK, CURVE_PRICE } from "../../../subgraph-core/utils/Constants"; -import { loadSeason } from "./Season"; import { CurvePrice } from "../../generated/Beanstalk-ABIs/CurvePrice"; -import { BeanstalkPrice_try_price } from "./BeanstalkPrice"; +import { BeanstalkPrice_try_price } from "./contracts/BeanstalkPrice"; +import { CURVE_PRICE } from "../../../subgraph-core/utils/Constants"; +import { BI_10, ONE_BD, toDecimal, ZERO_BD, ZERO_BI } from "../../../subgraph-core/utils/Decimals"; +import { setFieldHourlyCaseId, setHourlySoilSoldOut, takeFieldSnapshots } from "../entities/snapshots/Field"; +import { getCurrentSeason, getHarvestableIndex, loadBeanstalk, loadFarmer, loadSeason } from "../entities/Beanstalk"; +import { loadField, loadPlot } from "../entities/Field"; +import { expirePodListingIfExists } from "./Marketplace"; -// This function is for handling both the WeatherChange and TemperatureChange events. -// The logic is the same for both, this is intended to accommodate the renamed event and fields. -export function handleRateChange(evtAddress: Address, evtBlock: ethereum.Block, season: BigInt, caseId: BigInt, absChange: i32): void { - let field = loadField(evtAddress); - let fieldHourly = loadFieldHourly(evtAddress, season.toI32(), evtBlock.timestamp); - let fieldDaily = loadFieldDaily(evtAddress, evtBlock.timestamp); +class SowParams { + event: ethereum.Event; + account: Address; + fieldId: BigInt | null; + index: BigInt; + beans: BigInt; + pods: BigInt; +} + +class HarvestParams { + event: ethereum.Event; + account: Address; + fieldId: BigInt | null; + plots: BigInt[]; + beans: BigInt; +} + +class PlotTransferParams { + event: ethereum.Event; + from: Address; + to: Address; + fieldId: BigInt | null; + index: BigInt; + amount: BigInt; +} + +class TemperatureChangedParams { + event: ethereum.Event; + season: BigInt; + caseId: BigInt; + absChange: i32; +} + +export function sow(params: SowParams): void { + const protocol = params.event.address; + let sownBeans = params.beans; + // Update Farmer Totals + updateFieldTotals( + protocol, + params.account, + ZERO_BI, + sownBeans, + params.pods, + ZERO_BI, + ZERO_BI, + ZERO_BI, + params.event.block.timestamp, + params.event.block.number + ); + + let field = loadField(protocol); + loadFarmer(params.account); + let plot = loadPlot(protocol, params.index); + + let newIndexes = field.plotIndexes; + newIndexes.push(plot.index); + field.plotIndexes = newIndexes; + field.save(); + + plot.farmer = params.account.toHexString(); + plot.source = "SOW"; + plot.sourceHash = params.event.transaction.hash.toHexString(); + plot.season = field.season; + plot.creationHash = params.event.transaction.hash.toHexString(); + plot.createdAt = params.event.block.timestamp; + plot.updatedAt = params.event.block.timestamp; + plot.updatedAtBlock = params.event.block.number; + plot.pods = params.pods; + plot.beansPerPod = params.beans.times(BI_10.pow(6)).div(plot.pods); + plot.save(); + + incrementSows(protocol, params.account, params.event.block.timestamp, params.event.block.number); +} + +export function harvest(params: HarvestParams): void { + const protocol = params.event.address; + let beanstalk = loadBeanstalk(protocol); + let season = loadSeason(protocol, BigInt.fromI32(beanstalk.lastSeason)); + + let remainingIndex = ZERO_BI; + for (let i = 0; i < params.plots.length; i++) { + // Plot should exist + let plot = loadPlot(protocol, params.plots[i]); + + expirePodListingIfExists(protocol, plot.farmer, plot.index, params.event.block.timestamp); + + let harvestablePods = season.harvestableIndex.minus(plot.index); + + if (harvestablePods >= plot.pods) { + // Plot fully harvests + updateFieldTotals( + protocol, + params.account, + ZERO_BI, + ZERO_BI, + ZERO_BI, + ZERO_BI, + ZERO_BI, + plot.pods, + params.event.block.timestamp, + params.event.block.number + ); + + plot.harvestedPods = plot.pods; + plot.fullyHarvested = true; + plot.save(); + } else { + // Plot partially harvests + updateFieldTotals( + protocol, + params.account, + ZERO_BI, + ZERO_BI, + ZERO_BI, + ZERO_BI, + ZERO_BI, + harvestablePods, + params.event.block.timestamp, + params.event.block.number + ); + + remainingIndex = plot.index.plus(harvestablePods); + let remainingPods = plot.pods.minus(harvestablePods); + + let remainingPlot = loadPlot(protocol, remainingIndex); + remainingPlot.farmer = plot.farmer; + remainingPlot.source = plot.source; + remainingPlot.sourceHash = plot.sourceHash; + remainingPlot.season = beanstalk.lastSeason; + remainingPlot.creationHash = params.event.transaction.hash.toHexString(); + remainingPlot.createdAt = params.event.block.timestamp; + remainingPlot.updatedAt = params.event.block.timestamp; + remainingPlot.updatedAtBlock = params.event.block.number; + remainingPlot.index = remainingIndex; + remainingPlot.pods = remainingPods; + remainingPlot.beansPerPod = plot.beansPerPod; + remainingPlot.save(); + + plot.harvestedPods = harvestablePods; + plot.pods = harvestablePods; + plot.fullyHarvested = true; + plot.save(); + } + } + + // Remove the harvested plot IDs from the field list + let field = loadField(protocol); + let newIndexes = field.plotIndexes; + for (let i = 0; i < params.plots.length; i++) { + let plotIndex = newIndexes.indexOf(params.plots[i]); + newIndexes.splice(plotIndex, 1); + newIndexes.sort(); + } + if (remainingIndex !== ZERO_BI) { + newIndexes.push(remainingIndex); + } + field.plotIndexes = newIndexes; + field.save(); +} + +export function plotTransfer(params: PlotTransferParams): void { + const protocol = params.event.address; + const currentHarvestable = getHarvestableIndex(protocol); + + // Ensure both farmer entites exist + loadFarmer(params.from); + loadFarmer(params.to); + + // Update farmer field data + updateFieldTotals( + protocol, + params.from, + ZERO_BI, + ZERO_BI, + ZERO_BI, + ZERO_BI.minus(params.amount), + ZERO_BI, + ZERO_BI, + params.event.block.timestamp, + params.event.block.number, + false + ); + updateFieldTotals( + protocol, + params.to, + ZERO_BI, + ZERO_BI, + ZERO_BI, + params.amount, + ZERO_BI, + ZERO_BI, + params.event.block.timestamp, + params.event.block.number, + false + ); + + let field = loadField(protocol); + let sortedPlots = field.plotIndexes.sort(); + + let sourceIndex = ZERO_BI; + + for (let i = 0; i < sortedPlots.length; i++) { + // Handle only single comparison for first value of array + if (i == 0) { + if (sortedPlots[i] == params.index) { + sourceIndex = sortedPlots[i]; + break; + } else { + continue; + } + } + // Transferred plot matches existing. Start value of zero. + if (sortedPlots[i] == params.index) { + sourceIndex = sortedPlots[i]; + break; + } + // Transferred plot is in the middle of existing plot. Non-zero start value. + if (sortedPlots[i - 1] < params.index && params.index < sortedPlots[i]) { + sourceIndex = sortedPlots[i - 1]; + } + } + + let sourcePlot = loadPlot(protocol, sourceIndex); + let sourceEndIndex = sourceIndex.plus(sourcePlot.pods); + let transferEndIndex = params.index.plus(params.amount); - field.temperature += absChange; - fieldHourly.temperature += absChange; - fieldDaily.temperature += absChange; + // Determines how many of the pods being transferred are harvestable + const calcHarvestable = (index: BigInt, pods: BigInt, harvestableIndex: BigInt): BigInt => { + let harvestable = harvestableIndex.minus(index); + if (harvestable < ZERO_BI) { + return ZERO_BI; + } else { + return harvestable >= pods ? pods : harvestable; + } + }; + + let transferredHarvestable = calcHarvestable(params.index, params.amount, currentHarvestable); + + // Actually transfer the plots + if (sourcePlot.pods == params.amount) { + // Sending full plot + const isMarket = sourcePlot.source == "MARKET" && sourcePlot.sourceHash == params.event.transaction.hash.toHexString(); + if (!isMarket) { + sourcePlot.source = "TRANSFER"; + sourcePlot.sourceHash = params.event.transaction.hash.toHexString(); + sourcePlot.beansPerPod = sourcePlot.beansPerPod; + } + sourcePlot.farmer = params.to.toHexString(); + sourcePlot.updatedAt = params.event.block.timestamp; + sourcePlot.updatedAtBlock = params.event.block.number; + sourcePlot.save(); + } else if (sourceIndex == params.index) { + // We are only needing to split this plot once to send + // Start value of zero + let remainderIndex = sourceIndex.plus(params.amount); + let remainderPlot = loadPlot(protocol, remainderIndex); + sortedPlots.push(remainderIndex); + + const isMarket = sourcePlot.source == "MARKET" && sourcePlot.sourceHash == params.event.transaction.hash.toHexString(); + if (!isMarket) { + // When sending the start of the plot via market, these cannot be derived from sourcePlot. + remainderPlot.source = sourcePlot.source; + remainderPlot.sourceHash = sourcePlot.sourceHash; + remainderPlot.beansPerPod = sourcePlot.beansPerPod; + + sourcePlot.source = "TRANSFER"; + sourcePlot.sourceHash = params.event.transaction.hash.toHexString(); + sourcePlot.beansPerPod = sourcePlot.beansPerPod; + } + sourcePlot.farmer = params.to.toHexString(); + sourcePlot.updatedAt = params.event.block.timestamp; + sourcePlot.updatedAtBlock = params.event.block.number; + sourcePlot.pods = params.amount; + sourcePlot.harvestablePods = calcHarvestable(sourcePlot.index, sourcePlot.pods, currentHarvestable); + sourcePlot.save(); + + remainderPlot.farmer = params.from.toHexString(); + remainderPlot.season = field.season; + remainderPlot.creationHash = params.event.transaction.hash.toHexString(); + remainderPlot.createdAt = params.event.block.timestamp; + remainderPlot.updatedAt = params.event.block.timestamp; + remainderPlot.updatedAtBlock = params.event.block.number; + remainderPlot.index = remainderIndex; + remainderPlot.pods = sourceEndIndex.minus(transferEndIndex); + remainderPlot.harvestablePods = calcHarvestable(remainderPlot.index, remainderPlot.pods, currentHarvestable); + remainderPlot.save(); + } else if (sourceEndIndex == transferEndIndex) { + // We are only needing to split this plot once to send + // Non-zero start value. Sending to end of plot + let toPlot = loadPlot(protocol, params.index); + sortedPlots.push(params.index); + + sourcePlot.updatedAt = params.event.block.timestamp; + sourcePlot.updatedAtBlock = params.event.block.number; + sourcePlot.pods = sourcePlot.pods.minus(params.amount); + sourcePlot.harvestablePods = calcHarvestable(sourcePlot.index, sourcePlot.pods, currentHarvestable); + sourcePlot.save(); + + const isMarket = toPlot.source == "MARKET" && toPlot.sourceHash == params.event.transaction.hash.toHexString(); + if (!isMarket) { + toPlot.source = "TRANSFER"; + toPlot.sourceHash = params.event.transaction.hash.toHexString(); + toPlot.beansPerPod = sourcePlot.beansPerPod; + } + toPlot.farmer = params.to.toHexString(); + toPlot.season = field.season; + toPlot.creationHash = params.event.transaction.hash.toHexString(); + toPlot.createdAt = params.event.block.timestamp; + toPlot.updatedAt = params.event.block.timestamp; + toPlot.updatedAtBlock = params.event.block.number; + toPlot.index = params.index; + toPlot.pods = params.amount; + toPlot.harvestablePods = calcHarvestable(toPlot.index, toPlot.pods, currentHarvestable); + toPlot.save(); + } else { + // We have to split this plot twice to send + let remainderIndex = params.index.plus(params.amount); + let toPlot = loadPlot(protocol, params.index); + let remainderPlot = loadPlot(protocol, remainderIndex); + + sortedPlots.push(params.index); + sortedPlots.push(remainderIndex); - fieldHourly.caseId = caseId; + sourcePlot.updatedAt = params.event.block.timestamp; + sourcePlot.updatedAtBlock = params.event.block.number; + sourcePlot.pods = params.index.minus(sourcePlot.index); + sourcePlot.harvestablePods = calcHarvestable(sourcePlot.index, sourcePlot.pods, currentHarvestable); + sourcePlot.save(); - // Real Rate of Return + const isMarket = toPlot.source == "MARKET" && toPlot.sourceHash == params.event.transaction.hash.toHexString(); + if (!isMarket) { + toPlot.source = "TRANSFER"; + toPlot.sourceHash = params.event.transaction.hash.toHexString(); + toPlot.beansPerPod = sourcePlot.beansPerPod; + } + toPlot.farmer = params.to.toHexString(); + toPlot.season = field.season; + toPlot.creationHash = params.event.transaction.hash.toHexString(); + toPlot.createdAt = params.event.block.timestamp; + toPlot.updatedAt = params.event.block.timestamp; + toPlot.updatedAtBlock = params.event.block.number; + toPlot.index = params.index; + toPlot.pods = params.amount; + toPlot.harvestablePods = calcHarvestable(toPlot.index, toPlot.pods, currentHarvestable); + toPlot.save(); - let seasonEntity = loadSeason(evtAddress, season); + remainderPlot.farmer = params.from.toHexString(); + remainderPlot.source = sourcePlot.source; + remainderPlot.sourceHash = sourcePlot.sourceHash; + remainderPlot.season = field.season; + remainderPlot.creationHash = params.event.transaction.hash.toHexString(); + remainderPlot.createdAt = params.event.block.timestamp; + remainderPlot.updatedAt = params.event.block.timestamp; + remainderPlot.updatedAtBlock = params.event.block.number; + remainderPlot.index = remainderIndex; + remainderPlot.pods = sourceEndIndex.minus(transferEndIndex); + remainderPlot.harvestablePods = calcHarvestable(remainderPlot.index, remainderPlot.pods, currentHarvestable); + remainderPlot.beansPerPod = sourcePlot.beansPerPod; + remainderPlot.save(); + } + sortedPlots.sort(); + field.plotIndexes = sortedPlots; + field.save(); + + // Update any harvestable pod amounts + // No need to shift beanstalk field, only the farmer fields. + if (transferredHarvestable != ZERO_BI) { + updateFieldTotals( + protocol, + params.from, + ZERO_BI, + ZERO_BI, + ZERO_BI, + ZERO_BI, + ZERO_BI.minus(transferredHarvestable), + ZERO_BI, + params.event.block.timestamp, + params.event.block.number, + false + ); + updateFieldTotals( + protocol, + params.to, + ZERO_BI, + ZERO_BI, + ZERO_BI, + ZERO_BI, + transferredHarvestable, + ZERO_BI, + params.event.block.timestamp, + params.event.block.number, + false + ); + } +} + +// This function is for handling both the WeatherChange and TemperatureChange events. +// The logic is the same for both, this is intended to accommodate the renamed event and fields. +export function temperatureChanged(params: TemperatureChangedParams): void { + const protocol = params.event.address; + let field = loadField(protocol); + field.temperature += params.absChange; + let seasonEntity = loadSeason(protocol, params.season); let currentPrice = ZERO_BD; if (seasonEntity.price != ZERO_BD) { currentPrice = seasonEntity.price; } else { // Attempt to pull from Beanstalk Price contract first - let beanstalkQuery = BeanstalkPrice_try_price(evtAddress, evtBlock.number); + let beanstalkQuery = BeanstalkPrice_try_price(protocol, params.event.block.number); if (beanstalkQuery.reverted) { let curvePrice = CurvePrice.bind(CURVE_PRICE); currentPrice = toDecimal(curvePrice.getCurve().price); @@ -39,109 +427,118 @@ export function handleRateChange(evtAddress: Address, evtBlock: ethereum.Block, } field.realRateOfReturn = ONE_BD.plus(BigDecimal.fromString((field.temperature / 100).toString())).div(currentPrice); - fieldHourly.realRateOfReturn = field.realRateOfReturn; - fieldHourly.realRateOfReturn = field.realRateOfReturn; + takeFieldSnapshots(field, protocol, params.event.block.timestamp, params.event.block.number); field.save(); - fieldHourly.save(); - fieldDaily.save(); + + // Set caseId on the hourly snapshot + setFieldHourlyCaseId(params.caseId, field); } -export function loadField(account: Address): Field { - let field = Field.load(account.toHexString()); - if (field == null) { - field = new Field(account.toHexString()); - field.beanstalk = BEANSTALK.toHexString(); - if (account !== BEANSTALK) { - field.farmer = account.toHexString(); - } - field.season = 1; - field.temperature = 1; - field.realRateOfReturn = ZERO_BD; - field.numberOfSowers = 0; - field.numberOfSows = 0; - field.sownBeans = ZERO_BI; - field.plotIndexes = []; - field.unharvestablePods = ZERO_BI; - field.harvestablePods = ZERO_BI; - field.harvestedPods = ZERO_BI; - field.soil = ZERO_BI; - field.podIndex = ZERO_BI; - field.podRate = ZERO_BD; - field.save(); +export function updateFieldTotals( + protocol: Address, + account: Address, + soil: BigInt, + sownBeans: BigInt, + sownPods: BigInt, + transferredPods: BigInt, + harvestablePods: BigInt, + harvestedPods: BigInt, + timestamp: BigInt, + blockNumber: BigInt, + recurs: boolean = true +): void { + if (recurs && account != protocol) { + updateFieldTotals( + protocol, + protocol, + soil, + sownBeans, + sownPods, + transferredPods, + harvestablePods, + harvestedPods, + timestamp, + blockNumber + ); + } + let field = loadField(account); + + field.season = getCurrentSeason(protocol); + field.soil = field.soil.plus(soil).minus(sownBeans); + field.sownBeans = field.sownBeans.plus(sownBeans); + field.unharvestablePods = field.unharvestablePods.plus(sownPods).minus(harvestablePods).plus(transferredPods); + field.harvestablePods = field.harvestablePods.plus(harvestablePods); + field.harvestedPods = field.harvestedPods.plus(harvestedPods); + field.podIndex = field.podIndex.plus(sownPods); + + takeFieldSnapshots(field, protocol, timestamp, blockNumber); + field.save(); + + // Set extra info on the hourly snapshot + if (field.soil == ZERO_BI) { + setHourlySoilSoldOut(blockNumber, field); } - return field; } -export function loadFieldHourly(account: Address, season: i32, timestamp: BigInt): FieldHourlySnapshot { - // Hourly for Beanstalk is assumed to be by season. To keep other data correctly divided - // by season, we elect to use the season number for the hour number. - let id = account.toHexString() + "-" + season.toString(); - let hourly = FieldHourlySnapshot.load(id); - if (hourly == null) { - let field = loadField(account); - hourly = new FieldHourlySnapshot(id); - hourly.field = field.id; - hourly.season = season; - hourly.temperature = field.temperature; - hourly.realRateOfReturn = ZERO_BD; - hourly.podIndex = field.podIndex; - hourly.deltaNumberOfSowers = 0; - hourly.numberOfSowers = field.numberOfSowers; - hourly.deltaNumberOfSows = 0; - hourly.numberOfSows = field.numberOfSows; - hourly.deltaSownBeans = ZERO_BI; - hourly.sownBeans = field.sownBeans; - hourly.deltaUnharvestablePods = ZERO_BI; - hourly.unharvestablePods = field.unharvestablePods; - hourly.deltaHarvestablePods = ZERO_BI; - hourly.harvestablePods = field.harvestablePods; - hourly.deltaHarvestedPods = ZERO_BI; - hourly.harvestedPods = field.harvestedPods; - hourly.issuedSoil = ZERO_BI; - hourly.soil = ZERO_BI; - hourly.podRate = field.podRate; - hourly.blocksToSoldOutSoil = ZERO_BI; - hourly.soilSoldOut = false; - hourly.caseId = BI_MAX; - hourly.blockNumber = ZERO_BI; - hourly.createdAt = timestamp; - hourly.updatedAt = timestamp; - hourly.save(); +export function updateHarvestablePlots(protocol: Address, harvestableIndex: BigInt, timestamp: BigInt, blockNumber: BigInt): void { + let field = loadField(protocol); + let sortedIndexes = field.plotIndexes.sort(); + + for (let i = 0; i < sortedIndexes.length; i++) { + if (sortedIndexes[i] > harvestableIndex) { + break; + } + let plot = loadPlot(protocol, sortedIndexes[i]); + + // Plot is fully harvestable, but hasn't been harvested yet + if (plot.harvestablePods == plot.pods) { + continue; + } + + let harvestablePods = harvestableIndex.minus(plot.index); + let oldHarvestablePods = plot.harvestablePods; + plot.harvestablePods = harvestablePods >= plot.pods ? plot.pods : harvestablePods; + plot.save(); + + let deltaHarvestablePods = oldHarvestablePods == ZERO_BI ? plot.harvestablePods : plot.harvestablePods.minus(oldHarvestablePods); + + updateFieldTotals( + protocol, + Address.fromString(plot.farmer), + ZERO_BI, + ZERO_BI, + ZERO_BI, + ZERO_BI, + deltaHarvestablePods, + ZERO_BI, + timestamp, + blockNumber + ); } - return hourly; } -export function loadFieldDaily(account: Address, timestamp: BigInt): FieldDailySnapshot { - let hour = dayFromTimestamp(timestamp); - let id = account.toHexString() + "-" + hour.toString(); - let daily = FieldDailySnapshot.load(id); - if (daily == null) { - let field = loadField(account); - daily = new FieldDailySnapshot(id); - daily.field = field.id; - daily.season = field.season; - daily.temperature = field.temperature; - daily.realRateOfReturn = ZERO_BD; - daily.podIndex = field.podIndex; - daily.deltaNumberOfSowers = 0; - daily.numberOfSowers = field.numberOfSowers; - daily.deltaNumberOfSows = 0; - daily.numberOfSows = field.numberOfSows; - daily.deltaSownBeans = ZERO_BI; - daily.sownBeans = field.sownBeans; - daily.deltaUnharvestablePods = ZERO_BI; - daily.unharvestablePods = field.unharvestablePods; - daily.deltaHarvestablePods = ZERO_BI; - daily.harvestablePods = field.harvestablePods; - daily.deltaHarvestedPods = ZERO_BI; - daily.harvestedPods = field.harvestedPods; - daily.issuedSoil = ZERO_BI; - daily.soil = ZERO_BI; - daily.podRate = field.podRate; - daily.createdAt = timestamp; - daily.updatedAt = timestamp; - daily.save(); +// Increment number of unique sowers (protocol only) +function incrementSowers(protocol: Address, timestamp: BigInt, blockNumber: BigInt): void { + let field = loadField(protocol); + field.numberOfSowers += 1; + takeFieldSnapshots(field, protocol, timestamp, blockNumber); + field.save(); +} + +// Increment total number of sows for either an account or the protocol +function incrementSows(protocol: Address, account: Address, timestamp: BigInt, blockNumber: BigInt, recurs: boolean = true): void { + if (recurs && account != protocol) { + incrementSows(protocol, protocol, timestamp, blockNumber); + } + + let field = loadField(account); + field.numberOfSows += 1; + takeFieldSnapshots(field, protocol, timestamp, blockNumber); + field.save(); + + // Add to protocol numberOfSowers if this is the first time this account has sown + if (account != protocol && field.numberOfSows == 0) { + incrementSowers(protocol, timestamp, blockNumber); } - return daily; } diff --git a/projects/subgraph-beanstalk/src/utils/Germinating.ts b/projects/subgraph-beanstalk/src/utils/Germinating.ts deleted file mode 100644 index 32912d0149..0000000000 --- a/projects/subgraph-beanstalk/src/utils/Germinating.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { Address, BigDecimal, BigInt, store } from "@graphprotocol/graph-ts"; -import { toDecimal, ZERO_BD, ZERO_BI } from "../../../subgraph-core/utils/Decimals"; -import { Germinating } from "../../generated/schema"; - -export function loadOrCreateGerminating(address: Address, season: i32, isFarmer: boolean): Germinating { - const type = germinationSeasonCategory(season); - const id = address.toHexString() + "-" + type; - let germinating = Germinating.load(id); - if (germinating == null) { - germinating = new Germinating(id); - germinating.address = address.toHexString(); - germinating.type = type; - germinating.isFarmer = isFarmer; - germinating.season = season; - germinating.stalk = ZERO_BI; - germinating.tokenAmount = ZERO_BI; - germinating.bdv = ZERO_BI; - germinating.save(); - } - return germinating as Germinating; -} - -export function loadGerminating(address: Address, enumValue: i32): Germinating { - const id = address.toHexString() + "-" + germinationEnumCategory(enumValue); - let germinating = Germinating.load(id); - return germinating as Germinating; -} - -export function tryLoadBothGerminating(address: Address): Array { - return [Germinating.load(address.toHexString() + "-ODD"), Germinating.load(address.toHexString() + "-EVEN")]; -} - -export function getGerminatingBdvs(address: Address): Array { - const germinatingState = tryLoadBothGerminating(address); - return [ - germinatingState[0] !== null ? toDecimal(germinatingState[0]!.bdv) : ZERO_BD, - germinatingState[1] !== null ? toDecimal(germinatingState[1]!.bdv) : ZERO_BD - ]; -} - -export function deleteGerminating(germinating: Germinating): void { - store.remove("Germinating", germinating.id); -} - -function germinationSeasonCategory(season: i32): string { - return season % 2 == 0 ? "EVEN" : "ODD"; -} - -function germinationEnumCategory(enumValue: i32): string { - return enumValue == 0 ? "ODD" : "EVEN"; -} diff --git a/projects/subgraph-beanstalk/src/utils/Init.ts b/projects/subgraph-beanstalk/src/utils/Init.ts new file mode 100644 index 0000000000..44cef48257 --- /dev/null +++ b/projects/subgraph-beanstalk/src/utils/Init.ts @@ -0,0 +1,24 @@ +import { BigInt, ethereum } from "@graphprotocol/graph-ts"; +import { Version } from "../../generated/schema"; + +export function handleInitVersion(block: ethereum.Block): void { + const versionEntity = new Version("subgraph"); + versionEntity.versionNumber = "2.4.0"; + versionEntity.subgraphName = subgraphNameForBlockNumber(block.number); + versionEntity.chain = chainForBlockNumber(block.number); + versionEntity.save(); +} + +function subgraphNameForBlockNumber(blockNumber: BigInt): string { + if (blockNumber == BigInt.fromU32(12974075)) { + return "beanstalk"; + } + throw new Error("Unable to initialize subgraph name for this block number"); +} + +function chainForBlockNumber(blockNumber: BigInt): string { + if (blockNumber == BigInt.fromU32(12974075)) { + return "ethereum"; + } + throw new Error("Unable to initialize chain for this block number"); +} diff --git a/projects/subgraph-beanstalk/src/MarketplaceHandler.ts b/projects/subgraph-beanstalk/src/utils/Marketplace.ts similarity index 58% rename from projects/subgraph-beanstalk/src/MarketplaceHandler.ts rename to projects/subgraph-beanstalk/src/utils/Marketplace.ts index 05e3fdb7e5..70214835a6 100644 --- a/projects/subgraph-beanstalk/src/MarketplaceHandler.ts +++ b/projects/subgraph-beanstalk/src/utils/Marketplace.ts @@ -1,45 +1,30 @@ import { Address, BigInt, Bytes, ethereum, log } from "@graphprotocol/graph-ts"; -import { - PodListingCancelled, - PodListingCreated as PodListingCreated_v1, - PodListingFilled as PodListingFilled_v1, - PodOrderCancelled, - PodOrderCreated as PodOrderCreated_v1, - PodOrderFilled as PodOrderFilled_v1 -} from "../generated/Beanstalk-ABIs/PreReplant"; -import { PodListingCreated as PodListingCreated_v1_1 } from "../generated/Beanstalk-ABIs/Replanted"; -import { - PodListingCreated as PodListingCreated_v2, - PodListingFilled as PodListingFilled_v2, - PodOrderCreated as PodOrderCreated_v2, - PodOrderFilled as PodOrderFilled_v2 -} from "../generated/Beanstalk-ABIs/MarketV2"; - +import { loadPlot } from "../entities/Field"; +import { BI_10, ZERO_BI } from "../../../subgraph-core/utils/Decimals"; +import { loadPodFill, loadPodMarketplace } from "../entities/PodMarketplace"; +import { createHistoricalPodOrder, loadPodOrder } from "../entities/PodMarketplace"; +import { createHistoricalPodListing, loadPodListing } from "../entities/PodMarketplace"; import { Plot, + PodListing, PodListingCreated as PodListingCreatedEvent, PodListingFilled as PodListingFilledEvent, PodListingCancelled as PodListingCancelledEvent, PodOrderCreated as PodOrderCreatedEvent, PodOrderFilled as PodOrderFilledEvent, PodOrderCancelled as PodOrderCancelledEvent, - PodOrder, - PodListing -} from "../generated/schema"; -import { BI_10, ZERO_BI } from "../../subgraph-core/utils/Decimals"; -import { loadFarmer } from "./utils/Farmer"; -import { loadPodFill } from "./utils/PodFill"; -import { createHistoricalPodListing, loadPodListing } from "./utils/PodListing"; -import { - MarketplaceAction, - updateActiveListings, - updateActiveOrders, - updateMarketListingBalances, - updateMarketOrderBalances -} from "./utils/PodMarketplace"; -import { createHistoricalPodOrder, loadPodOrder } from "./utils/PodOrder"; -import { getHarvestableIndex } from "./utils/Season"; -import { loadPlot } from "./utils/Plot"; + PodOrder +} from "../../generated/schema"; +import { getHarvestableIndex, loadFarmer } from "../entities/Beanstalk"; +import { takeMarketSnapshots } from "../entities/snapshots/Marketplace"; + +export enum MarketplaceAction { + CREATED, + FILLED_PARTIAL, + FILLED_FULL, + CANCELLED, + EXPIRED +} class PodListingCreatedParams { event: ethereum.Event; @@ -56,6 +41,12 @@ class PodListingCreatedParams { pricingType: i32; // for v1, always 0 } +class PodListingCancelledParams { + event: ethereum.Event; + account: Address; + index: BigInt; +} + class PodOrderCreatedParams { event: ethereum.Event; account: Address; @@ -69,6 +60,12 @@ class PodOrderCreatedParams { pricingType: i32; // for v1, always 0 } +class PodOrderCancelledParams { + event: ethereum.Event; + account: Address; + id: Bytes; +} + // This one is the same for both listing/order fills. class MarketFillParams { event: ethereum.Event; @@ -82,239 +79,7 @@ class MarketFillParams { costInBeans: BigInt; } -/* ------------------------------------ - * POD MARKETPLACE V1 - * - * Proposal: BIP-11 https://bean.money/bip-11 - * Deployed: 02/05/2022 @ block 14148509 - * Code: https://github.com/BeanstalkFarms/Beanstalk/commit/75a67fc94cf2637ac1d7d7c89645492e31423fed - * ------------------------------------ - */ - -export function handlePodListingCreated(event: PodListingCreated_v1): void { - podListingCreated({ - event: event, - account: event.params.account, - index: event.params.index, - start: event.params.start, - amount: event.params.amount, - pricePerPod: event.params.pricePerPod, - maxHarvestableIndex: event.params.maxHarvestableIndex, - mode: event.params.toWallet ? 0 : 1, - minFillAmount: ZERO_BI, - pricingFunction: null, - pricingType: 0 - }); -} - -export function handlePodListingCancelled(event: PodListingCancelled): void { - let listing = PodListing.load(event.params.account.toHexString() + "-" + event.params.index.toString()); - if (listing !== null && listing.status == "ACTIVE") { - updateActiveListings( - event.address, - MarketplaceAction.CANCELLED, - event.params.account.toHexString(), - listing.index, - listing.maxHarvestableIndex - ); - updateMarketListingBalances(event.address, ZERO_BI, listing.remainingAmount, ZERO_BI, ZERO_BI, event.block.timestamp); - - listing.status = listing.filled == ZERO_BI ? "CANCELLED" : "CANCELLED_PARTIAL"; - listing.updatedAt = event.block.timestamp; - listing.save(); - - // Save the raw event data - let id = "podListingCancelled-" + event.transaction.hash.toHexString() + "-" + event.logIndex.toString(); - let rawEvent = new PodListingCancelledEvent(id); - rawEvent.hash = event.transaction.hash.toHexString(); - rawEvent.logIndex = event.logIndex.toI32(); - rawEvent.protocol = event.address.toHexString(); - rawEvent.historyID = listing.historyID; - rawEvent.account = event.params.account.toHexString(); - rawEvent.placeInLine = event.params.index.plus(listing.start).minus(getHarvestableIndex(event.address)); - rawEvent.index = event.params.index; - rawEvent.blockNumber = event.block.number; - rawEvent.createdAt = event.block.timestamp; - rawEvent.save(); - } -} - -export function handlePodListingFilled(event: PodListingFilled_v1): void { - let listing = loadPodListing(event.params.from, event.params.index); - const beanAmount = BigInt.fromI32(listing.pricePerPod).times(event.params.amount).div(BigInt.fromI32(1000000)); - - podListingFilled({ - event: event, - from: event.params.from, - to: event.params.to, - id: null, - index: event.params.index, - start: event.params.start, - amount: event.params.amount, - costInBeans: beanAmount - }); -} - -export function handlePodOrderCreated(event: PodOrderCreated_v1): void { - const beanAmount = event.params.amount.times(BigInt.fromI32(event.params.pricePerPod)).div(BigInt.fromString("1000000")); - podOrderCreated({ - event: event, - account: event.params.account, - id: event.params.id, - beanAmount: beanAmount, - pricePerPod: event.params.pricePerPod, - maxPlaceInLine: event.params.maxPlaceInLine, - minFillAmount: ZERO_BI, - pricingFunction: null, - pricingType: 0 - }); -} - -export function handlePodOrderFilled(event: PodOrderFilled_v1): void { - let order = loadPodOrder(event.params.id); - let beanAmount = BigInt.fromI32(order.pricePerPod).times(event.params.amount).div(BigInt.fromI32(1000000)); - - podOrderFilled({ - event: event, - from: event.params.from, - to: event.params.to, - id: event.params.id, - index: event.params.index, - start: event.params.start, - amount: event.params.amount, - costInBeans: beanAmount - }); -} - -export function handlePodOrderCancelled(event: PodOrderCancelled): void { - let order = PodOrder.load(event.params.id.toHexString()); - if (order !== null && order.status == "ACTIVE") { - order.status = order.podAmountFilled == ZERO_BI ? "CANCELLED" : "CANCELLED_PARTIAL"; - order.updatedAt = event.block.timestamp; - order.save(); - - updateActiveOrders(event.address, MarketplaceAction.CANCELLED, order.id, order.maxPlaceInLine); - updateMarketOrderBalances( - event.address, - ZERO_BI, - order.beanAmount.minus(order.beanAmountFilled), - ZERO_BI, - ZERO_BI, - event.block.timestamp - ); - - // Save the raw event data - let id = "podOrderCancelled-" + event.transaction.hash.toHexString() + "-" + event.logIndex.toString(); - let rawEvent = new PodOrderCancelledEvent(id); - rawEvent.hash = event.transaction.hash.toHexString(); - rawEvent.logIndex = event.logIndex.toI32(); - rawEvent.protocol = event.address.toHexString(); - rawEvent.historyID = order.historyID; - rawEvent.account = event.params.account.toHexString(); - rawEvent.orderId = event.params.id.toHexString(); - rawEvent.blockNumber = event.block.number; - rawEvent.createdAt = event.block.timestamp; - rawEvent.save(); - } -} - -/* ------------------------------------ - * POD MARKETPLACE V1 - REPLANTED - * - * When Beanstalk was Replanted, `event.params.mode` was changed from - * `bool` to `uint8`. - * - * Proposal: BIP-21 - * Deployed: 08/05/2022 at block 15278963 - * ------------------------------------ - */ - -export function handlePodListingCreated_v1_1(event: PodListingCreated_v1_1): void { - podListingCreated({ - event: event, - account: event.params.account, - index: event.params.index, - start: event.params.start, - amount: event.params.amount, - pricePerPod: event.params.pricePerPod, - maxHarvestableIndex: event.params.maxHarvestableIndex, - mode: event.params.mode, - minFillAmount: ZERO_BI, - pricingFunction: null, - pricingType: 0 - }); -} - -/* ------------------------------------ - * POD MARKETPLACE V2 - * - * Proposal: BIP-29 https://bean.money/bip-29 - * Deployed: 11/12/2022 @ block 15951072 - * ------------------------------------ - */ - -export function handlePodListingCreated_v2(event: PodListingCreated_v2): void { - podListingCreated({ - event: event, - account: event.params.account, - index: event.params.index, - start: event.params.start, - amount: event.params.amount, - pricePerPod: event.params.pricePerPod, - maxHarvestableIndex: event.params.maxHarvestableIndex, - mode: event.params.mode, - minFillAmount: event.params.minFillAmount, - pricingFunction: event.params.pricingFunction, - pricingType: event.params.pricingType - }); -} - -export function handlePodListingFilled_v2(event: PodListingFilled_v2): void { - podListingFilled({ - event: event, - from: event.params.from, - to: event.params.to, - id: null, - index: event.params.index, - start: event.params.start, - amount: event.params.amount, - costInBeans: event.params.costInBeans - }); -} - -export function handlePodOrderCreated_v2(event: PodOrderCreated_v2): void { - podOrderCreated({ - event: event, - account: event.params.account, - id: event.params.id, - beanAmount: event.params.amount, - pricePerPod: event.params.pricePerPod, - maxPlaceInLine: event.params.maxPlaceInLine, - minFillAmount: event.params.minFillAmount, - pricingFunction: event.params.pricingFunction, - pricingType: event.params.priceType - }); -} - -export function handlePodOrderFilled_v2(event: PodOrderFilled_v2): void { - podOrderFilled({ - event: event, - from: event.params.from, - to: event.params.to, - id: event.params.id, - index: event.params.index, - start: event.params.start, - amount: event.params.amount, - costInBeans: event.params.costInBeans - }); -} - -/* ------------------------------------ - * SHARED FUNCTIONS - * ------------------------------------ - */ - -function podListingCreated(params: PodListingCreatedParams): void { +export function podListingCreated(params: PodListingCreatedParams): void { let plot = Plot.load(params.index.toString()); if (plot == null) { return; @@ -368,7 +133,15 @@ function podListingCreated(params: PodListingCreatedParams): void { listing.index, listing.maxHarvestableIndex ); - updateMarketListingBalances(params.event.address, params.amount, ZERO_BI, ZERO_BI, ZERO_BI, params.event.block.timestamp); + updateMarketListingBalances( + params.event.address, + params.event.address, + params.amount, + ZERO_BI, + ZERO_BI, + ZERO_BI, + params.event.block.timestamp + ); /// Save raw event data let id = "podListingCreated-" + params.event.transaction.hash.toHexString() + "-" + params.event.logIndex.toString(); @@ -393,10 +166,18 @@ function podListingCreated(params: PodListingCreatedParams): void { rawEvent.save(); } -function podListingFilled(params: MarketFillParams): void { +export function podListingFilled(params: MarketFillParams): void { let listing = loadPodListing(params.from, params.index); - updateMarketListingBalances(params.event.address, ZERO_BI, ZERO_BI, params.amount, params.costInBeans, params.event.block.timestamp); + updateMarketListingBalances( + params.event.address, + params.event.address, + ZERO_BI, + ZERO_BI, + params.amount, + params.costInBeans, + params.event.block.timestamp + ); listing.filledAmount = params.amount; listing.remainingAmount = listing.remainingAmount.minus(params.amount); @@ -488,7 +269,47 @@ function podListingFilled(params: MarketFillParams): void { rawEvent.save(); } -function podOrderCreated(params: PodOrderCreatedParams): void { +export function podListingCancelled(params: PodListingCancelledParams): void { + let listing = PodListing.load(params.account.toHexString() + "-" + params.index.toString()); + if (listing !== null && listing.status == "ACTIVE") { + updateActiveListings( + params.event.address, + MarketplaceAction.CANCELLED, + params.account.toHexString(), + listing.index, + listing.maxHarvestableIndex + ); + updateMarketListingBalances( + params.event.address, + params.event.address, + ZERO_BI, + listing.remainingAmount, + ZERO_BI, + ZERO_BI, + params.event.block.timestamp + ); + + listing.status = listing.filled == ZERO_BI ? "CANCELLED" : "CANCELLED_PARTIAL"; + listing.updatedAt = params.event.block.timestamp; + listing.save(); + + // Save the raw event data + let id = "podListingCancelled-" + params.event.transaction.hash.toHexString() + "-" + params.event.logIndex.toString(); + let rawEvent = new PodListingCancelledEvent(id); + rawEvent.hash = params.event.transaction.hash.toHexString(); + rawEvent.logIndex = params.event.logIndex.toI32(); + rawEvent.protocol = params.event.address.toHexString(); + rawEvent.historyID = listing.historyID; + rawEvent.account = params.account.toHexString(); + rawEvent.placeInLine = params.index.plus(listing.start).minus(getHarvestableIndex(params.event.address)); + rawEvent.index = params.index; + rawEvent.blockNumber = params.event.block.number; + rawEvent.createdAt = params.event.block.timestamp; + rawEvent.save(); + } +} + +export function podOrderCreated(params: PodOrderCreatedParams): void { let order = loadPodOrder(params.id); loadFarmer(params.account); @@ -514,7 +335,15 @@ function podOrderCreated(params: PodOrderCreatedParams): void { order.save(); updateActiveOrders(params.event.address, MarketplaceAction.CREATED, order.id, order.maxPlaceInLine); - updateMarketOrderBalances(params.event.address, params.beanAmount, ZERO_BI, ZERO_BI, ZERO_BI, params.event.block.timestamp); + updateMarketOrderBalances( + params.event.address, + params.event.address, + params.beanAmount, + ZERO_BI, + ZERO_BI, + ZERO_BI, + params.event.block.timestamp + ); // Save the raw event data let id = "podOrderCreated-" + params.event.transaction.hash.toHexString() + "-" + params.event.logIndex.toString(); @@ -535,7 +364,7 @@ function podOrderCreated(params: PodOrderCreatedParams): void { rawEvent.save(); } -function podOrderFilled(params: MarketFillParams): void { +export function podOrderFilled(params: MarketFillParams): void { let order = loadPodOrder(params.id!); let fill = loadPodFill(params.event.address, params.index, params.event.transaction.hash.toHexString()); @@ -565,7 +394,15 @@ function podOrderFilled(params: MarketFillParams): void { updateActiveOrders(params.event.address, MarketplaceAction.FILLED_FULL, order.id, order.maxPlaceInLine); } - updateMarketOrderBalances(params.event.address, ZERO_BI, ZERO_BI, params.amount, params.costInBeans, params.event.block.timestamp); + updateMarketOrderBalances( + params.event.address, + params.event.address, + ZERO_BI, + ZERO_BI, + params.amount, + params.costInBeans, + params.event.block.timestamp + ); // Save the raw event data let id = "podOrderFilled-" + params.event.transaction.hash.toHexString() + "-" + params.event.logIndex.toString(); @@ -586,6 +423,105 @@ function podOrderFilled(params: MarketFillParams): void { rawEvent.save(); } +export function podOrderCancelled(params: PodOrderCancelledParams): void { + let order = PodOrder.load(params.id.toHexString()); + if (order !== null && order.status == "ACTIVE") { + order.status = order.podAmountFilled == ZERO_BI ? "CANCELLED" : "CANCELLED_PARTIAL"; + order.updatedAt = params.event.block.timestamp; + order.save(); + + updateActiveOrders(params.event.address, MarketplaceAction.CANCELLED, order.id, order.maxPlaceInLine); + updateMarketOrderBalances( + params.event.address, + params.event.address, + ZERO_BI, + order.beanAmount.minus(order.beanAmountFilled), + ZERO_BI, + ZERO_BI, + params.event.block.timestamp + ); + + // Save the raw event data + let id = "podOrderCancelled-" + params.event.transaction.hash.toHexString() + "-" + params.event.logIndex.toString(); + let rawEvent = new PodOrderCancelledEvent(id); + rawEvent.hash = params.event.transaction.hash.toHexString(); + rawEvent.logIndex = params.event.logIndex.toI32(); + rawEvent.protocol = params.event.address.toHexString(); + rawEvent.historyID = order.historyID; + rawEvent.account = params.account.toHexString(); + rawEvent.orderId = params.id.toHexString(); + rawEvent.blockNumber = params.event.block.number; + rawEvent.createdAt = params.event.block.timestamp; + rawEvent.save(); + } +} + +export function updateMarketListingBalances( + protocol: Address, + marketAddress: Address, + newPodAmount: BigInt, + cancelledPodAmount: BigInt, + filledPodAmount: BigInt, + filledBeanAmount: BigInt, + timestamp: BigInt +): void { + let market = loadPodMarketplace(marketAddress); + + const netListingChange = newPodAmount.minus(cancelledPodAmount).minus(filledPodAmount); + + market.listedPods = market.listedPods.plus(newPodAmount); + market.availableListedPods = market.availableListedPods.plus(netListingChange); + market.cancelledListedPods = market.cancelledListedPods.plus(cancelledPodAmount); + market.filledListedPods = market.filledListedPods.plus(filledPodAmount); + market.podVolume = market.podVolume.plus(filledPodAmount); + market.beanVolume = market.beanVolume.plus(filledBeanAmount); + + takeMarketSnapshots(market, protocol, timestamp); + market.save(); +} + +export function updateMarketOrderBalances( + protocol: Address, + marketAddress: Address, + newBeanAmount: BigInt, + cancelledBeanAmount: BigInt, + filledPodAmount: BigInt, + filledBeanAmount: BigInt, + timestamp: BigInt +): void { + let market = loadPodMarketplace(marketAddress); + + const netOrderChange = newBeanAmount.minus(cancelledBeanAmount).minus(filledBeanAmount); + + market.orderBeans = market.orderBeans.plus(newBeanAmount); + market.availableOrderBeans = market.availableOrderBeans.plus(netOrderChange); + market.filledOrderedPods = market.filledOrderedPods.plus(filledPodAmount); + market.filledOrderBeans = market.filledOrderBeans.plus(filledBeanAmount); + market.podVolume = market.podVolume.plus(filledPodAmount); + market.beanVolume = market.beanVolume.plus(filledBeanAmount); + market.cancelledOrderBeans = market.cancelledOrderBeans.plus(cancelledBeanAmount); + + takeMarketSnapshots(market, protocol, timestamp); + market.save(); +} + +export function updateExpiredPlots(protocol: Address, harvestableIndex: BigInt, timestamp: BigInt): void { + let market = loadPodMarketplace(protocol); + let remainingListings = market.activeListings; + + // Cancel any pod marketplace listings beyond the index + for (let i = 0; i < remainingListings.length; i++) { + const destructured = remainingListings[i].split("-"); + const maxHarvestableIndex = BigInt.fromString(destructured[2]); + if (harvestableIndex > maxHarvestableIndex) { + // This method updates the marketplace entity, so it will perform the splice. + expirePodListingIfExists(protocol, destructured[0], BigInt.fromString(destructured[1]), timestamp, i); + // A similar splice is done here also to track the updated index on the underlying array. + remainingListings.splice(i--, 1); + } + } +} + function setBeansPerPodAfterFill(event: ethereum.Event, plotIndex: BigInt, start: BigInt, length: BigInt, costInBeans: BigInt): void { // Load the plot that is being sent. It may or may not have been created already, depending // on whether the PlotTransfer event has already been processed (sometims its emitted after the market transfer). @@ -607,3 +543,101 @@ function setBeansPerPodAfterFill(event: ethereum.Event, plotIndex: BigInt, start fillPlot.sourceHash = event.transaction.hash.toHexString(); fillPlot.save(); } + +export function expirePodListingIfExists( + protocol: Address, + farmer: string, + listedPlotIndex: BigInt, + timestamp: BigInt, + activeListingIndex: i32 = -1 // If provided, avoids having to lookup the index +): void { + let listing = PodListing.load(farmer + "-" + listedPlotIndex.toString()); + if (listing == null || listing.status != "ACTIVE") { + return; + } + listing.status = "EXPIRED"; + listing.save(); + + let market = loadPodMarketplace(protocol); + + if (activeListingIndex == -1) { + // There should always be a matching entry in this list because it is verified that the listing is ACTIVE + for (let i = 0; i < market.activeListings.length; i++) { + const destructured = market.activeListings[i].split("-"); + // Unnecessary to check if the account matches. + if (destructured[1] == listedPlotIndex.toString()) { + activeListingIndex = i; + break; + } + } + } + + market.expiredListedPods = market.expiredListedPods.plus(listing.remainingAmount); + market.availableListedPods = market.availableListedPods.minus(listing.remainingAmount); + let activeListings = market.activeListings; + activeListings.splice(activeListingIndex, 1); + market.activeListings = activeListings; + + takeMarketSnapshots(market, protocol, timestamp); + market.save(); +} + +export function updateActiveListings( + protocol: Address, + action: MarketplaceAction, + farmer: string, + plotIndex: BigInt, + expiryIndex: BigInt +): void { + let market = loadPodMarketplace(protocol); + let listings = market.activeListings; + + if (action == MarketplaceAction.CREATED) { + listings.push(farmer + "-" + plotIndex.toString() + "-" + expiryIndex.toString()); + } + if ( + [MarketplaceAction.CANCELLED, MarketplaceAction.FILLED_PARTIAL, MarketplaceAction.FILLED_FULL, MarketplaceAction.EXPIRED].includes( + action + ) + ) { + listings.splice(Marketplace_findIndex_listing(listings, plotIndex), 1); + } + + market.activeListings = listings; + market.save(); +} + +export function updateActiveOrders(protocol: Address, action: MarketplaceAction, orderId: string, maxPlaceInLine: BigInt): void { + let market = loadPodMarketplace(protocol); + let orders = market.activeOrders; + + if (action == MarketplaceAction.CREATED) { + orders.push(orderId + "-" + maxPlaceInLine.toString()); + } + if ([MarketplaceAction.CANCELLED, MarketplaceAction.FILLED_FULL, MarketplaceAction.EXPIRED].includes(action)) { + orders.splice(Marketplace_findIndex_order(orders, orderId), 1); + } + + market.activeOrders = orders; + market.save(); +} + +function Marketplace_findIndex_listing(listings: string[], plotIndex: BigInt): i32 { + for (let i = 0; i < listings.length; i++) { + const values = listings[i].split("-"); + if (BigInt.fromString(values[1]) == plotIndex) { + return i; + } + } + return -1; +} + +function Marketplace_findIndex_order(orders: string[], orderId: string): i32 { + for (let i = 0; i < orders.length; i++) { + const values = orders[i].split("-"); + if (values[0] == orderId) { + return i; + } + } + return -1; +} diff --git a/projects/subgraph-beanstalk/src/utils/Plot.ts b/projects/subgraph-beanstalk/src/utils/Plot.ts deleted file mode 100644 index 8e97468e9c..0000000000 --- a/projects/subgraph-beanstalk/src/utils/Plot.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Address, BigInt } from "@graphprotocol/graph-ts"; -import { Plot } from "../../generated/schema"; -import { ADDRESS_ZERO } from "../../../subgraph-core/utils/Constants"; -import { ZERO_BI } from "../../../subgraph-core/utils/Decimals"; -import { loadField } from "./Field"; - -export function loadPlot(diamondAddress: Address, index: BigInt): Plot { - let plot = Plot.load(index.toString()); - if (plot == null) { - plot = new Plot(index.toString()); - plot.field = diamondAddress.toHexString(); - plot.farmer = ADDRESS_ZERO.toHexString(); - plot.source = "SOW"; // Should be overwritten in case of a transfer creating a new plot - plot.sourceHash = ""; - plot.season = 0; - plot.creationHash = ""; - plot.createdAt = ZERO_BI; - plot.updatedAt = ZERO_BI; - plot.updatedAtBlock = ZERO_BI; - plot.index = index; - plot.pods = ZERO_BI; - plot.beansPerPod = ZERO_BI; - plot.harvestablePods = ZERO_BI; - plot.harvestedPods = ZERO_BI; - plot.fullyHarvested = false; - plot.save(); - - let field = loadField(diamondAddress); - field.plotIndexes.push(plot.index); - field.save(); - } - return plot; -} diff --git a/projects/subgraph-beanstalk/src/utils/PodFill.ts b/projects/subgraph-beanstalk/src/utils/PodFill.ts deleted file mode 100644 index c882ab7f57..0000000000 --- a/projects/subgraph-beanstalk/src/utils/PodFill.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Address, BigInt } from "@graphprotocol/graph-ts"; -import { PodFill } from "../../generated/schema"; -import { ZERO_BI } from "../../../subgraph-core/utils/Decimals"; - -export function loadPodFill(diamondAddress: Address, index: BigInt, hash: String): PodFill { - let id = diamondAddress.toHexString() + "-" + index.toString() + "-" + hash; - let fill = PodFill.load(id); - if (fill == null) { - fill = new PodFill(id); - fill.podMarketplace = diamondAddress.toHexString(); - fill.createdAt = ZERO_BI; - fill.fromFarmer = ""; - fill.toFarmer = ""; - fill.placeInLine = ZERO_BI; - fill.amount = ZERO_BI; - fill.index = ZERO_BI; - fill.start = ZERO_BI; - fill.costInBeans = ZERO_BI; - fill.save(); - } - return fill; -} diff --git a/projects/subgraph-beanstalk/src/utils/PodListing.ts b/projects/subgraph-beanstalk/src/utils/PodListing.ts deleted file mode 100644 index ee94cec930..0000000000 --- a/projects/subgraph-beanstalk/src/utils/PodListing.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { Address, BigInt, log } from "@graphprotocol/graph-ts"; -import { PodListing } from "../../generated/schema"; -import { BEANSTALK } from "../../../subgraph-core/utils/Constants"; -import { ZERO_BI } from "../../../subgraph-core/utils/Decimals"; -import { loadPodMarketplace, loadPodMarketplaceDailySnapshot, loadPodMarketplaceHourlySnapshot } from "./PodMarketplace"; - -export function loadPodListing(account: Address, index: BigInt): PodListing { - let id = account.toHexString() + "-" + index.toString(); - let listing = PodListing.load(id); - - if (listing == null) { - listing = new PodListing(id); - listing.podMarketplace = BEANSTALK.toHexString(); - listing.historyID = ""; - listing.plot = index.toString(); - listing.farmer = account.toHexString(); - - listing.index = index; - listing.start = ZERO_BI; - listing.mode = 0; - - listing.maxHarvestableIndex = ZERO_BI; - listing.minFillAmount = ZERO_BI; - - listing.pricePerPod = 0; - - listing.originalIndex = index; - listing.originalAmount = ZERO_BI; - listing.filled = ZERO_BI; - - listing.amount = ZERO_BI; - listing.remainingAmount = ZERO_BI; - listing.filledAmount = ZERO_BI; - - listing.status = "ACTIVE"; - listing.createdAt = ZERO_BI; - listing.creationHash = ""; - listing.updatedAt = ZERO_BI; - - listing.save(); - } - - return listing; -} - -export function expirePodListingIfExists( - diamondAddress: Address, - farmer: string, - listedPlotIndex: BigInt, - timestamp: BigInt, - activeListingIndex: i32 = -1 // If provided, avoids having to lookup the index -): void { - let listing = PodListing.load(farmer + "-" + listedPlotIndex.toString()); - if (listing == null || listing.status != "ACTIVE") { - return; - } - listing.status = "EXPIRED"; - listing.save(); - - let market = loadPodMarketplace(diamondAddress); - - if (activeListingIndex == -1) { - // There should always be a matching entry in this list because it is verified that the listing is ACTIVE - for (let i = 0; i < market.activeListings.length; i++) { - const destructured = market.activeListings[i].split("-"); - // Unnecessary to check if the account matches. - if (destructured[1] == listedPlotIndex.toString()) { - activeListingIndex = i; - break; - } - } - } - - let marketHourly = loadPodMarketplaceHourlySnapshot(diamondAddress, market.season, timestamp); - let marketDaily = loadPodMarketplaceDailySnapshot(diamondAddress, timestamp); - - market.expiredListedPods = market.expiredListedPods.plus(listing.remainingAmount); - market.availableListedPods = market.availableListedPods.minus(listing.remainingAmount); - let activeListings = market.activeListings; - activeListings.splice(activeListingIndex, 1); - market.activeListings = activeListings; - market.save(); - - marketHourly.season = market.season; - marketHourly.deltaExpiredListedPods = marketHourly.deltaExpiredListedPods.plus(listing.remainingAmount); - marketHourly.expiredListedPods = market.expiredListedPods; - marketHourly.deltaAvailableListedPods = marketHourly.deltaAvailableListedPods.minus(listing.remainingAmount); - marketHourly.availableListedPods = market.availableListedPods; - marketHourly.save(); - - marketDaily.season = market.season; - marketDaily.deltaExpiredListedPods = marketDaily.deltaExpiredListedPods.plus(listing.remainingAmount); - marketDaily.expiredListedPods = market.expiredListedPods; - marketDaily.deltaAvailableListedPods = marketDaily.deltaAvailableListedPods.minus(listing.remainingAmount); - marketDaily.availableListedPods = market.availableListedPods; - marketDaily.save(); -} - -export function createHistoricalPodListing(listing: PodListing): void { - let created = false; - let id = listing.id; - for (let i = 0; !created; i++) { - id = listing.id + "-" + i.toString(); - let newListing = PodListing.load(id); - if (newListing == null) { - newListing = new PodListing(id); - newListing.podMarketplace = listing.podMarketplace; - newListing.historyID = listing.historyID; - newListing.plot = listing.plot; - newListing.farmer = listing.farmer; - - newListing.index = listing.index; - newListing.start = listing.start; - newListing.mode = listing.mode; - - newListing.maxHarvestableIndex = listing.maxHarvestableIndex; - newListing.minFillAmount = listing.minFillAmount; - - newListing.pricePerPod = listing.pricePerPod; - - newListing.originalIndex = listing.originalIndex; - newListing.originalAmount = listing.originalAmount; - newListing.filled = listing.filled; - - newListing.amount = listing.amount; - newListing.remainingAmount = listing.remainingAmount; - newListing.filledAmount = listing.filledAmount; - - newListing.fill = listing.fill; - - newListing.status = listing.status; - newListing.createdAt = listing.createdAt; - newListing.updatedAt = listing.updatedAt; - newListing.creationHash = listing.creationHash; - newListing.save(); - created = true; - } - } -} diff --git a/projects/subgraph-beanstalk/src/utils/PodMarketplace.ts b/projects/subgraph-beanstalk/src/utils/PodMarketplace.ts deleted file mode 100644 index d039d6a02b..0000000000 --- a/projects/subgraph-beanstalk/src/utils/PodMarketplace.ts +++ /dev/null @@ -1,310 +0,0 @@ -import { Address, BigInt, log } from "@graphprotocol/graph-ts"; -import { PodMarketplace, PodMarketplaceHourlySnapshot, PodMarketplaceDailySnapshot } from "../../generated/schema"; -import { dayFromTimestamp } from "./Dates"; -import { ZERO_BI } from "../../../subgraph-core/utils/Decimals"; -import { loadField } from "./Field"; -import { expirePodListingIfExists } from "./PodListing"; - -export enum MarketplaceAction { - CREATED, - FILLED_PARTIAL, - FILLED_FULL, - CANCELLED, - EXPIRED -} - -export function loadPodMarketplace(diamondAddress: Address): PodMarketplace { - let marketplace = PodMarketplace.load(diamondAddress.toHexString()); - if (marketplace == null) { - let field = loadField(diamondAddress); - marketplace = new PodMarketplace(diamondAddress.toHexString()); - marketplace.season = field.season; - marketplace.activeListings = []; - marketplace.activeOrders = []; - marketplace.listedPods = ZERO_BI; - marketplace.availableListedPods = ZERO_BI; - marketplace.filledListedPods = ZERO_BI; - marketplace.expiredListedPods = ZERO_BI; - marketplace.cancelledListedPods = ZERO_BI; - marketplace.orderBeans = ZERO_BI; - marketplace.availableOrderBeans = ZERO_BI; - marketplace.filledOrderedPods = ZERO_BI; - marketplace.filledOrderBeans = ZERO_BI; - marketplace.cancelledOrderBeans = ZERO_BI; - marketplace.podVolume = ZERO_BI; - marketplace.beanVolume = ZERO_BI; - marketplace.save(); - } - return marketplace; -} - -export function loadPodMarketplaceHourlySnapshot(diamondAddress: Address, season: i32, timestamp: BigInt): PodMarketplaceHourlySnapshot { - // Hourly for Beanstalk is assumed to be by season. To keep other data correctly divided - // by season, we elect to use the season number for the hour number. - let id = diamondAddress.toHexString() + "-" + season.toString(); - let marketplace = loadPodMarketplace(diamondAddress); - let snapshot = PodMarketplaceHourlySnapshot.load(id); - if (snapshot == null) { - snapshot = new PodMarketplaceHourlySnapshot(id); - snapshot.season = marketplace.season; - snapshot.podMarketplace = diamondAddress.toHexString(); - snapshot.deltaListedPods = ZERO_BI; - snapshot.listedPods = marketplace.listedPods; - snapshot.deltaAvailableListedPods = ZERO_BI; - snapshot.availableListedPods = marketplace.availableListedPods; - snapshot.deltaFilledListedPods = ZERO_BI; - snapshot.filledListedPods = marketplace.filledListedPods; - snapshot.deltaExpiredListedPods = ZERO_BI; - snapshot.expiredListedPods = marketplace.expiredListedPods; - snapshot.deltaCancelledListedPods = ZERO_BI; - snapshot.cancelledListedPods = marketplace.cancelledListedPods; - snapshot.deltaOrderBeans = ZERO_BI; - snapshot.orderBeans = marketplace.orderBeans; - snapshot.deltaAvailableOrderBeans = ZERO_BI; - snapshot.availableOrderBeans = marketplace.availableOrderBeans; - snapshot.deltaFilledOrderedPods = ZERO_BI; - snapshot.filledOrderedPods = marketplace.filledOrderedPods; - snapshot.deltaFilledOrderBeans = ZERO_BI; - snapshot.filledOrderBeans = marketplace.filledOrderBeans; - snapshot.deltaCancelledOrderBeans = ZERO_BI; - snapshot.cancelledOrderBeans = marketplace.cancelledOrderBeans; - snapshot.deltaPodVolume = ZERO_BI; - snapshot.podVolume = marketplace.podVolume; - snapshot.deltaBeanVolume = ZERO_BI; - snapshot.beanVolume = marketplace.beanVolume; - snapshot.createdAt = timestamp; - snapshot.updatedAt = timestamp; - snapshot.save(); - } - return snapshot; -} - -export function loadPodMarketplaceDailySnapshot(diamondAddress: Address, timestamp: BigInt): PodMarketplaceDailySnapshot { - let day = dayFromTimestamp(timestamp); - let id = diamondAddress.toHexString() + "-" + day.toString(); - let marketplace = loadPodMarketplace(diamondAddress); - let snapshot = PodMarketplaceDailySnapshot.load(id); - if (snapshot == null) { - snapshot = new PodMarketplaceDailySnapshot(id); - snapshot.season = marketplace.season; - snapshot.podMarketplace = diamondAddress.toHexString(); - snapshot.deltaListedPods = ZERO_BI; - snapshot.listedPods = marketplace.listedPods; - snapshot.deltaAvailableListedPods = ZERO_BI; - snapshot.availableListedPods = marketplace.availableListedPods; - snapshot.deltaFilledListedPods = ZERO_BI; - snapshot.filledListedPods = marketplace.filledListedPods; - snapshot.deltaExpiredListedPods = ZERO_BI; - snapshot.expiredListedPods = marketplace.expiredListedPods; - snapshot.deltaCancelledListedPods = ZERO_BI; - snapshot.cancelledListedPods = marketplace.cancelledListedPods; - snapshot.deltaOrderBeans = ZERO_BI; - snapshot.orderBeans = marketplace.orderBeans; - snapshot.deltaAvailableOrderBeans = ZERO_BI; - snapshot.availableOrderBeans = marketplace.availableOrderBeans; - snapshot.deltaFilledOrderedPods = ZERO_BI; - snapshot.filledOrderedPods = marketplace.filledOrderedPods; - snapshot.deltaFilledOrderBeans = ZERO_BI; - snapshot.filledOrderBeans = marketplace.filledOrderBeans; - snapshot.deltaCancelledOrderBeans = ZERO_BI; - snapshot.cancelledOrderBeans = marketplace.cancelledOrderBeans; - snapshot.deltaPodVolume = ZERO_BI; - snapshot.podVolume = marketplace.podVolume; - snapshot.deltaBeanVolume = ZERO_BI; - snapshot.beanVolume = marketplace.beanVolume; - snapshot.createdAt = timestamp; - snapshot.updatedAt = timestamp; - snapshot.save(); - } - return snapshot; -} - -export function updateMarketListingBalances( - marketAddress: Address, - newPodAmount: BigInt, - cancelledPodAmount: BigInt, - filledPodAmount: BigInt, - filledBeanAmount: BigInt, - timestamp: BigInt -): void { - let market = loadPodMarketplace(marketAddress); - let marketHourly = loadPodMarketplaceHourlySnapshot(marketAddress, market.season, timestamp); - let marketDaily = loadPodMarketplaceDailySnapshot(marketAddress, timestamp); - - const netListingChange = newPodAmount.minus(cancelledPodAmount).minus(filledPodAmount); - - market.listedPods = market.listedPods.plus(newPodAmount); - market.availableListedPods = market.availableListedPods.plus(netListingChange); - market.cancelledListedPods = market.cancelledListedPods.plus(cancelledPodAmount); - market.filledListedPods = market.filledListedPods.plus(filledPodAmount); - market.podVolume = market.podVolume.plus(filledPodAmount); - market.beanVolume = market.beanVolume.plus(filledBeanAmount); - market.save(); - - marketHourly.season = market.season; - marketHourly.deltaListedPods = marketHourly.deltaListedPods.plus(newPodAmount); - marketHourly.listedPods = market.listedPods; - marketHourly.deltaAvailableListedPods = marketHourly.deltaAvailableListedPods.plus(netListingChange); - marketHourly.availableListedPods = market.availableListedPods; - marketHourly.deltaCancelledListedPods = marketHourly.deltaCancelledListedPods.plus(cancelledPodAmount); - marketHourly.cancelledListedPods = market.cancelledListedPods; - marketHourly.deltaFilledListedPods = marketHourly.deltaFilledListedPods.plus(filledPodAmount); - marketHourly.filledListedPods = market.filledListedPods; - marketHourly.deltaPodVolume = marketHourly.deltaPodVolume.plus(filledPodAmount); - marketHourly.podVolume = market.podVolume; - marketHourly.deltaBeanVolume = marketHourly.deltaBeanVolume.plus(filledBeanAmount); - marketHourly.beanVolume = market.beanVolume; - marketHourly.updatedAt = timestamp; - marketHourly.save(); - - marketDaily.season = market.season; - marketDaily.deltaListedPods = marketDaily.deltaListedPods.plus(newPodAmount); - marketDaily.listedPods = market.listedPods; - marketDaily.deltaAvailableListedPods = marketDaily.deltaAvailableListedPods.plus(netListingChange); - marketDaily.availableListedPods = market.availableListedPods; - marketDaily.deltaCancelledListedPods = marketDaily.deltaCancelledListedPods.plus(cancelledPodAmount); - marketDaily.cancelledListedPods = market.cancelledListedPods; - marketDaily.deltaFilledListedPods = marketDaily.deltaFilledListedPods.plus(filledPodAmount); - marketDaily.filledListedPods = market.filledListedPods; - marketDaily.deltaPodVolume = marketDaily.deltaPodVolume.plus(filledPodAmount); - marketDaily.podVolume = market.podVolume; - marketDaily.deltaBeanVolume = marketDaily.deltaBeanVolume.plus(filledBeanAmount); - marketDaily.beanVolume = market.beanVolume; - marketDaily.updatedAt = timestamp; - marketDaily.save(); -} - -export function updateMarketOrderBalances( - marketAddress: Address, - newBeanAmount: BigInt, - cancelledBeanAmount: BigInt, - filledPodAmount: BigInt, - filledBeanAmount: BigInt, - timestamp: BigInt -): void { - let market = loadPodMarketplace(marketAddress); - let marketHourly = loadPodMarketplaceHourlySnapshot(marketAddress, market.season, timestamp); - let marketDaily = loadPodMarketplaceDailySnapshot(marketAddress, timestamp); - - const netOrderChange = newBeanAmount.minus(cancelledBeanAmount).minus(filledBeanAmount); - - market.orderBeans = market.orderBeans.plus(newBeanAmount); - market.availableOrderBeans = market.availableOrderBeans.plus(netOrderChange); - market.filledOrderedPods = market.filledOrderedPods.plus(filledPodAmount); - market.filledOrderBeans = market.filledOrderBeans.plus(filledBeanAmount); - market.podVolume = market.podVolume.plus(filledPodAmount); - market.beanVolume = market.beanVolume.plus(filledBeanAmount); - market.cancelledOrderBeans = market.cancelledOrderBeans.plus(cancelledBeanAmount); - market.save(); - - marketHourly.deltaOrderBeans = marketHourly.deltaOrderBeans.plus(newBeanAmount); - marketHourly.orderBeans = market.orderBeans; - marketHourly.deltaAvailableOrderBeans = marketHourly.deltaAvailableOrderBeans.plus(netOrderChange); - marketHourly.availableOrderBeans = market.availableOrderBeans; - marketHourly.deltaFilledOrderedPods = marketHourly.deltaFilledOrderedPods.plus(filledPodAmount); - marketHourly.filledOrderedPods = market.filledOrderedPods; - marketHourly.deltaFilledOrderBeans = marketHourly.deltaFilledOrderBeans.plus(filledBeanAmount); - marketHourly.filledOrderBeans = market.filledOrderBeans; - marketHourly.deltaPodVolume = marketHourly.deltaPodVolume.plus(filledPodAmount); - marketHourly.podVolume = market.podVolume; - marketHourly.deltaBeanVolume = marketHourly.deltaBeanVolume.plus(filledBeanAmount); - marketHourly.beanVolume = market.beanVolume; - marketHourly.deltaCancelledOrderBeans = marketHourly.deltaCancelledOrderBeans.plus(cancelledBeanAmount); - marketHourly.cancelledOrderBeans = market.cancelledOrderBeans; - marketHourly.updatedAt = timestamp; - marketHourly.save(); - - marketDaily.deltaOrderBeans = marketDaily.deltaOrderBeans.plus(newBeanAmount); - marketDaily.orderBeans = market.orderBeans; - marketDaily.deltaAvailableOrderBeans = marketHourly.deltaAvailableOrderBeans.plus(netOrderChange); - marketDaily.availableOrderBeans = market.availableOrderBeans; - marketDaily.deltaFilledOrderedPods = marketDaily.deltaFilledOrderedPods.plus(filledPodAmount); - marketDaily.filledOrderedPods = market.filledOrderedPods; - marketDaily.deltaFilledOrderBeans = marketHourly.deltaFilledOrderBeans.plus(filledBeanAmount); - marketDaily.filledOrderBeans = market.filledOrderBeans; - marketDaily.deltaPodVolume = marketDaily.deltaPodVolume.plus(filledPodAmount); - marketDaily.podVolume = market.podVolume; - marketDaily.deltaBeanVolume = marketDaily.deltaBeanVolume.plus(filledBeanAmount); - marketDaily.beanVolume = market.beanVolume; - marketDaily.deltaCancelledOrderBeans = marketDaily.deltaCancelledOrderBeans.plus(cancelledBeanAmount); - marketDaily.cancelledOrderBeans = market.cancelledOrderBeans; - marketDaily.updatedAt = timestamp; - marketDaily.save(); -} - -export function updateExpiredPlots(harvestableIndex: BigInt, diamondAddress: Address, timestamp: BigInt): void { - let market = loadPodMarketplace(diamondAddress); - let remainingListings = market.activeListings; - - // Cancel any pod marketplace listings beyond the index - for (let i = 0; i < remainingListings.length; i++) { - const destructured = remainingListings[i].split("-"); - const maxHarvestableIndex = BigInt.fromString(destructured[2]); - if (harvestableIndex > maxHarvestableIndex) { - // This method updates the marketplace entity, so it will perform the splice. - expirePodListingIfExists(diamondAddress, destructured[0], BigInt.fromString(destructured[1]), timestamp, i); - // A similar splice is done here also to track the updated index on the underlying array. - remainingListings.splice(i--, 1); - } - } -} - -export function updateActiveListings( - diamondAddress: Address, - action: MarketplaceAction, - farmer: string, - plotIndex: BigInt, - expiryIndex: BigInt -): void { - let market = loadPodMarketplace(diamondAddress); - let listings = market.activeListings; - - if (action == MarketplaceAction.CREATED) { - listings.push(farmer + "-" + plotIndex.toString() + "-" + expiryIndex.toString()); - } - if ( - [MarketplaceAction.CANCELLED, MarketplaceAction.FILLED_PARTIAL, MarketplaceAction.FILLED_FULL, MarketplaceAction.EXPIRED].includes( - action - ) - ) { - listings.splice(Marketplace_findIndex_listing(listings, plotIndex), 1); - } - - market.activeListings = listings; - market.save(); -} - -export function updateActiveOrders(diamondAddress: Address, action: MarketplaceAction, orderId: string, maxPlaceInLine: BigInt): void { - let market = loadPodMarketplace(diamondAddress); - let orders = market.activeOrders; - - if (action == MarketplaceAction.CREATED) { - orders.push(orderId + "-" + maxPlaceInLine.toString()); - } - if ([MarketplaceAction.CANCELLED, MarketplaceAction.FILLED_FULL, MarketplaceAction.EXPIRED].includes(action)) { - orders.splice(Marketplace_findIndex_order(orders, orderId), 1); - } - - market.activeOrders = orders; - market.save(); -} - -export function Marketplace_findIndex_listing(listings: string[], plotIndex: BigInt): i32 { - for (let i = 0; i < listings.length; i++) { - const values = listings[i].split("-"); - if (BigInt.fromString(values[1]) == plotIndex) { - return i; - } - } - return -1; -} - -export function Marketplace_findIndex_order(orders: string[], orderId: string): i32 { - for (let i = 0; i < orders.length; i++) { - const values = orders[i].split("-"); - if (values[0] == orderId) { - return i; - } - } - return -1; -} diff --git a/projects/subgraph-beanstalk/src/utils/PodOrder.ts b/projects/subgraph-beanstalk/src/utils/PodOrder.ts deleted file mode 100644 index 3f8cee0fe2..0000000000 --- a/projects/subgraph-beanstalk/src/utils/PodOrder.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { Bytes, BigInt, Address } from "@graphprotocol/graph-ts"; -import { PodOrder } from "../../generated/schema"; -import { BEANSTALK } from "../../../subgraph-core/utils/Constants"; -import { ZERO_BI } from "../../../subgraph-core/utils/Decimals"; - -export function loadPodOrder(orderID: Bytes): PodOrder { - let order = PodOrder.load(orderID.toHexString()); - if (order == null) { - order = new PodOrder(orderID.toHexString()); - order.podMarketplace = BEANSTALK.toHexString(); - order.historyID = ""; - order.farmer = ""; - order.createdAt = ZERO_BI; - order.updatedAt = ZERO_BI; - order.status = ""; - order.beanAmount = ZERO_BI; - order.podAmountFilled = ZERO_BI; - order.beanAmountFilled = ZERO_BI; - order.minFillAmount = ZERO_BI; - order.maxPlaceInLine = ZERO_BI; - order.pricePerPod = 0; - order.creationHash = ""; - order.fills = []; - order.save(); - } - return order; -} - -export function createHistoricalPodOrder(order: PodOrder): void { - let created = false; - let id = order.id; - for (let i = 0; !created; i++) { - id = order.id + "-" + i.toString(); - let newOrder = PodOrder.load(id); - if (newOrder == null) { - newOrder = new PodOrder(id); - newOrder.podMarketplace = order.podMarketplace; - newOrder.historyID = order.historyID; - newOrder.farmer = order.farmer; - newOrder.createdAt = order.createdAt; - newOrder.updatedAt = order.updatedAt; - newOrder.status = order.status; - newOrder.beanAmount = order.beanAmount; - newOrder.podAmountFilled = order.podAmountFilled; - newOrder.beanAmountFilled = order.beanAmountFilled; - newOrder.minFillAmount = order.minFillAmount; - newOrder.maxPlaceInLine = order.maxPlaceInLine; - newOrder.pricePerPod = order.pricePerPod; - newOrder.creationHash = order.creationHash; - newOrder.fills = order.fills; - newOrder.save(); - created = true; - } - } -} - -// Currently there is no concept of an expired pod order, but there may be in the future -// export function expirePodOrder(diamondAddress: Address, orderId: string, timestamp: BigInt, activeOrderIndex: i32): void { -// let order = loadPodOrder(Bytes.fromHexString(orderId)); -// order.status = "EXPIRED"; -// order.save(); -// -// let market = loadPodMarketplace(diamondAddress); -// let marketHourly = loadPodMarketplaceHourlySnapshot(diamondAddress, market.season, timestamp); -// let marketDaily = loadPodMarketplaceDailySnapshot(diamondAddress, timestamp); -// -// const expiredBeans = order.beanAmount.minus(order.beanAmountFilled); -// market.expiredOrderBeans = market.expiredOrderBeans.plus(expiredBeans); -// market.availableOrderBeans = market.availableOrderBeans.minus(expiredBeans); -// let activeOrders = market.activeOrders; -// activeOrders.splice(activeOrderIndex, 1); -// market.activeOrders = activeOrders; -// market.save(); -// -// marketHourly.season = market.season; -// marketHourly.deltaExpiredOrderBeans = marketHourly.deltaExpiredOrderBeans.plus(expiredBeans); -// marketHourly.expiredOrderBeans = market.expiredListedPods; -// marketHourly.deltaAvailableOrderBeans = marketHourly.deltaAvailableOrderBeans.minus(expiredBeans); -// marketHourly.availableOrderBeans = market.availableOrderBeans; -// marketHourly.save(); -// -// marketDaily.season = market.season; -// marketDaily.deltaExpiredOrderBeans = marketDaily.deltaExpiredOrderBeans.plus(expiredBeans); -// marketDaily.expiredOrderBeans = market.expiredListedPods; -// marketDaily.deltaAvailableOrderBeans = marketDaily.deltaAvailableOrderBeans.minus(expiredBeans); -// marketDaily.availableOrderBeans = market.availableOrderBeans; -// marketDaily.save(); -// } diff --git a/projects/subgraph-beanstalk/src/utils/PodTransfer.ts b/projects/subgraph-beanstalk/src/utils/PodTransfer.ts deleted file mode 100644 index edc24d3e0b..0000000000 --- a/projects/subgraph-beanstalk/src/utils/PodTransfer.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { PlotTransfer } from "../../generated/Beanstalk-ABIs/PreReplant"; -import { PodTransfer } from "../../generated/schema"; - -export function savePodTransfer(event: PlotTransfer): void { - let id = "podtransfer" + "-" + event.transaction.hash.toHexString() + "-" + event.transactionLogIndex.toString(); - let transfer = new PodTransfer(id); - transfer.hash = event.transaction.hash.toHexString(); - transfer.logIndex = event.transactionLogIndex.toI32(); - transfer.protocol = event.address.toHexString(); - transfer.toFarmer = event.params.to.toHexString(); - transfer.fromFarmer = event.params.from.toHexString(); - transfer.index = event.params.id; - transfer.pods = event.params.pods; - transfer.blockNumber = event.block.number; - transfer.createdAt = event.block.timestamp; - transfer.save(); -} diff --git a/projects/subgraph-beanstalk/src/utils/Season.ts b/projects/subgraph-beanstalk/src/utils/Season.ts index 3b0a7d5940..c8b2361836 100644 --- a/projects/subgraph-beanstalk/src/utils/Season.ts +++ b/projects/subgraph-beanstalk/src/utils/Season.ts @@ -1,47 +1,73 @@ -import { Address, BigInt } from "@graphprotocol/graph-ts"; -import { Season } from "../../generated/schema"; -import { loadBeanstalk } from "./Beanstalk"; -import { ONE_BI, ZERO_BD, ZERO_BI } from "../../../subgraph-core/utils/Decimals"; - -export function loadSeason(diamondAddress: Address, id: BigInt): Season { - let season = Season.load(id.toString()); - if (season == null) { - season = new Season(id.toString()); - season.beanstalk = diamondAddress.toHexString(); - season.season = id.toI32(); - season.sunriseBlock = ZERO_BI; - season.createdAt = ZERO_BI; - season.price = ZERO_BD; - season.beans = ZERO_BI; - season.marketCap = ZERO_BD; - season.deltaB = ZERO_BI; - season.deltaBeans = ZERO_BI; - season.rewardBeans = ZERO_BI; - season.incentiveBeans = ZERO_BI; - season.harvestableIndex = ZERO_BI; - season.save(); - if (id > ZERO_BI) { - let lastSeason = loadSeason(diamondAddress, id.minus(ONE_BI)); - season.beans = lastSeason.beans; - season.harvestableIndex = lastSeason.harvestableIndex; - season.save(); - } +import { Address, BigInt, ethereum, log } from "@graphprotocol/graph-ts"; +import { loadSeason } from "../entities/Beanstalk"; +import { loadPodMarketplace } from "../entities/PodMarketplace"; +import { takeMarketSnapshots } from "../entities/snapshots/Marketplace"; +import { takeSiloSnapshots } from "../entities/snapshots/Silo"; +import { loadSilo, loadSiloAsset, loadWhitelistTokenSetting } from "../entities/Silo"; +import { takeSiloAssetSnapshots } from "../entities/snapshots/SiloAsset"; +import { takeFieldSnapshots } from "../entities/snapshots/Field"; +import { BI_10, toDecimal, ZERO_BD, ZERO_BI } from "../../../subgraph-core/utils/Decimals"; +import { loadField } from "../entities/Field"; +import { setBdv, takeWhitelistTokenSettingSnapshots } from "../entities/snapshots/WhitelistTokenSetting"; +import { WhitelistTokenSetting } from "../../generated/schema"; +import { SeedGauge } from "../../generated/Beanstalk-ABIs/SeedGauge"; +import { isUnripe } from "./Constants"; +import { updateUnripeStats } from "./Barn"; - // Update beanstalk season - let beanstalk = loadBeanstalk(diamondAddress); - beanstalk.lastSeason = season.season; - beanstalk.save(); - } - return season; -} +export function sunrise(protocol: Address, season: BigInt, block: ethereum.Block): void { + let currentSeason = season.toI32(); + let seasonEntity = loadSeason(protocol, season); + seasonEntity.sunriseBlock = block.number; + seasonEntity.createdAt = block.timestamp; + seasonEntity.save(); + + // Update field metrics + let field = loadField(protocol); + + // -- Field level totals + field.season = currentSeason; + field.podRate = seasonEntity.beans == ZERO_BI ? ZERO_BD : toDecimal(field.unharvestablePods, 6).div(toDecimal(seasonEntity.beans, 6)); + + takeFieldSnapshots(field, protocol, block.timestamp, block.number); + field.save(); + + // Marketplace Season Update + let market = loadPodMarketplace(protocol); + market.season = currentSeason; + takeMarketSnapshots(market, protocol, block.timestamp); + market.save(); -export function getCurrentSeason(beanstalk: Address): i32 { - let beanstalkEntity = loadBeanstalk(beanstalk); - return beanstalkEntity.lastSeason; + // Create silo entities for the protocol + let silo = loadSilo(protocol); + takeSiloSnapshots(silo, protocol, block.timestamp); + silo.save(); + + // Update all whitelisted/dewhitelisted token info + const siloTokens = silo.whitelistedTokens.concat(silo.dewhitelistedTokens); + for (let i = 0; i < siloTokens.length; i++) { + const token = Address.fromString(siloTokens[i]); + + let siloAsset = loadSiloAsset(protocol, token); + takeSiloAssetSnapshots(siloAsset, protocol, block.timestamp); + siloAsset.save(); + + let whitelistTokenSetting = loadWhitelistTokenSetting(token); + takeWhitelistTokenSettingSnapshots(whitelistTokenSetting, protocol, block.timestamp); + whitelistTokenSetting.save(); + setTokenBdv(token, protocol, whitelistTokenSetting); + + if (isUnripe(token)) { + updateUnripeStats(token, protocol, block); + } + } } -export function getHarvestableIndex(beanstalk: Address): BigInt { - let bs = loadBeanstalk(beanstalk); - let season = loadSeason(beanstalk, BigInt.fromI32(bs.lastSeason)); - return season.harvestableIndex; +function setTokenBdv(token: Address, protocol: Address, whitelistTokenSetting: WhitelistTokenSetting): void { + // Get bdv if the bdv function is available onchain (not available prior to BIP-16) + const beanstalk_call = SeedGauge.bind(protocol); + const bdvResult = beanstalk_call.try_bdv(token, BI_10.pow(whitelistTokenSetting.decimals)); + if (bdvResult.reverted) { + return; + } + setBdv(bdvResult.value, whitelistTokenSetting); } diff --git a/projects/subgraph-beanstalk/src/utils/Silo.ts b/projects/subgraph-beanstalk/src/utils/Silo.ts new file mode 100644 index 0000000000..35976decc0 --- /dev/null +++ b/projects/subgraph-beanstalk/src/utils/Silo.ts @@ -0,0 +1,202 @@ +import { Address, BigInt, ethereum, log } from "@graphprotocol/graph-ts"; +import { takeSiloSnapshots } from "../entities/snapshots/Silo"; +import { loadSilo, loadSiloAsset, loadSiloDeposit, loadWhitelistTokenSetting, updateDeposit } from "../entities/Silo"; +import { takeSiloAssetSnapshots } from "../entities/snapshots/SiloAsset"; +import { stemFromSeason } from "./contracts/SiloCalculations"; +import { GAUGE_BIP45_BLOCK } from "../../../subgraph-core/utils/Constants"; +import { BI_10, ZERO_BI } from "../../../subgraph-core/utils/Decimals"; +import { loadBeanstalk, loadFarmer } from "../entities/Beanstalk"; + +class AddRemoveDepositsParams { + event: ethereum.Event; + account: Address; + token: Address; + seasons: BigInt[] | null; // Seasons not present in v3+ + stems: BigInt[] | null; // Stems not present in v2 + amounts: BigInt[]; + bdvs: BigInt[] | null; // bdv not present in v2 removal + depositVersion: String; +} + +export function addDeposits(params: AddRemoveDepositsParams): void { + for (let i = 0; i < params.amounts.length; ++i) { + let deposit = loadSiloDeposit({ + account: params.account, + token: params.token, + depositVersion: params.depositVersion, + season: params.seasons != null ? params.seasons![i] : null, + stem: params.stems != null ? params.stems![i] : null + }); + + // Set granular deposit version type + if (params.depositVersion == "season") { + deposit.depositVersion = "season"; + // Fill stem according to legacy conversion + deposit.stemV31 = stemFromSeason(params.seasons![i].toI32(), params.token); + } else { + deposit.depositVersion = params.event.block.number > GAUGE_BIP45_BLOCK ? "v3.1" : "v3"; + deposit.stemV31 = params.event.block.number > GAUGE_BIP45_BLOCK ? deposit.stem! : deposit.stem!.times(BI_10.pow(6)); + } + + deposit = updateDeposit(deposit, params.amounts[i], params.bdvs![i], params.event)!; + deposit.save(); + + // Ensure that a Farmer entity is set up for this account. + loadFarmer(params.account); + + updateDepositInSilo( + params.event.address, + params.account, + params.token, + params.amounts[i], + params.bdvs![i], + params.event.block.timestamp + ); + } +} + +export function removeDeposits(params: AddRemoveDepositsParams): void { + for (let i = 0; i < params.amounts.length; ++i) { + let deposit = loadSiloDeposit({ + account: params.account, + token: params.token, + depositVersion: params.depositVersion, + season: params.seasons != null ? params.seasons![i] : null, + stem: params.stems != null ? params.stems![i] : null + }); + + // Use bdv if it was provided (v2 events dont provide bdv), otherwise infer + let removedBdv = params.bdvs != null ? params.bdvs![i] : params.amounts[i].times(deposit.depositedBDV).div(deposit.depositedAmount); + + // If the amount goes to zero, the deposit is deleted and not returned + const updatedDeposit = updateDeposit(deposit, params.amounts[i].neg(), removedBdv.neg(), params.event); + if (updatedDeposit !== null) { + updatedDeposit.save(); + } + + // Update protocol totals + updateDepositInSilo( + params.event.address, + params.account, + params.token, + params.amounts[i].neg(), + removedBdv.neg(), + params.event.block.timestamp + ); + } +} + +export function updateDepositInSilo( + protocol: Address, + account: Address, + token: Address, + deltaAmount: BigInt, + deltaBdv: BigInt, + timestamp: BigInt, + recurs: boolean = true +): void { + if (recurs && account != protocol) { + updateDepositInSilo(protocol, protocol, token, deltaAmount, deltaBdv, timestamp); + } + let silo = loadSilo(account); + silo.depositedBDV = silo.depositedBDV.plus(deltaBdv); + + const newSeedStalk = updateDepositInSiloAsset(protocol, account, token, deltaAmount, deltaBdv, timestamp, false); + // Individual farmer seeds cannot be directly tracked due to seed gauge + if (account == protocol) { + silo.grownStalkPerSeason = silo.grownStalkPerSeason.plus(newSeedStalk); + } + takeSiloSnapshots(silo, protocol, timestamp); + silo.save(); +} + +export function updateDepositInSiloAsset( + protocol: Address, + account: Address, + token: Address, + deltaAmount: BigInt, + deltaBdv: BigInt, + timestamp: BigInt, + recurs: boolean = true +): BigInt { + if (recurs && account != protocol) { + updateDepositInSiloAsset(protocol, protocol, token, deltaAmount, deltaBdv, timestamp); + } + let asset = loadSiloAsset(account, token); + + let tokenSettings = loadWhitelistTokenSetting(token); + let newGrownStalk = deltaBdv.times(tokenSettings.stalkEarnedPerSeason).div(BigInt.fromI32(1000000)); + + asset.depositedBDV = asset.depositedBDV.plus(deltaBdv); + asset.depositedAmount = asset.depositedAmount.plus(deltaAmount); + + takeSiloAssetSnapshots(asset, protocol, timestamp); + asset.save(); + + return newGrownStalk; +} + +export function addWithdrawToSiloAsset( + protocol: Address, + account: Address, + token: Address, + deltaAmount: BigInt, + timestamp: BigInt, + recurs: boolean = true +): void { + if (recurs && account != protocol) { + addWithdrawToSiloAsset(protocol, protocol, token, deltaAmount, timestamp); + } + let asset = loadSiloAsset(account, token); + asset.withdrawnAmount = asset.withdrawnAmount.plus(deltaAmount); + takeSiloAssetSnapshots(asset, protocol, timestamp); + asset.save(); +} + +export function updateStalkBalances( + protocol: Address, + account: Address, + deltaStalk: BigInt, + deltaRoots: BigInt, + timestamp: BigInt, + recurs: boolean = true +): void { + if (recurs && account != protocol) { + updateStalkBalances(protocol, protocol, deltaStalk, deltaRoots, timestamp); + } + let silo = loadSilo(account); + silo.stalk = silo.stalk.plus(deltaStalk); + silo.roots = silo.roots.plus(deltaRoots); + + takeSiloSnapshots(silo, protocol, timestamp); + + // Add account to active list if needed + if (account !== protocol) { + let beanstalk = loadBeanstalk(protocol); + let farmerIndex = beanstalk.activeFarmers.indexOf(account.toHexString()); + if (farmerIndex == -1) { + let newFarmers = beanstalk.activeFarmers; + newFarmers.push(account.toHexString()); + beanstalk.activeFarmers = newFarmers; + beanstalk.save(); + silo.activeFarmers += 1; + } else if (silo.stalk == ZERO_BI) { + let newFarmers = beanstalk.activeFarmers; + newFarmers.splice(farmerIndex, 1); + beanstalk.activeFarmers = newFarmers; + beanstalk.save(); + silo.activeFarmers -= 1; + } + } + silo.save(); +} + +export function updateSeedsBalances(protocol: Address, account: Address, seeds: BigInt, timestamp: BigInt, recurs: boolean = true): void { + if (recurs && account != protocol) { + updateSeedsBalances(protocol, protocol, seeds, timestamp); + } + let silo = loadSilo(account); + silo.seeds = silo.seeds.plus(seeds); + takeSiloSnapshots(silo, protocol, timestamp); + silo.save(); +} diff --git a/projects/subgraph-beanstalk/src/utils/SiloEntities.ts b/projects/subgraph-beanstalk/src/utils/SiloEntities.ts deleted file mode 100644 index f3178e1f7e..0000000000 --- a/projects/subgraph-beanstalk/src/utils/SiloEntities.ts +++ /dev/null @@ -1,381 +0,0 @@ -import { Address, BigInt, Bytes } from "@graphprotocol/graph-ts"; -import { - Silo, - SiloHourlySnapshot, - SiloDailySnapshot, - SiloDeposit, - SiloWithdraw, - SiloYield, - SiloAssetDailySnapshot, - SiloAssetHourlySnapshot, - SiloAsset, - WhitelistTokenSetting, - WhitelistTokenHourlySnapshot, - WhitelistTokenDailySnapshot, - TokenYield -} from "../../generated/schema"; -import { BEANSTALK, UNRIPE_BEAN, UNRIPE_BEAN_3CRV } from "../../../subgraph-core/utils/Constants"; -import { dayFromTimestamp, hourFromTimestamp } from "./Dates"; -import { ZERO_BD, ZERO_BI } from "../../../subgraph-core/utils/Decimals"; -import { loadBeanstalk } from "./Beanstalk"; - -/* ===== Base Silo Entities ===== */ - -export function loadSilo(account: Address): Silo { - let silo = Silo.load(account.toHexString()); - if (silo == null) { - silo = new Silo(account.toHexString()); - silo.beanstalk = BEANSTALK.toHexString(); - if (account !== BEANSTALK) { - silo.farmer = account.toHexString(); - } - silo.whitelistedTokens = []; - silo.dewhitelistedTokens = []; - silo.depositedBDV = ZERO_BI; - silo.stalk = ZERO_BI; - silo.plantableStalk = ZERO_BI; - silo.seeds = ZERO_BI; - silo.grownStalkPerSeason = ZERO_BI; - silo.roots = ZERO_BI; - silo.germinatingStalk = ZERO_BI; - silo.beanMints = ZERO_BI; - silo.activeFarmers = 0; - silo.save(); - } - return silo as Silo; -} - -export function loadSiloHourlySnapshot(account: Address, season: i32, timestamp: BigInt): SiloHourlySnapshot { - let id = account.toHexString() + "-" + season.toString(); - let snapshot = SiloHourlySnapshot.load(id); - if (snapshot == null) { - snapshot = new SiloHourlySnapshot(id); - let silo = loadSilo(account); - snapshot.season = season; - snapshot.silo = account.toHexString(); - snapshot.depositedBDV = silo.depositedBDV; - snapshot.stalk = silo.stalk; - snapshot.plantableStalk = silo.plantableStalk; - snapshot.seeds = silo.seeds; - snapshot.grownStalkPerSeason = silo.grownStalkPerSeason; - snapshot.roots = silo.roots; - snapshot.germinatingStalk = silo.germinatingStalk; - snapshot.beanToMaxLpGpPerBdvRatio = silo.beanToMaxLpGpPerBdvRatio; - snapshot.beanMints = silo.beanMints; - snapshot.activeFarmers = silo.activeFarmers; - snapshot.deltaDepositedBDV = ZERO_BI; - snapshot.deltaStalk = ZERO_BI; - snapshot.deltaPlantableStalk = ZERO_BI; - snapshot.deltaSeeds = ZERO_BI; - snapshot.deltaRoots = ZERO_BI; - snapshot.deltaGerminatingStalk = ZERO_BI; - snapshot.deltaBeanMints = ZERO_BI; - snapshot.deltaActiveFarmers = 0; - snapshot.createdAt = BigInt.fromString(hourFromTimestamp(timestamp)); - snapshot.updatedAt = timestamp; - snapshot.save(); - } - return snapshot as SiloHourlySnapshot; -} - -export function loadSiloDailySnapshot(account: Address, timestamp: BigInt): SiloDailySnapshot { - let day = dayFromTimestamp(timestamp); - let id = account.toHexString() + "-" + day.toString(); - let snapshot = SiloDailySnapshot.load(id); - if (snapshot == null) { - snapshot = new SiloDailySnapshot(id); - let silo = loadSilo(account); - snapshot.season = 0; - snapshot.silo = account.toHexString(); - snapshot.depositedBDV = silo.depositedBDV; - snapshot.stalk = silo.stalk; - snapshot.plantableStalk = silo.plantableStalk; - snapshot.seeds = silo.seeds; - snapshot.grownStalkPerSeason = silo.grownStalkPerSeason; - snapshot.roots = silo.roots; - snapshot.germinatingStalk = silo.germinatingStalk; - snapshot.beanToMaxLpGpPerBdvRatio = silo.beanToMaxLpGpPerBdvRatio; - snapshot.beanMints = silo.beanMints; - snapshot.activeFarmers = silo.activeFarmers; - snapshot.deltaDepositedBDV = ZERO_BI; - snapshot.deltaStalk = ZERO_BI; - snapshot.deltaPlantableStalk = ZERO_BI; - snapshot.deltaSeeds = ZERO_BI; - snapshot.deltaRoots = ZERO_BI; - snapshot.deltaGerminatingStalk = ZERO_BI; - snapshot.deltaBeanMints = ZERO_BI; - snapshot.deltaActiveFarmers = 0; - snapshot.createdAt = BigInt.fromString(day); - snapshot.updatedAt = timestamp; - snapshot.save(); - } - return snapshot as SiloDailySnapshot; -} - -/* ===== Asset Entities ===== */ - -export function loadSiloAsset(account: Address, token: Address): SiloAsset { - let id = account.toHexString() + "-" + token.toHexString(); - let asset = SiloAsset.load(id); - - if (asset == null) { - //let tokenEntity = loadToken(token) - asset = new SiloAsset(id); - asset.silo = account.toHexString(); - asset.token = token.toHexString(); - asset.depositedBDV = ZERO_BI; - asset.depositedAmount = ZERO_BI; - asset.withdrawnAmount = ZERO_BI; - asset.farmAmount = ZERO_BI; - asset.save(); - } - return asset as SiloAsset; -} - -export function loadSiloAssetHourlySnapshot(account: Address, token: Address, season: i32, timestamp: BigInt): SiloAssetHourlySnapshot { - let hour = hourFromTimestamp(timestamp); - let id = account.toHexString() + "-" + token.toHexString() + "-" + season.toString(); - let snapshot = SiloAssetHourlySnapshot.load(id); - if (snapshot == null) { - let asset = loadSiloAsset(account, token); - snapshot = new SiloAssetHourlySnapshot(id); - snapshot.season = season; - snapshot.siloAsset = asset.id; - snapshot.depositedBDV = asset.depositedBDV; - snapshot.depositedAmount = asset.depositedAmount; - snapshot.withdrawnAmount = asset.withdrawnAmount; - snapshot.farmAmount = asset.farmAmount; - snapshot.deltaDepositedBDV = ZERO_BI; - snapshot.deltaDepositedAmount = ZERO_BI; - snapshot.deltaWithdrawnAmount = ZERO_BI; - snapshot.deltaFarmAmount = ZERO_BI; - snapshot.createdAt = BigInt.fromString(hour); - snapshot.updatedAt = ZERO_BI; - snapshot.save(); - } - return snapshot as SiloAssetHourlySnapshot; -} - -export function loadSiloAssetDailySnapshot(account: Address, token: Address, timestamp: BigInt): SiloAssetDailySnapshot { - let day = dayFromTimestamp(timestamp); - let id = account.toHexString() + "-" + token.toHexString() + "-" + day.toString(); - let snapshot = SiloAssetDailySnapshot.load(id); - if (snapshot == null) { - let asset = loadSiloAsset(account, token); - snapshot = new SiloAssetDailySnapshot(id); - snapshot.season = 0; - snapshot.siloAsset = asset.id; - snapshot.depositedBDV = asset.depositedBDV; - snapshot.depositedAmount = asset.depositedAmount; - snapshot.withdrawnAmount = asset.withdrawnAmount; - snapshot.farmAmount = asset.farmAmount; - snapshot.deltaDepositedBDV = ZERO_BI; - snapshot.deltaDepositedAmount = ZERO_BI; - snapshot.deltaWithdrawnAmount = ZERO_BI; - snapshot.deltaFarmAmount = ZERO_BI; - snapshot.createdAt = BigInt.fromString(day); - snapshot.updatedAt = ZERO_BI; - snapshot.save(); - } - return snapshot as SiloAssetDailySnapshot; -} - -/* ===== Whitelist Token Settings Entities ===== */ - -export function addToSiloWhitelist(siloAddress: Address, token: Address): void { - let silo = loadSilo(siloAddress); - let currentList = silo.whitelistedTokens; - currentList.push(token.toHexString()); - silo.whitelistedTokens = currentList; - silo.save(); -} - -export function loadWhitelistTokenSetting(token: Address): WhitelistTokenSetting { - let setting = WhitelistTokenSetting.load(token); - if (setting == null) { - setting = new WhitelistTokenSetting(token); - setting.selector = Bytes.empty(); - setting.stalkEarnedPerSeason = ZERO_BI; - setting.stalkIssuedPerBdv = ZERO_BI; - setting.milestoneSeason = 0; - setting.updatedAt = ZERO_BI; - setting.save(); - - // Check token addresses and set replant seeds/stalk for Unripe due to event timing. - if (token == UNRIPE_BEAN) { - setting.stalkIssuedPerBdv = BigInt.fromString("10000000000"); - setting.stalkEarnedPerSeason = BigInt.fromI32(2000000); - setting.save(); - } else if (token == UNRIPE_BEAN_3CRV) { - setting.stalkIssuedPerBdv = BigInt.fromString("10000000000"); - setting.stalkEarnedPerSeason = BigInt.fromI32(4000000); - setting.save(); - } - } - return setting as WhitelistTokenSetting; -} - -export function loadWhitelistTokenHourlySnapshot(token: Address, season: i32, timestamp: BigInt): WhitelistTokenHourlySnapshot { - let hour = hourFromTimestamp(timestamp); - let id = token.toHexString() + "-" + season.toString(); - let snapshot = WhitelistTokenHourlySnapshot.load(id); - if (snapshot == null) { - let setting = loadWhitelistTokenSetting(token); - snapshot = new WhitelistTokenHourlySnapshot(id); - snapshot.season = season; - snapshot.token = setting.id; - snapshot.selector = setting.selector; - snapshot.gpSelector = setting.gpSelector; - snapshot.lwSelector = setting.lwSelector; - snapshot.stalkEarnedPerSeason = setting.stalkEarnedPerSeason; - snapshot.stalkIssuedPerBdv = setting.stalkIssuedPerBdv; - snapshot.milestoneSeason = setting.milestoneSeason; - snapshot.gaugePoints = setting.gaugePoints; - snapshot.optimalPercentDepositedBdv = setting.optimalPercentDepositedBdv; - snapshot.createdAt = BigInt.fromString(hour); - snapshot.updatedAt = ZERO_BI; - snapshot.save(); - } - return snapshot as WhitelistTokenHourlySnapshot; -} - -export function loadWhitelistTokenDailySnapshot(token: Address, timestamp: BigInt): WhitelistTokenDailySnapshot { - let day = dayFromTimestamp(timestamp); - let id = token.toHexString() + "-" + day.toString(); - let snapshot = WhitelistTokenDailySnapshot.load(id); - if (snapshot == null) { - let setting = loadWhitelistTokenSetting(token); - snapshot = new WhitelistTokenDailySnapshot(id); - snapshot.token = setting.id; - snapshot.selector = setting.selector; - snapshot.gpSelector = setting.gpSelector; - snapshot.lwSelector = setting.lwSelector; - snapshot.stalkEarnedPerSeason = setting.stalkEarnedPerSeason; - snapshot.stalkIssuedPerBdv = setting.stalkIssuedPerBdv; - snapshot.milestoneSeason = setting.milestoneSeason; - snapshot.gaugePoints = setting.gaugePoints; - snapshot.optimalPercentDepositedBdv = setting.optimalPercentDepositedBdv; - snapshot.createdAt = BigInt.fromString(day); - snapshot.updatedAt = ZERO_BI; - snapshot.save(); - } - return snapshot as WhitelistTokenDailySnapshot; -} - -/* ===== Deposit Entities ===== */ - -export function loadSiloDeposit(account: Address, token: Address, season: BigInt): SiloDeposit { - let id = account.toHexString() + "-" + token.toHexString() + "-" + season.toString(); - let deposit = SiloDeposit.load(id); - if (deposit == null) { - deposit = new SiloDeposit(id); - deposit.farmer = account.toHexString(); - deposit.token = token.toHexString(); - deposit.season = season.toI32(); - deposit.amount = ZERO_BI; - deposit.depositedAmount = ZERO_BI; - deposit.withdrawnAmount = ZERO_BI; - deposit.bdv = ZERO_BI; - deposit.depositedBDV = ZERO_BI; - deposit.withdrawnBDV = ZERO_BI; - deposit.hashes = []; - deposit.createdAt = ZERO_BI; - deposit.updatedAt = ZERO_BI; - deposit.save(); - } - return deposit; -} - -export function loadSiloDepositV3(account: Address, token: Address, stem: BigInt): SiloDeposit { - let id = account.toHexString() + "-" + token.toHexString() + "-" + stem.toString(); - let deposit = SiloDeposit.load(id); - if (deposit == null) { - let beanstalk = loadBeanstalk(BEANSTALK); - deposit = new SiloDeposit(id); - deposit.farmer = account.toHexString(); - deposit.token = token.toHexString(); - deposit.season = beanstalk.lastSeason; - deposit.stem = stem; - deposit.amount = ZERO_BI; - deposit.depositedAmount = ZERO_BI; - deposit.withdrawnAmount = ZERO_BI; - deposit.bdv = ZERO_BI; - deposit.depositedBDV = ZERO_BI; - deposit.withdrawnBDV = ZERO_BI; - deposit.hashes = []; - deposit.createdAt = ZERO_BI; - deposit.updatedAt = ZERO_BI; - deposit.save(); - } - return deposit; -} - -/* ===== Withdraw Entities ===== */ - -export function loadSiloWithdraw(account: Address, token: Address, season: i32): SiloWithdraw { - let id = account.toHexString() + "-" + token.toHexString() + "-" + season.toString(); - let withdraw = SiloWithdraw.load(id); - if (withdraw == null) { - withdraw = new SiloWithdraw(id); - withdraw.farmer = account.toHexString(); - withdraw.token = token.toHexString(); - withdraw.withdrawSeason = season; - withdraw.claimableSeason = season + 1; - withdraw.claimed = false; - withdraw.amount = ZERO_BI; - withdraw.hashes = []; - withdraw.createdAt = ZERO_BI; - withdraw.save(); - } - return withdraw as SiloWithdraw; -} - -/* ===== Yield Entities ===== */ - -export function loadSiloYield(season: i32, window: i32): SiloYield { - let siloYield = SiloYield.load(season.toString() + "-" + window.toString()); - if (siloYield == null) { - siloYield = new SiloYield(season.toString() + "-" + window.toString()); - siloYield.season = season; - siloYield.beta = ZERO_BD; - siloYield.u = 0; - siloYield.beansPerSeasonEMA = ZERO_BD; - siloYield.whitelistedTokens = []; - siloYield.createdAt = ZERO_BI; - - if (window == 24) { - siloYield.emaWindow = "ROLLING_24_HOUR"; - } else if (window == 168) { - siloYield.emaWindow = "ROLLING_7_DAY"; - } else if (window == 720) { - siloYield.emaWindow = "ROLLING_30_DAY"; - } - siloYield.save(); - } - return siloYield as SiloYield; -} - -export function loadTokenYield(token: Address, season: i32, window: i32): TokenYield { - let id = token.concatI32(season).concatI32(window); - let tokenYield = TokenYield.load(id); - if (tokenYield == null) { - tokenYield = new TokenYield(id); - tokenYield.token = token; - tokenYield.season = season; - tokenYield.siloYield = season.toString() + "-" + window.toString(); - tokenYield.beanAPY = ZERO_BD; - tokenYield.stalkAPY = ZERO_BD; - tokenYield.createdAt = ZERO_BI; - tokenYield.save(); - } - return tokenYield as TokenYield; -} - -export function SiloAsset_findIndex_token(a: SiloAsset[], targetToken: string): i32 { - for (let j = 0; j < a.length; j++) { - if (a[j].token == targetToken) { - return j; - } - } - return -1; -} diff --git a/projects/subgraph-beanstalk/src/utils/Token.ts b/projects/subgraph-beanstalk/src/utils/Token.ts deleted file mode 100644 index d85f3fb315..0000000000 --- a/projects/subgraph-beanstalk/src/utils/Token.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Address } from "@graphprotocol/graph-ts"; -import { ERC20 } from "../../generated/Beanstalk-ABIs/ERC20"; -import { Token } from "../../generated/schema"; -import { ZERO_BD, ZERO_BI } from "./Decimals"; - -export function loadToken(token: Address): Token { - let tokenEntity = Token.load(token.toHexString()); - if (tokenEntity == null) { - let tokenERC20 = ERC20.bind(token); - tokenEntity = new Token(token.toHexString()); - - // Assign replant token info manually since deposits are emitted prior to token deployment - if (token.toHexString() == "0x1BEA0050E63e05FBb5D8BA2f10cf5800B6224449") { - // Unripe Bean - tokenEntity.name = "Unripe Bean"; - tokenEntity.symbol = "urBEAN"; - tokenEntity.decimals = 6; - } else if (token.toHexString() == "0x1BEA3CcD22F4EBd3d37d731BA31Eeca95713716D") { - // Unripe Bean:3CRV - tokenEntity.name = "Unripe BEAN3CRV"; - tokenEntity.symbol = "urBEAN3CRV"; - tokenEntity.decimals = 6; - } else if (token.toHexString() == "0xBEA0000029AD1c77D3d5D23Ba2D8893dB9d1Efab") { - // Unripe Bean:3CRV - tokenEntity.name = "BEAN"; - tokenEntity.symbol = "BEAN"; - tokenEntity.decimals = 6; - } else if (token.toHexString() == "0x1BEA3CcD22F4EBd3d37d731BA31Eeca95713716D") { - // Unripe Bean:3CRV - tokenEntity.name = "BEAN3CRV"; - tokenEntity.symbol = "BEAN3CRV"; - tokenEntity.decimals = 18; - } else { - tokenEntity.name = "Unknown"; - tokenEntity.symbol = "Unknown"; - tokenEntity.decimals = 18; - } - - tokenEntity.lastPriceUSD = ZERO_BD; - tokenEntity.lastPriceBlockNumber = ZERO_BI; - tokenEntity.save(); - } - return tokenEntity as Token; -} diff --git a/projects/subgraph-beanstalk/src/utils/Transaction.ts b/projects/subgraph-beanstalk/src/utils/Transaction.ts deleted file mode 100644 index 60cf7334e4..0000000000 --- a/projects/subgraph-beanstalk/src/utils/Transaction.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { ethereum } from "@graphprotocol/graph-ts"; -// schema imports -import { Transaction } from "../../generated/schema"; - -export function loadTransaction(eth_transaction: ethereum.Transaction, eth_block: ethereum.Block): Transaction { - let transaction = Transaction.load(eth_transaction.hash.toHex()); - if (transaction == null) { - transaction = new Transaction(eth_transaction.hash.toHex()); - transaction.timestamp = eth_block.timestamp; - transaction.blockNumber = eth_block.number; - transaction.from = eth_transaction.from; - transaction.to = eth_transaction.to; - transaction.save(); - } - return transaction as Transaction; -} diff --git a/projects/subgraph-beanstalk/src/YieldHandler.ts b/projects/subgraph-beanstalk/src/utils/Yield.ts similarity index 76% rename from projects/subgraph-beanstalk/src/YieldHandler.ts rename to projects/subgraph-beanstalk/src/utils/Yield.ts index 5ec8ca83c4..2244eda32c 100644 --- a/projects/subgraph-beanstalk/src/YieldHandler.ts +++ b/projects/subgraph-beanstalk/src/utils/Yield.ts @@ -1,55 +1,57 @@ import { Address, BigDecimal, BigInt, log } from "@graphprotocol/graph-ts"; -import { BasinBip } from "../generated/Beanstalk-ABIs/BasinBip"; -import { BEANSTALK, BEAN_ERC20, FERTILIZER } from "../../subgraph-core/utils/Constants"; -import { toDecimal, ZERO_BD, ZERO_BI } from "../../subgraph-core/utils/Decimals"; -import { loadFertilizer } from "./utils/Fertilizer"; -import { loadFertilizerYield } from "./utils/FertilizerYield"; +import { toDecimal, ZERO_BD, ZERO_BI } from "../../../subgraph-core/utils/Decimals"; import { loadSilo, loadSiloAsset, - loadSiloHourlySnapshot, loadSiloYield, loadTokenYield, loadWhitelistTokenSetting, SiloAsset_findIndex_token -} from "./utils/SiloEntities"; -import { BigDecimal_sum, f64_sum, f64_max } from "../../subgraph-core/utils/ArrayMath"; -import { getGerminatingBdvs } from "./utils/Germinating"; -import { getCurrentSeason } from "./utils/Season"; -import { SiloAsset, WhitelistTokenSetting } from "../generated/schema"; +} from "../entities/Silo"; +import { BigDecimal_sum, f64_sum, f64_max } from "../../../subgraph-core/utils/ArrayMath"; +import { SiloAsset, WhitelistTokenSetting } from "../../generated/schema"; +import { calculateAPYPreGauge } from "./legacy/LegacyYield"; +import { getGerminatingBdvs } from "../entities/Germinating"; +import { getCurrentSeason, getRewardMinted, loadBeanstalk } from "../entities/Beanstalk"; +import { loadFertilizer, loadFertilizerYield } from "../entities/Fertilizer"; +import { getProtocolFertilizer } from "./Constants"; +import { REPLANT_SEASON } from "../../../subgraph-core/utils/Constants"; +import { SeedGauge } from "../../generated/Beanstalk-ABIs/SeedGauge"; 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 { - updateWindowEMA(t, timestamp, ROLLING_24_WINDOW); - updateWindowEMA(t, timestamp, ROLLING_7_DAY_WINDOW); - updateWindowEMA(t, timestamp, ROLLING_30_DAY_WINDOW); +// Note: minimum allowable season is REPLANT_SEASON +export function updateBeanEMA(protocol: Address, timestamp: BigInt): void { + updateWindowEMA(protocol, timestamp, ROLLING_24_WINDOW); + updateWindowEMA(protocol, timestamp, ROLLING_7_DAY_WINDOW); + updateWindowEMA(protocol, timestamp, ROLLING_30_DAY_WINDOW); + + if (getCurrentSeason(protocol) > 20_000) { + // Earlier values were set by cache + updateSiloVAPYs(protocol, timestamp, ROLLING_24_WINDOW); + updateSiloVAPYs(protocol, timestamp, ROLLING_7_DAY_WINDOW); + updateSiloVAPYs(protocol, timestamp, ROLLING_30_DAY_WINDOW); + } + + updateFertAPY(protocol, timestamp, ROLLING_24_WINDOW); + updateFertAPY(protocol, timestamp, ROLLING_7_DAY_WINDOW); + updateFertAPY(protocol, timestamp, ROLLING_30_DAY_WINDOW); } -/** - * - * - */ -function updateWindowEMA(t: i32, timestamp: BigInt, window: i32): void { +function updateWindowEMA(protocol: Address, timestamp: BigInt, window: i32): void { + const t = getCurrentSeason(protocol); + let silo = loadSilo(protocol); + let siloYield = loadSiloYield(t, window); + // 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.save(); - - updateFertAPY(t, timestamp, window); - return; } - 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; @@ -64,16 +66,16 @@ function updateWindowEMA(t: i32, timestamp: BigInt, window: i32): void { 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); - currentEMA = toDecimal(season.deltaBeanMints).minus(priorEMA).times(siloYield.beta).plus(priorEMA); + for (let i = REPLANT_SEASON.toI32(); i <= t; i++) { + let rewardMint = getRewardMinted(i); + currentEMA = toDecimal(rewardMint).minus(priorEMA).times(siloYield.beta).plus(priorEMA); priorEMA = currentEMA; } } else { // Calculate EMA for the prior 720 seasons 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); + let rewardMint = getRewardMinted(i); + currentEMA = toDecimal(rewardMint).minus(priorEMA).times(siloYield.beta).plus(priorEMA); priorEMA = currentEMA; } } @@ -81,13 +83,12 @@ function updateWindowEMA(t: i32, timestamp: BigInt, window: i32): void { siloYield.beansPerSeasonEMA = currentEMA; siloYield.createdAt = timestamp; siloYield.save(); - - updateSiloVAPYs(t, timestamp, window); - updateFertAPY(t, timestamp, window); } -export function updateSiloVAPYs(t: i32, timestamp: BigInt, window: i32): void { - let silo = loadSilo(BEANSTALK); +export function updateSiloVAPYs(protocol: Address, timestamp: BigInt, window: i32): void { + const beanstalk = loadBeanstalk(protocol); + const t = beanstalk.lastSeason; + let silo = loadSilo(protocol); let siloYield = loadSiloYield(t, window); const currentEMA = siloYield.beansPerSeasonEMA; @@ -113,7 +114,7 @@ export function updateSiloVAPYs(t: i32, timestamp: BigInt, window: i32): void { // Chooses which apy calculation to use if (!isGaugeLive) { - const beanGrownStalk = loadWhitelistTokenSetting(BEAN_ERC20).stalkEarnedPerSeason; + const beanGrownStalk = loadWhitelistTokenSetting(Address.fromString(beanstalk.token)).stalkEarnedPerSeason; for (let i = 0; i < whitelistSettings.length; ++i) { const tokenAPY = calculateAPYPreGauge( currentEMA, @@ -147,7 +148,7 @@ export function updateSiloVAPYs(t: i32, timestamp: BigInt, window: i32): void { const siloTokens = siloYield.whitelistedTokens.concat(silo.dewhitelistedTokens); const depositedAssets: SiloAsset[] = []; for (let i = 0; i < siloTokens.length; ++i) { - depositedAssets.push(loadSiloAsset(BEANSTALK, Address.fromString(siloTokens[i]))); + depositedAssets.push(loadSiloAsset(protocol, Address.fromString(siloTokens[i]))); } // .load() is not supported on graph-node v0.30.0. Instead the above derivation of depositedAssets is used @@ -169,7 +170,7 @@ export function updateSiloVAPYs(t: i32, timestamp: BigInt, window: i32): void { germinatingGaugeLpBdv.push(germinating); staticSeeds.push(null); } else { - if (whitelistSettings[i].id == BEAN_ERC20) { + if (whitelistSettings[i].id == Address.fromString(beanstalk.token)) { tokens.push(-1); depositedBeanBdv = depositedBdv; germinatingBeanBdv = germinating; @@ -200,7 +201,7 @@ export function updateSiloVAPYs(t: i32, timestamp: BigInt, window: i32): void { depositedBeanBdv, siloStalk, CATCH_UP_RATE, - BigInt.fromU32(getCurrentSeason(BEANSTALK)), + BigInt.fromU32(getCurrentSeason(protocol)), germinatingBeanBdv, germinatingGaugeLpBdv, germinatingNonGaugeBdv, @@ -218,79 +219,6 @@ export function updateSiloVAPYs(t: i32, timestamp: BigInt, window: i32): void { } } -/** - * - * @param n An estimate of number of Beans minted to the Silo per Season on average - * over the next 720 Seasons. This could be pre-calculated as a SMA, EMA, or otherwise. - * @param seedsPerBDV The number of seeds per BDV Beanstalk rewards for this token. - * @returns - */ - -export function calculateAPYPreGauge( - n: BigDecimal, - seedsPerBDV: BigDecimal, - seedsPerBeanBDV: BigDecimal, - stalk: BigInt, - seeds: BigInt -): BigDecimal[] { - // Initialize sequence - const beansPerSeason: f64 = parseFloat(n.toString()); - let C: f64 = parseFloat(toDecimal(seeds).toString()); // Init: Total Seeds - let K: f64 = parseFloat(toDecimal(stalk, 10).toString()); // Init: Total Stalk - let b: f64 = parseFloat(seedsPerBDV.div(seedsPerBeanBDV).toString()); // Init: User BDV - let k: f64 = 1; // Init: User Stalk - - let _seedsPerBeanBdv: f64 = parseInt(seedsPerBeanBDV.toString()); - - // Farmer initial values - let b_start: f64 = b; - let k_start: f64 = k; - - // Placeholders for above values during each iteration - let C_i: f64 = 0; - let K_i: f64 = 0; - let b_i: f64 = 0; - let k_i: f64 = 0; - - // Stalk and Seeds per Deposited Bean. - let STALK_PER_SEED: f64 = 0.0001; // 1/10,000 Stalk per Seed - let STALK_PER_BEAN: f64 = parseFloat(seedsPerBeanBDV.div(BigDecimal.fromString("10000")).toString()); // 3 Seeds per Bean * 1/10,000 Stalk per Seed - - for (let i = 0; i < 8760; i++) { - // Each Season, Farmer's ownership = `current Stalk / total Stalk` - let ownership: f64 = k / K; - let newBDV: f64 = beansPerSeason * ownership; - - // Total Seeds: each seignorage Bean => 3 Seeds - C_i = C + beansPerSeason * _seedsPerBeanBdv; - // Total Stalk: each seignorage Bean => 1 Stalk, each outstanding Bean => 1/10_000 Stalk - K_i = K + beansPerSeason + STALK_PER_SEED * C; - // Farmer BDV: each seignorage Bean => 1 BDV - b_i = b + newBDV; - // Farmer Stalk: each 1 BDV => 1 Stalk, each outstanding Bean => d = 1/5_000 Stalk per Bean - k_i = k + newBDV + STALK_PER_BEAN * b; - - C = C_i; - K = K_i; - b = b_i; - k = k_i; - } - - // Examples: - // ------------------------------- - // b_start = 1 - // b = 1 - // b.minus(b_start) = 0 = 0% APY - // - // b_start = 1 - // b = 1.1 - // b.minus(b_start) = 0.1 = 10% APY - let beanApy = b - b_start; // beanAPY - let stalkApy = k - k_start; // stalkAPY - - return [BigDecimal.fromString(beanApy.toString()), BigDecimal.fromString(stalkApy.toString())]; -} - /** * Calculates silo Bean/Stalk vAPY when Seed Gauge is active. * @@ -454,35 +382,6 @@ export function calculateGaugeVAPYs( return retval; } -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 = BasinBip.bind(BEANSTALK); - if (t < 6534) { - let currentFertHumidity = beanstalk.try_getCurrentHumidity(); - fertilizerYield.humidity = BigDecimal.fromString(currentFertHumidity.reverted ? "500" : currentFertHumidity.value.toString()).div( - BigDecimal.fromString("1000") - ); - } else { - // Avoid contract call for season >= 6534 since humidity will always be 0.2 - // This gives a significant performance improvement, but will need to be revisited if humidity ever changes - fertilizerYield.humidity = BigDecimal.fromString("0.2"); - } - - fertilizerYield.outstandingFert = fertilizer.supply; - fertilizerYield.beansPerSeasonEMA = siloYield.beansPerSeasonEMA; - fertilizerYield.deltaBpf = fertilizerYield.beansPerSeasonEMA.div(BigDecimal.fromString(fertilizerYield.outstandingFert.toString())); - fertilizerYield.simpleAPY = - fertilizerYield.deltaBpf == ZERO_BD - ? ZERO_BD - : fertilizerYield.humidity.div( - BigDecimal.fromString("1").plus(fertilizerYield.humidity).div(fertilizerYield.deltaBpf).div(BigDecimal.fromString("8760")) - ); - fertilizerYield.createdAt = timestamp; - fertilizerYield.save(); -} - function updateR(R: f64, change: f64): f64 { const newR = R + change; if (newR > 1) { @@ -506,9 +405,46 @@ function deltaRFromState(earnedBeans: f64): f64 { return -0.01; } -// TODO: implement the various gauge point functions and choose which one to call based on the stored selector +// (this may no longer be relevant as an api approach to vapys is preferred) +// Can implement the various gauge point functions and choose which one to call based on the stored selector // see {GaugePointFacet.defaultGaugePointFunction} for implementation. // This will become relevant once there are multiple functions implemented in the contract. function updateGaugePoints(gaugePoints: f64, currentPercent: f64, optimalPercent: f64): f64 { return gaugePoints; } + +function updateFertAPY(protocol: Address, timestamp: BigInt, window: i32): void { + const fertAddress = getProtocolFertilizer(protocol); + if (fertAddress === null) { + return; + } + + const beanstalk = loadBeanstalk(protocol); + const t = beanstalk.lastSeason; + let siloYield = loadSiloYield(t, window); + let fertilizerYield = loadFertilizerYield(t, window); + let fertilizer = loadFertilizer(fertAddress); + let contract = SeedGauge.bind(protocol); + if (t < 6534) { + let currentFertHumidity = contract.try_getCurrentHumidity(); + fertilizerYield.humidity = BigDecimal.fromString(currentFertHumidity.reverted ? "500" : currentFertHumidity.value.toString()).div( + BigDecimal.fromString("1000") + ); + } else { + // Avoid contract call for season >= 6534 since humidity will always be 0.2 + // This gives a significant performance improvement, but will need to be revisited if humidity ever changes + fertilizerYield.humidity = BigDecimal.fromString("0.2"); + } + + fertilizerYield.outstandingFert = fertilizer.supply; + fertilizerYield.beansPerSeasonEMA = siloYield.beansPerSeasonEMA; + fertilizerYield.deltaBpf = fertilizerYield.beansPerSeasonEMA.div(BigDecimal.fromString(fertilizerYield.outstandingFert.toString())); + fertilizerYield.simpleAPY = + fertilizerYield.deltaBpf == ZERO_BD + ? ZERO_BD + : fertilizerYield.humidity.div( + BigDecimal.fromString("1").plus(fertilizerYield.humidity).div(fertilizerYield.deltaBpf).div(BigDecimal.fromString("8760")) + ); + fertilizerYield.createdAt = timestamp; + fertilizerYield.save(); +} diff --git a/projects/subgraph-beanstalk/src/utils/BeanstalkPrice.ts b/projects/subgraph-beanstalk/src/utils/contracts/BeanstalkPrice.ts similarity index 96% rename from projects/subgraph-beanstalk/src/utils/BeanstalkPrice.ts rename to projects/subgraph-beanstalk/src/utils/contracts/BeanstalkPrice.ts index 32c07e9ac8..53a5da5dca 100644 --- a/projects/subgraph-beanstalk/src/utils/BeanstalkPrice.ts +++ b/projects/subgraph-beanstalk/src/utils/contracts/BeanstalkPrice.ts @@ -4,10 +4,10 @@ import { BeanstalkPrice, BeanstalkPrice__priceResultPPsStruct, BeanstalkPrice__priceResultPStruct -} from "../../generated/Beanstalk-ABIs/BeanstalkPrice"; -import { BEANSTALK_PRICE_1, BEANSTALK_PRICE_2, PRICE_2_BLOCK } from "../../../subgraph-core/utils/Constants"; -import { ZERO_BI } from "../../../subgraph-core/utils/Decimals"; -import { loadSilo } from "./SiloEntities"; +} from "../../../generated/Beanstalk-ABIs/BeanstalkPrice"; +import { BEANSTALK_PRICE_1, BEANSTALK_PRICE_2, PRICE_2_BLOCK } from "../../../../subgraph-core/utils/Constants"; +import { ZERO_BI } from "../../../../subgraph-core/utils/Decimals"; +import { loadSilo } from "../../entities/Silo"; // Can't use the autogenerated one because the fields need to be updateable class PriceOverallStruct { diff --git a/projects/subgraph-beanstalk/src/utils/contracts/SiloCalculations.ts b/projects/subgraph-beanstalk/src/utils/contracts/SiloCalculations.ts new file mode 100644 index 0000000000..902490fbf0 --- /dev/null +++ b/projects/subgraph-beanstalk/src/utils/contracts/SiloCalculations.ts @@ -0,0 +1,28 @@ +import { Address, BigInt } from "@graphprotocol/graph-ts"; +import { BEAN_3CRV, BEAN_ERC20, UNRIPE_BEAN, UNRIPE_LP } from "../../../../subgraph-core/utils/Constants"; +import { BI_10 } from "../../../../subgraph-core/utils/Decimals"; + +const STEM_START_SEASON = 14210; + +export function stemFromSeason(season: i32, token: Address): BigInt { + return seasonToV3Stem(season, STEM_START_SEASON, getLegacySeedsPerToken(token)); +} + +// Equivalent to LibLegacyTokenSilo.seasonToStem +function seasonToV3Stem(season: i32, stemStartSeason: i32, seedsPerBdv: i32): BigInt { + return BigInt.fromI32(season - stemStartSeason).times(BigInt.fromI32(seedsPerBdv).times(BI_10.pow(6))); +} + +// Equivalent to LibLegacyTokenSilo.getLegacySeedsPerToken +function getLegacySeedsPerToken(token: Address): i32 { + if (token == BEAN_ERC20) { + return 2; + } else if (token == UNRIPE_BEAN) { + return 2; + } else if (token == UNRIPE_LP) { + return 4; + } else if (token == BEAN_3CRV) { + return 4; + } + return 0; +} diff --git a/projects/subgraph-beanstalk/src/utils/legacy/LegacySilo.ts b/projects/subgraph-beanstalk/src/utils/legacy/LegacySilo.ts new file mode 100644 index 0000000000..750fc0f99b --- /dev/null +++ b/projects/subgraph-beanstalk/src/utils/legacy/LegacySilo.ts @@ -0,0 +1,46 @@ +import { Address, BigInt, log } from "@graphprotocol/graph-ts"; +import { loadSilo, loadSiloAsset, loadSiloWithdraw } from "../../entities/Silo"; +import { takeSiloAssetSnapshots } from "../../entities/snapshots/SiloAsset"; +import { loadBeanstalk } from "../../entities/Beanstalk"; +import { updateSeedsBalances, updateStalkBalances } from "../Silo"; +import { Replanted } from "../../../generated/Beanstalk-ABIs/Replanted"; + +export function updateClaimedWithdraw( + protocol: Address, + account: Address, + token: Address, + withdrawSeason: BigInt, + timestamp: BigInt +): void { + let withdraw = loadSiloWithdraw(account, token, withdrawSeason.toI32()); + withdraw.claimed = true; + withdraw.save(); + + let asset = loadSiloAsset(account, token); + asset.withdrawnAmount = asset.withdrawnAmount.minus(withdraw.amount); + takeSiloAssetSnapshots(asset, protocol, timestamp); + asset.save(); +} + +// Replanted -> SiloV3 +// This should be run at sunrise for the previous season to update any farmers stalk/seed/roots balances from silo transfers. +export function updateStalkWithCalls(protocol: Address, timestamp: BigInt): void { + let beanstalk = loadBeanstalk(protocol); + let beanstalk_call = Replanted.bind(protocol); + + for (let i = 0; i < beanstalk.farmersToUpdate.length; i++) { + let account = Address.fromString(beanstalk.farmersToUpdate[i]); + let silo = loadSilo(account); + updateStalkBalances( + protocol, + account, + beanstalk_call.balanceOfStalk(account).minus(silo.stalk), + beanstalk_call.balanceOfRoots(account).minus(silo.roots), + timestamp, + false + ); + updateSeedsBalances(protocol, account, beanstalk_call.balanceOfSeeds(account).minus(silo.seeds), timestamp, false); + } + beanstalk.farmersToUpdate = []; + beanstalk.save(); +} diff --git a/projects/subgraph-beanstalk/src/utils/legacy/LegacyYield.ts b/projects/subgraph-beanstalk/src/utils/legacy/LegacyYield.ts new file mode 100644 index 0000000000..041fbc8931 --- /dev/null +++ b/projects/subgraph-beanstalk/src/utils/legacy/LegacyYield.ts @@ -0,0 +1,73 @@ +import { BigDecimal, BigInt, log } from "@graphprotocol/graph-ts"; +import { toDecimal } from "../../../../subgraph-core/utils/Decimals"; + +/** + * @param n An estimate of number of Beans minted to the Silo per Season on average + * over the next 720 Seasons. This could be pre-calculated as a SMA, EMA, or otherwise. + * @param seedsPerBDV The number of seeds per BDV Beanstalk rewards for this token. + * @returns + */ +export function calculateAPYPreGauge( + n: BigDecimal, + seedsPerBDV: BigDecimal, + seedsPerBeanBDV: BigDecimal, + stalk: BigInt, + seeds: BigInt +): BigDecimal[] { + // Initialize sequence + const beansPerSeason: f64 = parseFloat(n.toString()); + let C: f64 = parseFloat(toDecimal(seeds).toString()); // Init: Total Seeds + let K: f64 = parseFloat(toDecimal(stalk, 10).toString()); // Init: Total Stalk + let b: f64 = parseFloat(seedsPerBDV.div(seedsPerBeanBDV).toString()); // Init: User BDV + let k: f64 = 1; // Init: User Stalk + + let _seedsPerBeanBdv: f64 = parseInt(seedsPerBeanBDV.toString()); + + // Farmer initial values + let b_start: f64 = b; + let k_start: f64 = k; + + // Placeholders for above values during each iteration + let C_i: f64 = 0; + let K_i: f64 = 0; + let b_i: f64 = 0; + let k_i: f64 = 0; + + // Stalk and Seeds per Deposited Bean. + let STALK_PER_SEED: f64 = 0.0001; // 1/10,000 Stalk per Seed + let STALK_PER_BEAN: f64 = parseFloat(seedsPerBeanBDV.div(BigDecimal.fromString("10000")).toString()); // 3 Seeds per Bean * 1/10,000 Stalk per Seed + + for (let i = 0; i < 8760; i++) { + // Each Season, Farmer's ownership = `current Stalk / total Stalk` + let ownership: f64 = k / K; + let newBDV: f64 = beansPerSeason * ownership; + + // Total Seeds: each seignorage Bean => 3 Seeds + C_i = C + beansPerSeason * _seedsPerBeanBdv; + // Total Stalk: each seignorage Bean => 1 Stalk, each outstanding Bean => 1/10_000 Stalk + K_i = K + beansPerSeason + STALK_PER_SEED * C; + // Farmer BDV: each seignorage Bean => 1 BDV + b_i = b + newBDV; + // Farmer Stalk: each 1 BDV => 1 Stalk, each outstanding Bean => d = 1/5_000 Stalk per Bean + k_i = k + newBDV + STALK_PER_BEAN * b; + + C = C_i; + K = K_i; + b = b_i; + k = k_i; + } + + // Examples: + // ------------------------------- + // b_start = 1 + // b = 1 + // b.minus(b_start) = 0 = 0% APY + // + // b_start = 1 + // b = 1.1 + // b.minus(b_start) = 0.1 = 10% APY + let beanApy = b - b_start; // beanAPY + let stalkApy = k - k_start; // stalkAPY + + return [BigDecimal.fromString(beanApy.toString()), BigDecimal.fromString(stalkApy.toString())]; +} diff --git a/projects/subgraph-beanstalk/src/yield_cache/CacheLoader.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/CacheLoader.ts similarity index 94% rename from projects/subgraph-beanstalk/src/yield_cache/CacheLoader.ts rename to projects/subgraph-beanstalk/src/utils/yield_cache/CacheLoader.ts index 2788e0dd03..1858abe128 100644 --- a/projects/subgraph-beanstalk/src/yield_cache/CacheLoader.ts +++ b/projects/subgraph-beanstalk/src/utils/yield_cache/CacheLoader.ts @@ -1,5 +1,5 @@ import { Address, BigDecimal, BigInt } from "@graphprotocol/graph-ts"; -import { loadSiloYield, loadTokenYield } from "../utils/SiloEntities"; +import { loadSiloYield, loadTokenYield } from "../../entities/Silo"; export function loadSiloCache(SILO_YIELD: string[][]): void { for (let i = 0; i < SILO_YIELD.length; i++) { diff --git a/projects/subgraph-beanstalk/src/yield_cache/window_1/HistoricSilo_10_000.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/window_1/HistoricSilo_10_000.ts similarity index 100% rename from projects/subgraph-beanstalk/src/yield_cache/window_1/HistoricSilo_10_000.ts rename to projects/subgraph-beanstalk/src/utils/yield_cache/window_1/HistoricSilo_10_000.ts diff --git a/projects/subgraph-beanstalk/src/yield_cache/window_1/HistoricSilo_15_000.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/window_1/HistoricSilo_15_000.ts similarity index 100% rename from projects/subgraph-beanstalk/src/yield_cache/window_1/HistoricSilo_15_000.ts rename to projects/subgraph-beanstalk/src/utils/yield_cache/window_1/HistoricSilo_15_000.ts diff --git a/projects/subgraph-beanstalk/src/yield_cache/window_1/HistoricSilo_20_000.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/window_1/HistoricSilo_20_000.ts similarity index 100% rename from projects/subgraph-beanstalk/src/yield_cache/window_1/HistoricSilo_20_000.ts rename to projects/subgraph-beanstalk/src/utils/yield_cache/window_1/HistoricSilo_20_000.ts diff --git a/projects/subgraph-beanstalk/src/yield_cache/window_1/HistoricToken_12_000.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/window_1/HistoricToken_12_000.ts similarity index 100% rename from projects/subgraph-beanstalk/src/yield_cache/window_1/HistoricToken_12_000.ts rename to projects/subgraph-beanstalk/src/utils/yield_cache/window_1/HistoricToken_12_000.ts diff --git a/projects/subgraph-beanstalk/src/yield_cache/window_1/HistoricToken_20_000.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/window_1/HistoricToken_20_000.ts similarity index 100% rename from projects/subgraph-beanstalk/src/yield_cache/window_1/HistoricToken_20_000.ts rename to projects/subgraph-beanstalk/src/utils/yield_cache/window_1/HistoricToken_20_000.ts diff --git a/projects/subgraph-beanstalk/src/utils/yield_cache/window_1/LoadSilo_1.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/window_1/LoadSilo_1.ts new file mode 100644 index 0000000000..9e6f17d71d --- /dev/null +++ b/projects/subgraph-beanstalk/src/utils/yield_cache/window_1/LoadSilo_1.ts @@ -0,0 +1,7 @@ +import { ethereum } from "@graphprotocol/graph-ts"; +import { loadSiloCache } from "../CacheLoader"; +import { SILO_YIELD_24_HOUR_10_000 } from "./HistoricSilo_10_000"; + +export function handleLoadSilo1_1(block: ethereum.Block): void { + loadSiloCache(SILO_YIELD_24_HOUR_10_000); +} diff --git a/projects/subgraph-beanstalk/src/utils/yield_cache/window_1/LoadSilo_2.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/window_1/LoadSilo_2.ts new file mode 100644 index 0000000000..14b3c2a3ec --- /dev/null +++ b/projects/subgraph-beanstalk/src/utils/yield_cache/window_1/LoadSilo_2.ts @@ -0,0 +1,7 @@ +import { ethereum } from "@graphprotocol/graph-ts"; +import { loadSiloCache } from "../CacheLoader"; +import { SILO_YIELD_24_HOUR_15_000 } from "./HistoricSilo_15_000"; + +export function handleLoadSilo1_2(block: ethereum.Block): void { + loadSiloCache(SILO_YIELD_24_HOUR_15_000); +} diff --git a/projects/subgraph-beanstalk/src/utils/yield_cache/window_1/LoadSilo_3.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/window_1/LoadSilo_3.ts new file mode 100644 index 0000000000..fd11fdbdd6 --- /dev/null +++ b/projects/subgraph-beanstalk/src/utils/yield_cache/window_1/LoadSilo_3.ts @@ -0,0 +1,7 @@ +import { ethereum } from "@graphprotocol/graph-ts"; +import { loadSiloCache } from "../CacheLoader"; +import { SILO_YIELD_24_HOUR_20_000 } from "./HistoricSilo_20_000"; + +export function handleLoadSilo1_3(block: ethereum.Block): void { + loadSiloCache(SILO_YIELD_24_HOUR_20_000); +} diff --git a/projects/subgraph-beanstalk/src/utils/yield_cache/window_1/LoadToken_1.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/window_1/LoadToken_1.ts new file mode 100644 index 0000000000..5551144985 --- /dev/null +++ b/projects/subgraph-beanstalk/src/utils/yield_cache/window_1/LoadToken_1.ts @@ -0,0 +1,7 @@ +import { ethereum } from "@graphprotocol/graph-ts"; +import { loadTokenCache } from "../CacheLoader"; +import { TOKEN_YIELD_24_HOUR_12_000 } from "./HistoricToken_12_000"; + +export function handleLoadToken1_1(block: ethereum.Block): void { + loadTokenCache(TOKEN_YIELD_24_HOUR_12_000); +} diff --git a/projects/subgraph-beanstalk/src/utils/yield_cache/window_1/LoadToken_2.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/window_1/LoadToken_2.ts new file mode 100644 index 0000000000..a89ad04008 --- /dev/null +++ b/projects/subgraph-beanstalk/src/utils/yield_cache/window_1/LoadToken_2.ts @@ -0,0 +1,7 @@ +import { ethereum } from "@graphprotocol/graph-ts"; +import { loadTokenCache } from "../CacheLoader"; +import { TOKEN_YIELD_24_HOUR_20_000 } from "./HistoricToken_20_000"; + +export function handleLoadToken1_2(block: ethereum.Block): void { + loadTokenCache(TOKEN_YIELD_24_HOUR_20_000); +} diff --git a/projects/subgraph-beanstalk/src/yield_cache/window_2/HistoricSilo_10_000.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/window_2/HistoricSilo_10_000.ts similarity index 100% rename from projects/subgraph-beanstalk/src/yield_cache/window_2/HistoricSilo_10_000.ts rename to projects/subgraph-beanstalk/src/utils/yield_cache/window_2/HistoricSilo_10_000.ts diff --git a/projects/subgraph-beanstalk/src/yield_cache/window_2/HistoricSilo_15_000.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/window_2/HistoricSilo_15_000.ts similarity index 100% rename from projects/subgraph-beanstalk/src/yield_cache/window_2/HistoricSilo_15_000.ts rename to projects/subgraph-beanstalk/src/utils/yield_cache/window_2/HistoricSilo_15_000.ts diff --git a/projects/subgraph-beanstalk/src/yield_cache/window_2/HistoricSilo_20_000.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/window_2/HistoricSilo_20_000.ts similarity index 100% rename from projects/subgraph-beanstalk/src/yield_cache/window_2/HistoricSilo_20_000.ts rename to projects/subgraph-beanstalk/src/utils/yield_cache/window_2/HistoricSilo_20_000.ts diff --git a/projects/subgraph-beanstalk/src/yield_cache/window_2/HistoricToken_12_000.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/window_2/HistoricToken_12_000.ts similarity index 100% rename from projects/subgraph-beanstalk/src/yield_cache/window_2/HistoricToken_12_000.ts rename to projects/subgraph-beanstalk/src/utils/yield_cache/window_2/HistoricToken_12_000.ts diff --git a/projects/subgraph-beanstalk/src/yield_cache/window_2/HistoricToken_20_000.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/window_2/HistoricToken_20_000.ts similarity index 100% rename from projects/subgraph-beanstalk/src/yield_cache/window_2/HistoricToken_20_000.ts rename to projects/subgraph-beanstalk/src/utils/yield_cache/window_2/HistoricToken_20_000.ts diff --git a/projects/subgraph-beanstalk/src/utils/yield_cache/window_2/LoadSilo_1.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/window_2/LoadSilo_1.ts new file mode 100644 index 0000000000..13724f84d2 --- /dev/null +++ b/projects/subgraph-beanstalk/src/utils/yield_cache/window_2/LoadSilo_1.ts @@ -0,0 +1,7 @@ +import { ethereum } from "@graphprotocol/graph-ts"; +import { loadSiloCache } from "../CacheLoader"; +import { SILO_YIELD_7_DAY_10_000 } from "./HistoricSilo_10_000"; + +export function handleLoadSilo2_1(block: ethereum.Block): void { + loadSiloCache(SILO_YIELD_7_DAY_10_000); +} diff --git a/projects/subgraph-beanstalk/src/utils/yield_cache/window_2/LoadSilo_2.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/window_2/LoadSilo_2.ts new file mode 100644 index 0000000000..8633a11905 --- /dev/null +++ b/projects/subgraph-beanstalk/src/utils/yield_cache/window_2/LoadSilo_2.ts @@ -0,0 +1,7 @@ +import { ethereum } from "@graphprotocol/graph-ts"; +import { loadSiloCache } from "../CacheLoader"; +import { SILO_YIELD_7_DAY_15_000 } from "./HistoricSilo_15_000"; + +export function handleLoadSilo2_2(block: ethereum.Block): void { + loadSiloCache(SILO_YIELD_7_DAY_15_000); +} diff --git a/projects/subgraph-beanstalk/src/utils/yield_cache/window_2/LoadSilo_3.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/window_2/LoadSilo_3.ts new file mode 100644 index 0000000000..28bc4fad74 --- /dev/null +++ b/projects/subgraph-beanstalk/src/utils/yield_cache/window_2/LoadSilo_3.ts @@ -0,0 +1,7 @@ +import { ethereum } from "@graphprotocol/graph-ts"; +import { loadSiloCache } from "../CacheLoader"; +import { SILO_YIELD_7_DAY_20_000 } from "./HistoricSilo_20_000"; + +export function handleLoadSilo2_3(block: ethereum.Block): void { + loadSiloCache(SILO_YIELD_7_DAY_20_000); +} diff --git a/projects/subgraph-beanstalk/src/utils/yield_cache/window_2/LoadToken_1.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/window_2/LoadToken_1.ts new file mode 100644 index 0000000000..06dfd4e555 --- /dev/null +++ b/projects/subgraph-beanstalk/src/utils/yield_cache/window_2/LoadToken_1.ts @@ -0,0 +1,7 @@ +import { ethereum } from "@graphprotocol/graph-ts"; +import { loadTokenCache } from "../CacheLoader"; +import { TOKEN_YIELD_7_DAY_12_000 } from "./HistoricToken_12_000"; + +export function handleLoadToken2_1(block: ethereum.Block): void { + loadTokenCache(TOKEN_YIELD_7_DAY_12_000); +} diff --git a/projects/subgraph-beanstalk/src/utils/yield_cache/window_2/LoadToken_2.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/window_2/LoadToken_2.ts new file mode 100644 index 0000000000..9ffebca8d1 --- /dev/null +++ b/projects/subgraph-beanstalk/src/utils/yield_cache/window_2/LoadToken_2.ts @@ -0,0 +1,7 @@ +import { ethereum } from "@graphprotocol/graph-ts"; +import { loadTokenCache } from "../CacheLoader"; +import { TOKEN_YIELD_7_DAY_20_000 } from "./HistoricToken_20_000"; + +export function handleLoadToken2_2(block: ethereum.Block): void { + loadTokenCache(TOKEN_YIELD_7_DAY_20_000); +} diff --git a/projects/subgraph-beanstalk/src/yield_cache/window_3/HistoricSilo_10_000.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/window_3/HistoricSilo_10_000.ts similarity index 100% rename from projects/subgraph-beanstalk/src/yield_cache/window_3/HistoricSilo_10_000.ts rename to projects/subgraph-beanstalk/src/utils/yield_cache/window_3/HistoricSilo_10_000.ts diff --git a/projects/subgraph-beanstalk/src/yield_cache/window_3/HistoricSilo_15_000.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/window_3/HistoricSilo_15_000.ts similarity index 100% rename from projects/subgraph-beanstalk/src/yield_cache/window_3/HistoricSilo_15_000.ts rename to projects/subgraph-beanstalk/src/utils/yield_cache/window_3/HistoricSilo_15_000.ts diff --git a/projects/subgraph-beanstalk/src/yield_cache/window_3/HistoricSilo_20_000.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/window_3/HistoricSilo_20_000.ts similarity index 100% rename from projects/subgraph-beanstalk/src/yield_cache/window_3/HistoricSilo_20_000.ts rename to projects/subgraph-beanstalk/src/utils/yield_cache/window_3/HistoricSilo_20_000.ts diff --git a/projects/subgraph-beanstalk/src/yield_cache/window_3/HistoricToken_12_000.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/window_3/HistoricToken_12_000.ts similarity index 100% rename from projects/subgraph-beanstalk/src/yield_cache/window_3/HistoricToken_12_000.ts rename to projects/subgraph-beanstalk/src/utils/yield_cache/window_3/HistoricToken_12_000.ts diff --git a/projects/subgraph-beanstalk/src/yield_cache/window_3/HistoricToken_20_000.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/window_3/HistoricToken_20_000.ts similarity index 100% rename from projects/subgraph-beanstalk/src/yield_cache/window_3/HistoricToken_20_000.ts rename to projects/subgraph-beanstalk/src/utils/yield_cache/window_3/HistoricToken_20_000.ts diff --git a/projects/subgraph-beanstalk/src/utils/yield_cache/window_3/LoadSilo_1.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/window_3/LoadSilo_1.ts new file mode 100644 index 0000000000..1d6d3ff8fc --- /dev/null +++ b/projects/subgraph-beanstalk/src/utils/yield_cache/window_3/LoadSilo_1.ts @@ -0,0 +1,7 @@ +import { ethereum } from "@graphprotocol/graph-ts"; +import { loadSiloCache } from "../CacheLoader"; +import { SILO_YIELD_30_DAY_10_000 } from "./HistoricSilo_10_000"; + +export function handleLoadSilo3_1(block: ethereum.Block): void { + loadSiloCache(SILO_YIELD_30_DAY_10_000); +} diff --git a/projects/subgraph-beanstalk/src/utils/yield_cache/window_3/LoadSilo_2.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/window_3/LoadSilo_2.ts new file mode 100644 index 0000000000..22b63f0075 --- /dev/null +++ b/projects/subgraph-beanstalk/src/utils/yield_cache/window_3/LoadSilo_2.ts @@ -0,0 +1,7 @@ +import { ethereum } from "@graphprotocol/graph-ts"; +import { loadSiloCache } from "../CacheLoader"; +import { SILO_YIELD_30_DAY_15_000 } from "./HistoricSilo_15_000"; + +export function handleLoadSilo3_2(block: ethereum.Block): void { + loadSiloCache(SILO_YIELD_30_DAY_15_000); +} diff --git a/projects/subgraph-beanstalk/src/utils/yield_cache/window_3/LoadSilo_3.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/window_3/LoadSilo_3.ts new file mode 100644 index 0000000000..14d2a83034 --- /dev/null +++ b/projects/subgraph-beanstalk/src/utils/yield_cache/window_3/LoadSilo_3.ts @@ -0,0 +1,7 @@ +import { ethereum } from "@graphprotocol/graph-ts"; +import { loadSiloCache } from "../CacheLoader"; +import { SILO_YIELD_30_DAY_20_000 } from "./HistoricSilo_20_000"; + +export function handleLoadSilo3_3(block: ethereum.Block): void { + loadSiloCache(SILO_YIELD_30_DAY_20_000); +} diff --git a/projects/subgraph-beanstalk/src/utils/yield_cache/window_3/LoadToken_1.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/window_3/LoadToken_1.ts new file mode 100644 index 0000000000..709f1485e5 --- /dev/null +++ b/projects/subgraph-beanstalk/src/utils/yield_cache/window_3/LoadToken_1.ts @@ -0,0 +1,7 @@ +import { ethereum } from "@graphprotocol/graph-ts"; +import { loadTokenCache } from "../CacheLoader"; +import { TOKEN_YIELD_30_DAY_12_000 } from "./HistoricToken_12_000"; + +export function handleLoadToken3_1(block: ethereum.Block): void { + loadTokenCache(TOKEN_YIELD_30_DAY_12_000); +} diff --git a/projects/subgraph-beanstalk/src/utils/yield_cache/window_3/LoadToken_2.ts b/projects/subgraph-beanstalk/src/utils/yield_cache/window_3/LoadToken_2.ts new file mode 100644 index 0000000000..8e1d177840 --- /dev/null +++ b/projects/subgraph-beanstalk/src/utils/yield_cache/window_3/LoadToken_2.ts @@ -0,0 +1,7 @@ +import { ethereum } from "@graphprotocol/graph-ts"; +import { loadTokenCache } from "../CacheLoader"; +import { TOKEN_YIELD_30_DAY_20_000 } from "./HistoricToken_20_000"; + +export function handleLoadToken3_2(block: ethereum.Block): void { + loadTokenCache(TOKEN_YIELD_30_DAY_20_000); +} diff --git a/projects/subgraph-beanstalk/src/yield_cache/window_1/LoadSilo_1.ts b/projects/subgraph-beanstalk/src/yield_cache/window_1/LoadSilo_1.ts deleted file mode 100644 index 4579daa4be..0000000000 --- a/projects/subgraph-beanstalk/src/yield_cache/window_1/LoadSilo_1.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ZERO_BI } from "../../../../subgraph-core/utils/Decimals"; -import { DiamondCut } from "../../../generated/Beanstalk-ABIs/PreReplant"; -import { loadBeanstalk } from "../../utils/Beanstalk"; -import { loadSiloCache } from "../CacheLoader"; -import { SILO_YIELD_24_HOUR_10_000 } from "./HistoricSilo_10_000"; - -export function handleLoadSilo1_1(event: DiamondCut): void { - let beanstalk = loadBeanstalk(event.address); - - // Load the historical vAPY figures in bulk at start - if (beanstalk.lastUpgrade == ZERO_BI) { - loadSiloCache(SILO_YIELD_24_HOUR_10_000); - } -} diff --git a/projects/subgraph-beanstalk/src/yield_cache/window_1/LoadSilo_2.ts b/projects/subgraph-beanstalk/src/yield_cache/window_1/LoadSilo_2.ts deleted file mode 100644 index dae3a046c6..0000000000 --- a/projects/subgraph-beanstalk/src/yield_cache/window_1/LoadSilo_2.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ZERO_BI } from "../../../../subgraph-core/utils/Decimals"; -import { DiamondCut } from "../../../generated/Beanstalk-ABIs/PreReplant"; -import { loadBeanstalk } from "../../utils/Beanstalk"; -import { loadSiloCache } from "../CacheLoader"; -import { SILO_YIELD_24_HOUR_15_000 } from "./HistoricSilo_15_000"; - -export function handleLoadSilo1_2(event: DiamondCut): void { - let beanstalk = loadBeanstalk(event.address); - - // Load the historical vAPY figures in bulk at start - if (beanstalk.lastUpgrade == ZERO_BI) { - loadSiloCache(SILO_YIELD_24_HOUR_15_000); - } -} diff --git a/projects/subgraph-beanstalk/src/yield_cache/window_1/LoadSilo_3.ts b/projects/subgraph-beanstalk/src/yield_cache/window_1/LoadSilo_3.ts deleted file mode 100644 index 607d193fd6..0000000000 --- a/projects/subgraph-beanstalk/src/yield_cache/window_1/LoadSilo_3.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ZERO_BI } from "../../../../subgraph-core/utils/Decimals"; -import { DiamondCut } from "../../../generated/Beanstalk-ABIs/PreReplant"; -import { loadBeanstalk } from "../../utils/Beanstalk"; -import { loadSiloCache } from "../CacheLoader"; -import { SILO_YIELD_24_HOUR_20_000 } from "./HistoricSilo_20_000"; - -export function handleLoadSilo1_3(event: DiamondCut): void { - let beanstalk = loadBeanstalk(event.address); - - // Load the historical vAPY figures in bulk at start - if (beanstalk.lastUpgrade == ZERO_BI) { - loadSiloCache(SILO_YIELD_24_HOUR_20_000); - } -} diff --git a/projects/subgraph-beanstalk/src/yield_cache/window_1/LoadToken_1.ts b/projects/subgraph-beanstalk/src/yield_cache/window_1/LoadToken_1.ts deleted file mode 100644 index e997119726..0000000000 --- a/projects/subgraph-beanstalk/src/yield_cache/window_1/LoadToken_1.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ZERO_BI } from "../../../../subgraph-core/utils/Decimals"; -import { DiamondCut } from "../../../generated/Beanstalk-ABIs/PreReplant"; -import { loadBeanstalk } from "../../utils/Beanstalk"; -import { loadTokenCache } from "../CacheLoader"; -import { TOKEN_YIELD_24_HOUR_12_000 } from "./HistoricToken_12_000"; - -export function handleLoadToken1_1(event: DiamondCut): void { - let beanstalk = loadBeanstalk(event.address); - - // Load the historical vAPY figures in bulk at start - if (beanstalk.lastUpgrade == ZERO_BI) { - loadTokenCache(TOKEN_YIELD_24_HOUR_12_000); - } -} diff --git a/projects/subgraph-beanstalk/src/yield_cache/window_1/LoadToken_2.ts b/projects/subgraph-beanstalk/src/yield_cache/window_1/LoadToken_2.ts deleted file mode 100644 index a76315ec93..0000000000 --- a/projects/subgraph-beanstalk/src/yield_cache/window_1/LoadToken_2.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ZERO_BI } from "../../../../subgraph-core/utils/Decimals"; -import { DiamondCut } from "../../../generated/Beanstalk-ABIs/PreReplant"; -import { loadBeanstalk } from "../../utils/Beanstalk"; -import { loadTokenCache } from "../CacheLoader"; -import { TOKEN_YIELD_24_HOUR_20_000 } from "./HistoricToken_20_000"; - -export function handleLoadToken1_2(event: DiamondCut): void { - let beanstalk = loadBeanstalk(event.address); - - // Load the historical vAPY figures in bulk at start - if (beanstalk.lastUpgrade == ZERO_BI) { - loadTokenCache(TOKEN_YIELD_24_HOUR_20_000); - } -} diff --git a/projects/subgraph-beanstalk/src/yield_cache/window_2/LoadSilo_1.ts b/projects/subgraph-beanstalk/src/yield_cache/window_2/LoadSilo_1.ts deleted file mode 100644 index e46610d6ff..0000000000 --- a/projects/subgraph-beanstalk/src/yield_cache/window_2/LoadSilo_1.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ZERO_BI } from "../../../../subgraph-core/utils/Decimals"; -import { DiamondCut } from "../../../generated/Beanstalk-ABIs/PreReplant"; -import { loadBeanstalk } from "../../utils/Beanstalk"; -import { loadSiloCache } from "../CacheLoader"; -import { SILO_YIELD_7_DAY_10_000 } from "./HistoricSilo_10_000"; - -export function handleLoadSilo2_1(event: DiamondCut): void { - let beanstalk = loadBeanstalk(event.address); - - // Load the historical vAPY figures in bulk at start - if (beanstalk.lastUpgrade == ZERO_BI) { - loadSiloCache(SILO_YIELD_7_DAY_10_000); - } -} diff --git a/projects/subgraph-beanstalk/src/yield_cache/window_2/LoadSilo_2.ts b/projects/subgraph-beanstalk/src/yield_cache/window_2/LoadSilo_2.ts deleted file mode 100644 index 448a714f8b..0000000000 --- a/projects/subgraph-beanstalk/src/yield_cache/window_2/LoadSilo_2.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ZERO_BI } from "../../../../subgraph-core/utils/Decimals"; -import { DiamondCut } from "../../../generated/Beanstalk-ABIs/PreReplant"; -import { loadBeanstalk } from "../../utils/Beanstalk"; -import { loadSiloCache } from "../CacheLoader"; -import { SILO_YIELD_7_DAY_15_000 } from "./HistoricSilo_15_000"; - -export function handleLoadSilo2_2(event: DiamondCut): void { - let beanstalk = loadBeanstalk(event.address); - - // Load the historical vAPY figures in bulk at start - if (beanstalk.lastUpgrade == ZERO_BI) { - loadSiloCache(SILO_YIELD_7_DAY_15_000); - } -} diff --git a/projects/subgraph-beanstalk/src/yield_cache/window_2/LoadSilo_3.ts b/projects/subgraph-beanstalk/src/yield_cache/window_2/LoadSilo_3.ts deleted file mode 100644 index 5ada4329f0..0000000000 --- a/projects/subgraph-beanstalk/src/yield_cache/window_2/LoadSilo_3.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ZERO_BI } from "../../../../subgraph-core/utils/Decimals"; -import { DiamondCut } from "../../../generated/Beanstalk-ABIs/PreReplant"; -import { loadBeanstalk } from "../../utils/Beanstalk"; -import { loadSiloCache } from "../CacheLoader"; -import { SILO_YIELD_7_DAY_20_000 } from "./HistoricSilo_20_000"; - -export function handleLoadSilo2_3(event: DiamondCut): void { - let beanstalk = loadBeanstalk(event.address); - - // Load the historical vAPY figures in bulk at start - if (beanstalk.lastUpgrade == ZERO_BI) { - loadSiloCache(SILO_YIELD_7_DAY_20_000); - } -} diff --git a/projects/subgraph-beanstalk/src/yield_cache/window_2/LoadToken_1.ts b/projects/subgraph-beanstalk/src/yield_cache/window_2/LoadToken_1.ts deleted file mode 100644 index 9af4d83f9a..0000000000 --- a/projects/subgraph-beanstalk/src/yield_cache/window_2/LoadToken_1.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ZERO_BI } from "../../../../subgraph-core/utils/Decimals"; -import { DiamondCut } from "../../../generated/Beanstalk-ABIs/PreReplant"; -import { loadBeanstalk } from "../../utils/Beanstalk"; -import { loadTokenCache } from "../CacheLoader"; -import { TOKEN_YIELD_7_DAY_12_000 } from "./HistoricToken_12_000"; - -export function handleLoadToken2_1(event: DiamondCut): void { - let beanstalk = loadBeanstalk(event.address); - - // Load the historical vAPY figures in bulk at start - if (beanstalk.lastUpgrade == ZERO_BI) { - loadTokenCache(TOKEN_YIELD_7_DAY_12_000); - } -} diff --git a/projects/subgraph-beanstalk/src/yield_cache/window_2/LoadToken_2.ts b/projects/subgraph-beanstalk/src/yield_cache/window_2/LoadToken_2.ts deleted file mode 100644 index ffae89e7c2..0000000000 --- a/projects/subgraph-beanstalk/src/yield_cache/window_2/LoadToken_2.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ZERO_BI } from "../../../../subgraph-core/utils/Decimals"; -import { DiamondCut } from "../../../generated/Beanstalk-ABIs/PreReplant"; -import { loadBeanstalk } from "../../utils/Beanstalk"; -import { loadTokenCache } from "../CacheLoader"; -import { TOKEN_YIELD_7_DAY_20_000 } from "./HistoricToken_20_000"; - -export function handleLoadToken2_2(event: DiamondCut): void { - let beanstalk = loadBeanstalk(event.address); - - // Load the historical vAPY figures in bulk at start - if (beanstalk.lastUpgrade == ZERO_BI) { - loadTokenCache(TOKEN_YIELD_7_DAY_20_000); - } -} diff --git a/projects/subgraph-beanstalk/src/yield_cache/window_3/LoadSilo_1.ts b/projects/subgraph-beanstalk/src/yield_cache/window_3/LoadSilo_1.ts deleted file mode 100644 index 19f13a4f28..0000000000 --- a/projects/subgraph-beanstalk/src/yield_cache/window_3/LoadSilo_1.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ZERO_BI } from "../../../../subgraph-core/utils/Decimals"; -import { DiamondCut } from "../../../generated/Beanstalk-ABIs/PreReplant"; -import { loadBeanstalk } from "../../utils/Beanstalk"; -import { loadSiloCache } from "../CacheLoader"; -import { SILO_YIELD_30_DAY_10_000 } from "./HistoricSilo_10_000"; - -export function handleLoadSilo3_1(event: DiamondCut): void { - let beanstalk = loadBeanstalk(event.address); - - // Load the historical vAPY figures in bulk at start - if (beanstalk.lastUpgrade == ZERO_BI) { - loadSiloCache(SILO_YIELD_30_DAY_10_000); - } -} diff --git a/projects/subgraph-beanstalk/src/yield_cache/window_3/LoadSilo_2.ts b/projects/subgraph-beanstalk/src/yield_cache/window_3/LoadSilo_2.ts deleted file mode 100644 index 6c50058e54..0000000000 --- a/projects/subgraph-beanstalk/src/yield_cache/window_3/LoadSilo_2.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ZERO_BI } from "../../../../subgraph-core/utils/Decimals"; -import { DiamondCut } from "../../../generated/Beanstalk-ABIs/PreReplant"; -import { loadBeanstalk } from "../../utils/Beanstalk"; -import { loadSiloCache } from "../CacheLoader"; -import { SILO_YIELD_30_DAY_15_000 } from "./HistoricSilo_15_000"; - -export function handleLoadSilo3_2(event: DiamondCut): void { - let beanstalk = loadBeanstalk(event.address); - - // Load the historical vAPY figures in bulk at start - if (beanstalk.lastUpgrade == ZERO_BI) { - loadSiloCache(SILO_YIELD_30_DAY_15_000); - } -} diff --git a/projects/subgraph-beanstalk/src/yield_cache/window_3/LoadSilo_3.ts b/projects/subgraph-beanstalk/src/yield_cache/window_3/LoadSilo_3.ts deleted file mode 100644 index e9252a6867..0000000000 --- a/projects/subgraph-beanstalk/src/yield_cache/window_3/LoadSilo_3.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ZERO_BI } from "../../../../subgraph-core/utils/Decimals"; -import { DiamondCut } from "../../../generated/Beanstalk-ABIs/PreReplant"; -import { loadBeanstalk } from "../../utils/Beanstalk"; -import { loadSiloCache } from "../CacheLoader"; -import { SILO_YIELD_30_DAY_20_000 } from "./HistoricSilo_20_000"; - -export function handleLoadSilo3_3(event: DiamondCut): void { - let beanstalk = loadBeanstalk(event.address); - - // Load the historical vAPY figures in bulk at start - if (beanstalk.lastUpgrade == ZERO_BI) { - loadSiloCache(SILO_YIELD_30_DAY_20_000); - } -} diff --git a/projects/subgraph-beanstalk/src/yield_cache/window_3/LoadToken_1.ts b/projects/subgraph-beanstalk/src/yield_cache/window_3/LoadToken_1.ts deleted file mode 100644 index f9133bbb3f..0000000000 --- a/projects/subgraph-beanstalk/src/yield_cache/window_3/LoadToken_1.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ZERO_BI } from "../../../../subgraph-core/utils/Decimals"; -import { DiamondCut } from "../../../generated/Beanstalk-ABIs/PreReplant"; -import { loadBeanstalk } from "../../utils/Beanstalk"; -import { loadTokenCache } from "../CacheLoader"; -import { TOKEN_YIELD_30_DAY_12_000 } from "./HistoricToken_12_000"; - -export function handleLoadToken3_1(event: DiamondCut): void { - let beanstalk = loadBeanstalk(event.address); - - // Load the historical vAPY figures in bulk at start - if (beanstalk.lastUpgrade == ZERO_BI) { - loadTokenCache(TOKEN_YIELD_30_DAY_12_000); - } -} diff --git a/projects/subgraph-beanstalk/src/yield_cache/window_3/LoadToken_2.ts b/projects/subgraph-beanstalk/src/yield_cache/window_3/LoadToken_2.ts deleted file mode 100644 index 306b9e2977..0000000000 --- a/projects/subgraph-beanstalk/src/yield_cache/window_3/LoadToken_2.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ZERO_BI } from "../../../../subgraph-core/utils/Decimals"; -import { DiamondCut } from "../../../generated/Beanstalk-ABIs/PreReplant"; -import { loadBeanstalk } from "../../utils/Beanstalk"; -import { loadTokenCache } from "../CacheLoader"; -import { TOKEN_YIELD_30_DAY_20_000 } from "./HistoricToken_20_000"; - -export function handleLoadToken3_2(event: DiamondCut): void { - let beanstalk = loadBeanstalk(event.address); - - // Load the historical vAPY figures in bulk at start - if (beanstalk.lastUpgrade == ZERO_BI) { - loadTokenCache(TOKEN_YIELD_30_DAY_20_000); - } -} diff --git a/projects/subgraph-beanstalk/tests/SeedGauge.test.ts b/projects/subgraph-beanstalk/tests/SeedGauge.test.ts index 5035dde5fa..b00d7c2ec1 100644 --- a/projects/subgraph-beanstalk/tests/SeedGauge.test.ts +++ b/projects/subgraph-beanstalk/tests/SeedGauge.test.ts @@ -1,21 +1,17 @@ import { afterEach, assert, clearStore, describe, test } from "matchstick-as/assembly/index"; import { log } from "matchstick-as/assembly/log"; import { BigInt } from "@graphprotocol/graph-ts"; - import { - handleTemperatureChange, handleBeanToMaxLpGpPerBdvRatioChange, handleGaugePointChange, handleUpdateAverageStalkPerBdvPerSeason, handleFarmerGerminatingStalkBalanceChanged, handleTotalGerminatingBalanceChanged, - handleWhitelistToken_BIP45, handleUpdateGaugeSettings, handleTotalGerminatingStalkChanged, handleTotalStalkChangedFromGermination -} from "../src/GaugeHandler"; - -import { BEAN_ERC20, BEAN_WETH_CP2_WELL, BEANSTALK, UNRIPE_BEAN, UNRIPE_BEAN_3CRV } from "../../subgraph-core/utils/Constants"; +} from "../src/handlers/GaugeHandler"; +import { BEAN_ERC20, BEANSTALK } from "../../subgraph-core/utils/Constants"; import { createBeanToMaxLpGpPerBdvRatioChangeEvent, createFarmerGerminatingStalkBalanceChangedEvent, @@ -29,10 +25,12 @@ import { import { createWhitelistTokenV4Event } from "./event-mocking/Whitelist"; import { createTemperatureChangeEvent } from "./event-mocking/Field"; import { simpleMockPrice } from "../../subgraph-core/tests/event-mocking/Price"; -import { loadSilo } from "../src/utils/SiloEntities"; import { mockBlock } from "../../subgraph-core/tests/event-mocking/Block"; -import { dayFromTimestamp } from "../src/utils/Dates"; import { setSeason } from "./utils/Season"; +import { dayFromTimestamp } from "../../subgraph-core/utils/Dates"; +import { loadSilo } from "../src/entities/Silo"; +import { handleWhitelistToken } from "../src/handlers/SiloHandler"; +import { handleTemperatureChange } from "../src/handlers/FieldHandler"; const ANVIL_ADDR_1 = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266".toLowerCase(); @@ -61,6 +59,7 @@ describe("Seed Gauge", () => { describe("Seasonal Adjustments", () => { test("event: BeanToMaxLpGpPerBdvRatioChange (initialization)", () => { const initialRatio = BigInt.fromI32(66).times(ratioDecimals); + setSeason(20000); handleBeanToMaxLpGpPerBdvRatioChange( createBeanToMaxLpGpPerBdvRatioChangeEvent(BigInt.fromU32(20000), BigInt.fromU32(10), initialRatio) ); @@ -173,7 +172,7 @@ describe("Seed Gauge", () => { describe("Owner Configuration", () => { test("event: WhitelistToken", () => { - handleWhitelistToken_BIP45( + handleWhitelistToken( createWhitelistTokenV4Event( BEAN_ERC20.toHexString(), "0x12345678", @@ -224,7 +223,7 @@ describe("Seed Gauge", () => { const timestamp = BigInt.fromU32(1712793374); const day = dayFromTimestamp(timestamp); assert.notInStore("WhitelistTokenHourlySnapshot", BEAN_ERC20.toHexString() + "-1"); - assert.notInStore("WhitelistTokenDailySnapshot", BEAN_ERC20.toHexString() + "-" + day); + assert.notInStore("WhitelistTokenDailySnapshot", BEAN_ERC20.toHexString() + "-" + day.toString()); let event = createUpdateGaugeSettingsEvent( BEAN_ERC20.toHexString(), @@ -236,7 +235,7 @@ describe("Seed Gauge", () => { handleUpdateGaugeSettings(event); assert.fieldEquals("WhitelistTokenHourlySnapshot", BEAN_ERC20.toHexString() + "-1", "gpSelector", "0x12341234"); - assert.fieldEquals("WhitelistTokenDailySnapshot", BEAN_ERC20.toHexString() + "-" + day, "gpSelector", "0x12341234"); + assert.fieldEquals("WhitelistTokenDailySnapshot", BEAN_ERC20.toHexString() + "-" + day.toString(), "gpSelector", "0x12341234"); }); }); }); diff --git a/projects/subgraph-beanstalk/tests/Silo-Replanted.test.ts b/projects/subgraph-beanstalk/tests/Silo-Replanted.test.ts deleted file mode 100644 index d3059b4e96..0000000000 --- a/projects/subgraph-beanstalk/tests/Silo-Replanted.test.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { BigInt } from "@graphprotocol/graph-ts"; -import { afterEach, assert, clearStore, describe, test } from "matchstick-as/assembly/index"; -import { - handleAddDeposit, - handleDewhitelistToken, - handleRemoveDeposit, - handleWhitelistToken, - handleWhitelistToken_V3 -} from "../src/SiloHandler"; -import { BEAN_ERC20, BEAN_WETH_CP2_WELL, BEANSTALK, LUSD_3POOL } from "../../subgraph-core/utils/Constants"; -import { createAddDepositEvent, createRemoveDepositEvent } from "./event-mocking/Silo"; -import { createDewhitelistTokenEvent, createWhitelistTokenV2Event, createWhitelistTokenV3Event } from "./event-mocking/Whitelist"; -import { ONE_BI } from "../../subgraph-core/utils/Decimals"; - -describe("Mocked Events", () => { - afterEach(() => { - clearStore(); - }); - - describe("Bean", () => { - test("AddDeposit - Silo and Assets updated", () => { - let account = "0x1234567890abcdef1234567890abcdef12345678".toLowerCase(); - let token = BEAN_ERC20.toHexString().toLowerCase(); - - let newAddDepositEvent = createAddDepositEvent(account, token, 6100, 1000, 6, 1000); - - handleAddDeposit(newAddDepositEvent); - - assert.fieldEquals("Silo", account, "depositedBDV", "1000000000"); - assert.fieldEquals("SiloAsset", account + "-" + token, "depositedBDV", "1000000000"); - assert.fieldEquals("SiloAsset", account + "-" + token, "depositedAmount", "1000000000"); - }); - - test("RemoveDeposit - Farmer Silo Amounts 50% Initial", () => { - let account = "0x1234567890abcdef1234567890abcdef12345678".toLowerCase(); - let token = BEAN_ERC20.toHexString().toLowerCase(); - - let newAddDepositEvent = createAddDepositEvent(account, token, 6100, 1000, 6, 1000); - - handleAddDeposit(newAddDepositEvent); - - let newRemoveDepositEvent = createRemoveDepositEvent(account, token, 6100, BigInt.fromString("500000000")); - - handleRemoveDeposit(newRemoveDepositEvent); - - assert.fieldEquals("Silo", account, "depositedBDV", "500000000"); - assert.fieldEquals("SiloDeposit", account + "-" + token + "-6100", "withdrawnAmount", "500000000"); - assert.fieldEquals("SiloDeposit", account + "-" + token + "-6100", "withdrawnBDV", "500000000"); - assert.fieldEquals("SiloAsset", account + "-" + token, "depositedBDV", "500000000"); - assert.fieldEquals("SiloAsset", account + "-" + token, "depositedAmount", "500000000"); - }); - - test("RemoveDeposit - Farmer Silo Amounts 50% Remaining", () => { - let account = "0x1234567890abcdef1234567890abcdef12345678".toLowerCase(); - let token = BEAN_ERC20.toHexString().toLowerCase(); - - let newAddDepositEvent = createAddDepositEvent(account, token, 6100, 1000, 6, 1000); - - handleAddDeposit(newAddDepositEvent); - - let newRemoveDepositEvent = createRemoveDepositEvent(account, token, 6100, BigInt.fromString("500000000")); - - handleRemoveDeposit(newRemoveDepositEvent); - - let secondRemoveDepositEvent = createRemoveDepositEvent(account, token, 6100, BigInt.fromString("250000000")); - - handleRemoveDeposit(secondRemoveDepositEvent); - - assert.fieldEquals("Silo", account, "depositedBDV", "250000000"); - assert.fieldEquals("SiloDeposit", account + "-" + token + "-6100", "withdrawnAmount", "750000000"); - assert.fieldEquals("SiloDeposit", account + "-" + token + "-6100", "withdrawnBDV", "750000000"); - assert.fieldEquals("SiloAsset", account + "-" + token, "depositedBDV", "250000000"); - assert.fieldEquals("SiloAsset", account + "-" + token, "depositedAmount", "250000000"); - }); - }); - - describe("Whitelist", () => { - test("Whitelist token v2", () => { - handleWhitelistToken(createWhitelistTokenV2Event(BEAN_ERC20.toHexString(), "0xabcd1234", ONE_BI, BigInt.fromString("1234"))); - assert.fieldEquals("Silo", BEANSTALK.toHexString(), "whitelistedTokens", "[" + BEAN_ERC20.toHexString() + "]"); - - handleWhitelistToken(createWhitelistTokenV2Event(BEAN_WETH_CP2_WELL.toHexString(), "0xabcd1234", ONE_BI, BigInt.fromString("1234"))); - assert.fieldEquals( - "Silo", - BEANSTALK.toHexString(), - "whitelistedTokens", - "[" + BEAN_ERC20.toHexString() + ", " + BEAN_WETH_CP2_WELL.toHexString() + "]" - ); - }); - - test("Whitelist token v3", () => { - handleWhitelistToken_V3(createWhitelistTokenV3Event(BEAN_ERC20.toHexString(), "0xabcd1234", ONE_BI, BigInt.fromString("1234"))); - assert.fieldEquals("Silo", BEANSTALK.toHexString(), "whitelistedTokens", "[" + BEAN_ERC20.toHexString() + "]"); - - handleWhitelistToken_V3( - createWhitelistTokenV3Event(BEAN_WETH_CP2_WELL.toHexString(), "0xabcd1234", ONE_BI, BigInt.fromString("1234")) - ); - assert.fieldEquals( - "Silo", - BEANSTALK.toHexString(), - "whitelistedTokens", - "[" + BEAN_ERC20.toHexString() + ", " + BEAN_WETH_CP2_WELL.toHexString() + "]" - ); - }); - - // v4 tested in gauge test - - test("Dewhitelist token", () => { - handleWhitelistToken(createWhitelistTokenV2Event(BEAN_ERC20.toHexString(), "0xabcd1234", ONE_BI, BigInt.fromString("1234"))); - handleWhitelistToken(createWhitelistTokenV2Event(BEAN_WETH_CP2_WELL.toHexString(), "0xabcd1234", ONE_BI, BigInt.fromString("1234"))); - assert.fieldEquals( - "Silo", - BEANSTALK.toHexString(), - "whitelistedTokens", - "[" + BEAN_ERC20.toHexString() + ", " + BEAN_WETH_CP2_WELL.toHexString() + "]" - ); - - handleDewhitelistToken(createDewhitelistTokenEvent(BEAN_ERC20.toHexString())); - assert.fieldEquals("Silo", BEANSTALK.toHexString(), "whitelistedTokens", "[" + BEAN_WETH_CP2_WELL.toHexString() + "]"); - assert.fieldEquals("Silo", BEANSTALK.toHexString(), "dewhitelistedTokens", "[" + BEAN_ERC20.toHexString() + "]"); - - // Try dewhitelisting a non-whitelisted token. Nothing should happen - handleDewhitelistToken(createDewhitelistTokenEvent(LUSD_3POOL.toHexString())); - assert.fieldEquals("Silo", BEANSTALK.toHexString(), "whitelistedTokens", "[" + BEAN_WETH_CP2_WELL.toHexString() + "]"); - assert.fieldEquals("Silo", BEANSTALK.toHexString(), "dewhitelistedTokens", "[" + BEAN_ERC20.toHexString() + "]"); - }); - }); -}); diff --git a/projects/subgraph-beanstalk/tests/Silo.test.ts b/projects/subgraph-beanstalk/tests/Silo.test.ts new file mode 100644 index 0000000000..6b3fb9a5e0 --- /dev/null +++ b/projects/subgraph-beanstalk/tests/Silo.test.ts @@ -0,0 +1,305 @@ +import { BigInt } from "@graphprotocol/graph-ts"; +import { afterEach, assert, clearStore, describe, test } from "matchstick-as/assembly/index"; +import { + BEAN_3CRV, + BEAN_ERC20, + BEAN_WETH_CP2_WELL, + BEANSTALK, + GAUGE_BIP45_BLOCK, + LUSD_3POOL, + UNRIPE_BEAN, + UNRIPE_LP +} from "../../subgraph-core/utils/Constants"; +import { + createAddDepositV2Event, + createAddDepositV3Event, + createRemoveDepositsV2Event, + createRemoveDepositV2Event, + createRemoveDepositV3Event +} from "./event-mocking/Silo"; +import { createDewhitelistTokenEvent, createWhitelistTokenV2Event, createWhitelistTokenV3Event } from "./event-mocking/Whitelist"; +import { ONE_BI, ZERO_BI } from "../../subgraph-core/utils/Decimals"; +import { mockBlock } from "../../subgraph-core/tests/event-mocking/Block"; +import { dayFromTimestamp } from "../../subgraph-core/utils/Dates"; +import { setSeason } from "./utils/Season"; +import { + handleAddDeposit_v2, + handleRemoveDeposit_v2, + handleRemoveDeposits_v2, + handleWhitelistToken_v2, + handleWhitelistToken_v3 +} from "../src/handlers/legacy/LegacySiloHandler"; +import { handleAddDeposit, handleDewhitelistToken, handleRemoveDeposit } from "../src/handlers/SiloHandler"; +import { stemFromSeason } from "../src/utils/contracts/SiloCalculations"; + +describe("Silo Events", () => { + afterEach(() => { + clearStore(); + }); + + describe("Deposit/Withdraw", () => { + test("AddDeposit - Silo v2", () => { + let account = "0x1234567890abcdef1234567890abcdef12345678".toLowerCase(); + let token = BEAN_ERC20.toHexString().toLowerCase(); + + let newAddDepositEvent = createAddDepositV2Event(account, token, 6100, 1000, 6, 1000); + handleAddDeposit_v2(newAddDepositEvent); + + assert.fieldEquals("Silo", account, "depositedBDV", "1000000000"); + assert.fieldEquals("SiloDeposit", account + "-" + token + "-season-6100", "season", "6100"); + assert.fieldEquals("SiloDeposit", account + "-" + token + "-season-6100", "depositVersion", "season"); + assert.fieldEquals("SiloDeposit", account + "-" + token + "-season-6100", "stem", "null"); + assert.fieldEquals("SiloDeposit", account + "-" + token + "-season-6100", "stemV31", "-16220000000"); + assert.fieldEquals("SiloDeposit", account + "-" + token + "-season-6100", "depositedAmount", "1000000000"); + assert.fieldEquals("SiloAsset", account + "-" + token, "depositedBDV", "1000000000"); + }); + + test("AddDeposit - Silo v3", () => { + let account = "0x1234567890abcdef1234567890abcdef12345678".toLowerCase(); + let token = BEAN_ERC20.toHexString().toLowerCase(); + + let newAddDepositEvent = createAddDepositV3Event(account, token, BigInt.fromU32(1500), 1000, 6, 1000); + handleAddDeposit(newAddDepositEvent); + + assert.fieldEquals("Silo", account, "depositedBDV", "1000000000"); + assert.fieldEquals("SiloDeposit", account + "-" + token + "-stem-1500", "stem", "1500"); + assert.fieldEquals("SiloDeposit", account + "-" + token + "-stem-1500", "depositVersion", "v3"); + assert.fieldEquals("SiloDeposit", account + "-" + token + "-stem-1500", "stemV31", "1500000000"); + assert.fieldEquals("SiloDeposit", account + "-" + token + "-stem-1500", "depositedAmount", "1000000000"); + assert.fieldEquals("SiloAsset", account + "-" + token, "depositedBDV", "1000000000"); + + // with V3.1 stem + let addDeposit31 = createAddDepositV3Event(account, token, BigInt.fromI64(5700000000), 2500, 6, 2500); + addDeposit31.block = mockBlock(GAUGE_BIP45_BLOCK.plus(ONE_BI)); + handleAddDeposit(addDeposit31); + + assert.fieldEquals("Silo", account, "depositedBDV", "3500000000"); + assert.fieldEquals("SiloDeposit", account + "-" + token + "-stem-5700000000", "stem", "5700000000"); + assert.fieldEquals("SiloDeposit", account + "-" + token + "-stem-5700000000", "depositVersion", "v3.1"); + assert.fieldEquals("SiloDeposit", account + "-" + token + "-stem-5700000000", "stemV31", "5700000000"); + assert.fieldEquals("SiloDeposit", account + "-" + token + "-stem-5700000000", "depositedAmount", "2500000000"); + assert.fieldEquals("SiloAsset", account + "-" + token, "depositedBDV", "3500000000"); + }); + + test("RemoveDeposit - 80% removed", () => { + let account = "0x1234567890abcdef1234567890abcdef12345678".toLowerCase(); + let token = BEAN_ERC20.toHexString().toLowerCase(); + + let newAddDepositEvent = createAddDepositV2Event(account, token, 6100, 1000, 6, 1000); + handleAddDeposit_v2(newAddDepositEvent); + + let newRemoveDepositEvent = createRemoveDepositV2Event(account, token, 6100, BigInt.fromString("800000000")); + handleRemoveDeposit_v2(newRemoveDepositEvent); + + assert.fieldEquals("Silo", account, "depositedBDV", "200000000"); + assert.fieldEquals("SiloDeposit", account + "-" + token + "-season-6100", "depositedAmount", "200000000"); + assert.fieldEquals("SiloDeposit", account + "-" + token + "-season-6100", "depositedBDV", "200000000"); + assert.fieldEquals("SiloAsset", account + "-" + token, "depositedBDV", "200000000"); + assert.fieldEquals("SiloAsset", account + "-" + token, "depositedAmount", "200000000"); + }); + + test("RemoveDeposit - Multiple removals", () => { + let account = "0x1234567890abcdef1234567890abcdef12345678".toLowerCase(); + let token = BEAN_ERC20.toHexString().toLowerCase(); + + let newAddDepositEvent = createAddDepositV2Event(account, token, 6100, 1000, 6, 1000); + handleAddDeposit_v2(newAddDepositEvent); + + let removeEvent = createRemoveDepositV2Event(account, token, 6100, BigInt.fromString("500000000")); + handleRemoveDeposit_v2(removeEvent); + + let removeEvent2 = createRemoveDepositV2Event(account, token, 6100, BigInt.fromString("200000000")); + handleRemoveDeposit_v2(removeEvent2); + + assert.fieldEquals("Silo", account, "depositedBDV", "300000000"); + assert.fieldEquals("SiloDeposit", account + "-" + token + "-season-6100", "depositedAmount", "300000000"); + assert.fieldEquals("SiloDeposit", account + "-" + token + "-season-6100", "depositedBDV", "300000000"); + assert.fieldEquals("SiloAsset", account + "-" + token, "depositedBDV", "300000000"); + assert.fieldEquals("SiloAsset", account + "-" + token, "depositedAmount", "300000000"); + + // Remove the deposit completely + let removeEvent3 = createRemoveDepositV2Event(account, token, 6100, BigInt.fromString("300000000")); + handleRemoveDeposit_v2(removeEvent3); + + assert.fieldEquals("Silo", account, "depositedBDV", "0"); + assert.notInStore("SiloDeposit", account + "-" + token + "-season-6100"); + assert.fieldEquals("SiloAsset", account + "-" + token, "depositedBDV", "0"); + }); + + test("Adding/Removing multiple tokens/types - Silo/Asset balance totals", () => { + let account1 = "0x1234567890abcdef1234567890abcdef12345678".toLowerCase(); + let account2 = "0x1234567890abcdef1234567890abcdef12345679".toLowerCase(); + let token1 = BEAN_ERC20.toHexString().toLowerCase(); + let token2 = BEAN_3CRV.toHexString().toLowerCase(); + + let addV2_1 = createAddDepositV2Event(account1, token1, 6100, 1000, 6, 1000); + handleAddDeposit_v2(addV2_1); + let addV3_1 = createAddDepositV3Event(account1, token1, BigInt.fromU32(70), 2000, 6, 2000); + handleAddDeposit(addV3_1); + let addV3_2 = createAddDepositV3Event(account1, token2, BigInt.fromU32(50), 1000, 6, 1000); + handleAddDeposit(addV3_2); + let addV3_3 = createAddDepositV3Event(account2, token2, BigInt.fromU32(90), 5000, 6, 4000); + handleAddDeposit(addV3_3); + + assert.fieldEquals("Silo", BEANSTALK.toHexString(), "depositedBDV", "8000000000"); + assert.fieldEquals("Silo", account1, "depositedBDV", "4000000000"); + assert.fieldEquals("Silo", account2, "depositedBDV", "4000000000"); + assert.fieldEquals("SiloAsset", BEANSTALK.toHexString() + "-" + token1, "depositedBDV", "3000000000"); + assert.fieldEquals("SiloAsset", BEANSTALK.toHexString() + "-" + token2, "depositedBDV", "5000000000"); + assert.fieldEquals("SiloAsset", account1 + "-" + token1, "depositedBDV", "3000000000"); + assert.fieldEquals("SiloAsset", account1 + "-" + token2, "depositedBDV", "1000000000"); + assert.fieldEquals("SiloAsset", account2 + "-" + token2, "depositedBDV", "4000000000"); + + let removeV2_1 = createRemoveDepositsV2Event(account1, token1, [6100], [BigInt.fromU32(1000000000)], BigInt.fromU32(1000000000)); + handleRemoveDeposits_v2(removeV2_1); + let removeV3_1 = createRemoveDepositV3Event( + account2, + token2, + BigInt.fromU32(90), + BigInt.fromU32(1500000000), + BigInt.fromU32(1500000000) + ); + handleRemoveDeposit(removeV3_1); + + assert.fieldEquals("Silo", BEANSTALK.toHexString(), "depositedBDV", "5500000000"); + assert.fieldEquals("Silo", account1, "depositedBDV", "3000000000"); + assert.fieldEquals("Silo", account2, "depositedBDV", "2500000000"); + assert.fieldEquals("SiloAsset", BEANSTALK.toHexString() + "-" + token1, "depositedBDV", "2000000000"); + assert.fieldEquals("SiloAsset", BEANSTALK.toHexString() + "-" + token2, "depositedBDV", "3500000000"); + assert.fieldEquals("SiloAsset", account1 + "-" + token1, "depositedBDV", "2000000000"); + assert.fieldEquals("SiloAsset", account1 + "-" + token2, "depositedBDV", "1000000000"); + assert.fieldEquals("SiloAsset", account2 + "-" + token2, "depositedBDV", "2500000000"); + }); + + // It is assumed sufficient to test a few fields updating properly + test("Hourly/Daily snapshots update appropriately", () => { + const baseTimestamp = BigInt.fromU32(1712732400); + const hours15 = BigInt.fromU64(15 * 60 * 60); + let account = "0x1234567890abcdef1234567890abcdef12345678".toLowerCase(); + let token = BEAN_ERC20.toHexString().toLowerCase(); + + let addV3_1 = createAddDepositV3Event(account, token, BigInt.fromU32(70), 2000, 6, 2000); + addV3_1.block = mockBlock(ZERO_BI, baseTimestamp); + setSeason(20000); + handleAddDeposit(addV3_1); + + assert.fieldEquals("SiloHourlySnapshot", account + "-20000", "depositedBDV", "2000000000"); + assert.fieldEquals("SiloHourlySnapshot", account + "-20000", "deltaDepositedBDV", "2000000000"); + assert.fieldEquals( + "SiloDailySnapshot", + account + "-" + dayFromTimestamp(addV3_1.block.timestamp).toString(), + "depositedBDV", + "2000000000" + ); + assert.fieldEquals( + "SiloDailySnapshot", + account + "-" + dayFromTimestamp(addV3_1.block.timestamp).toString(), + "deltaDepositedBDV", + "2000000000" + ); + + let addV3_2 = createAddDepositV3Event(account, token, BigInt.fromU32(50), 1000, 6, 1000); + addV3_2.block = mockBlock(ZERO_BI, baseTimestamp.plus(hours15)); + setSeason(20015); + handleAddDeposit(addV3_2); + + assert.fieldEquals("SiloHourlySnapshot", account + "-20015", "depositedBDV", "3000000000"); + assert.fieldEquals("SiloHourlySnapshot", account + "-20015", "deltaDepositedBDV", "1000000000"); + assert.fieldEquals( + "SiloDailySnapshot", + account + "-" + dayFromTimestamp(addV3_2.block.timestamp).toString(), + "depositedBDV", + "3000000000" + ); + assert.fieldEquals( + "SiloDailySnapshot", + account + "-" + dayFromTimestamp(addV3_2.block.timestamp).toString(), + "deltaDepositedBDV", + "3000000000" + ); + + let addV3_3 = createAddDepositV3Event(account, token, BigInt.fromU32(90), 5000, 6, 4000); + addV3_3.block = mockBlock(ZERO_BI, baseTimestamp.plus(hours15).plus(hours15)); + setSeason(20030); + handleAddDeposit(addV3_3); + + assert.fieldEquals("SiloHourlySnapshot", account + "-20030", "depositedBDV", "7000000000"); + assert.fieldEquals("SiloHourlySnapshot", account + "-20030", "deltaDepositedBDV", "4000000000"); + assert.fieldEquals( + "SiloDailySnapshot", + account + "-" + dayFromTimestamp(addV3_3.block.timestamp).toString(), + "depositedBDV", + "7000000000" + ); + assert.fieldEquals( + "SiloDailySnapshot", + account + "-" + dayFromTimestamp(addV3_3.block.timestamp).toString(), + "deltaDepositedBDV", + "4000000000" + ); + }); + }); + + describe("Whitelist", () => { + test("Whitelist token v2", () => { + handleWhitelistToken_v2(createWhitelistTokenV2Event(BEAN_ERC20.toHexString(), "0xabcd1234", ONE_BI, BigInt.fromString("1234"))); + assert.fieldEquals("Silo", BEANSTALK.toHexString(), "whitelistedTokens", "[" + BEAN_ERC20.toHexString() + "]"); + + handleWhitelistToken_v2( + createWhitelistTokenV2Event(BEAN_WETH_CP2_WELL.toHexString(), "0xabcd1234", ONE_BI, BigInt.fromString("1234")) + ); + assert.fieldEquals( + "Silo", + BEANSTALK.toHexString(), + "whitelistedTokens", + "[" + BEAN_ERC20.toHexString() + ", " + BEAN_WETH_CP2_WELL.toHexString() + "]" + ); + }); + + test("Whitelist token v3", () => { + handleWhitelistToken_v3(createWhitelistTokenV3Event(BEAN_ERC20.toHexString(), "0xabcd1234", ONE_BI, BigInt.fromString("1234"))); + assert.fieldEquals("Silo", BEANSTALK.toHexString(), "whitelistedTokens", "[" + BEAN_ERC20.toHexString() + "]"); + + handleWhitelistToken_v3( + createWhitelistTokenV3Event(BEAN_WETH_CP2_WELL.toHexString(), "0xabcd1234", ONE_BI, BigInt.fromString("1234")) + ); + assert.fieldEquals( + "Silo", + BEANSTALK.toHexString(), + "whitelistedTokens", + "[" + BEAN_ERC20.toHexString() + ", " + BEAN_WETH_CP2_WELL.toHexString() + "]" + ); + }); + + // v4 tested in gauge test + + test("Dewhitelist token", () => { + handleWhitelistToken_v2(createWhitelistTokenV2Event(BEAN_ERC20.toHexString(), "0xabcd1234", ONE_BI, BigInt.fromString("1234"))); + handleWhitelistToken_v2( + createWhitelistTokenV2Event(BEAN_WETH_CP2_WELL.toHexString(), "0xabcd1234", ONE_BI, BigInt.fromString("1234")) + ); + assert.fieldEquals( + "Silo", + BEANSTALK.toHexString(), + "whitelistedTokens", + "[" + BEAN_ERC20.toHexString() + ", " + BEAN_WETH_CP2_WELL.toHexString() + "]" + ); + + handleDewhitelistToken(createDewhitelistTokenEvent(BEAN_ERC20.toHexString())); + assert.fieldEquals("Silo", BEANSTALK.toHexString(), "whitelistedTokens", "[" + BEAN_WETH_CP2_WELL.toHexString() + "]"); + assert.fieldEquals("Silo", BEANSTALK.toHexString(), "dewhitelistedTokens", "[" + BEAN_ERC20.toHexString() + "]"); + + // Try dewhitelisting a non-whitelisted token. Nothing should happen + handleDewhitelistToken(createDewhitelistTokenEvent(LUSD_3POOL.toHexString())); + assert.fieldEquals("Silo", BEANSTALK.toHexString(), "whitelistedTokens", "[" + BEAN_WETH_CP2_WELL.toHexString() + "]"); + assert.fieldEquals("Silo", BEANSTALK.toHexString(), "dewhitelistedTokens", "[" + BEAN_ERC20.toHexString() + "]"); + }); + }); +}); + +test("Legacy stem calculation", () => { + assert.bigIntEquals(BigInt.fromI64(-5528000000), stemFromSeason(11446, BEAN_ERC20)); + assert.bigIntEquals(BigInt.fromI64(-31556000000), stemFromSeason(6321, BEAN_3CRV)); + assert.bigIntEquals(BigInt.fromI64(-16272000000), stemFromSeason(6074, UNRIPE_BEAN)); + assert.bigIntEquals(BigInt.fromI64(-32684000000), stemFromSeason(6039, UNRIPE_LP)); +}); diff --git a/projects/subgraph-beanstalk/tests/YieldHandler.test.ts b/projects/subgraph-beanstalk/tests/Yield.test.ts similarity index 93% rename from projects/subgraph-beanstalk/tests/YieldHandler.test.ts rename to projects/subgraph-beanstalk/tests/Yield.test.ts index 17273f331c..088c92e434 100644 --- a/projects/subgraph-beanstalk/tests/YieldHandler.test.ts +++ b/projects/subgraph-beanstalk/tests/Yield.test.ts @@ -1,23 +1,24 @@ import { BigInt, BigDecimal, log, Bytes } from "@graphprotocol/graph-ts"; -import { afterEach, assert, clearStore, describe, test } from "matchstick-as/assembly/index"; -import * as YieldHandler from "../src/YieldHandler"; -import { BI_10, BigDecimal_isClose, ZERO_BD, ZERO_BI } from "../../subgraph-core/utils/Decimals"; -import { loadSilo, loadSiloAsset, loadSiloYield, loadTokenYield, loadWhitelistTokenSetting } from "../src/utils/SiloEntities"; +import { assert, describe, test } from "matchstick-as/assembly/index"; +import { BigDecimal_isClose, ZERO_BD, ZERO_BI } from "../../subgraph-core/utils/Decimals"; +import { loadSilo, loadSiloAsset, loadSiloYield, loadTokenYield, loadWhitelistTokenSetting } from "../src/entities/Silo"; import { BEAN_3CRV, BEAN_ERC20, BEAN_WETH_CP2_WELL, BEANSTALK, UNRIPE_BEAN, - UNRIPE_BEAN_3CRV, + UNRIPE_LP, LUSD_3POOL } from "../../subgraph-core/utils/Constants"; import { setSeason } from "./utils/Season"; +import { calculateAPYPreGauge } from "../src/utils/legacy/LegacyYield"; +import { calculateGaugeVAPYs, updateSiloVAPYs } from "../src/utils/Yield"; describe("APY Calculations", () => { describe("Pre-Gauge", () => { test("No Bean mints", () => { - const apy = YieldHandler.calculateAPYPreGauge( + const apy = calculateAPYPreGauge( BigDecimal.fromString("0"), // n BigDecimal.fromString("2"), // seedsPerBDV BigDecimal.fromString("2"), // seedsPerBeanBDV @@ -34,14 +35,14 @@ describe("APY Calculations", () => { // Sequence recreated here for testing: // https://docs.google.com/spreadsheets/d/1h7pPEydeAMze_uZMZzodTB3kvEXz_dGGje4KKm83gRM/edit#gid=1845553589 test("Yields are higher with 4 seeds", () => { - const apy2 = YieldHandler.calculateAPYPreGauge( + const apy2 = calculateAPYPreGauge( BigDecimal.fromString("1278"), BigDecimal.fromString("3"), BigDecimal.fromString("3"), BigInt.fromString("1636664801904743831"), BigInt.fromString("24942000280720") ); - const apy4 = YieldHandler.calculateAPYPreGauge( + const apy4 = calculateAPYPreGauge( BigDecimal.fromString("1278"), BigDecimal.fromString("4.5"), BigDecimal.fromString("3"), @@ -64,7 +65,7 @@ describe("APY Calculations", () => { describe("With Seed Gauge", () => { test("Token yields - direct calculation", () => { // using non-gauge bdv 19556945 + 24417908 + 164986 (Unripe + 3crv after dewhitelisted) - const apy = YieldHandler.calculateGaugeVAPYs( + const apy = calculateGaugeVAPYs( [-1, 0, -2], BigDecimal.fromString("1278"), [BigDecimal.fromString("100")], @@ -151,7 +152,7 @@ describe("APY Calculations", () => { BEAN_ERC20.toHexString(), BEAN_WETH_CP2_WELL.toHexString(), UNRIPE_BEAN.toHexString(), - UNRIPE_BEAN_3CRV.toHexString() + UNRIPE_LP.toHexString() ]; silo.dewhitelistedTokens = [BEAN_3CRV.toHexString()]; silo.save(); @@ -174,7 +175,7 @@ describe("APY Calculations", () => { urbeanWhitelistSettings.stalkEarnedPerSeason = ZERO_BI; urbeanWhitelistSettings.save(); - let urlpWhitelistSettings = loadWhitelistTokenSetting(UNRIPE_BEAN_3CRV); + let urlpWhitelistSettings = loadWhitelistTokenSetting(UNRIPE_LP); urlpWhitelistSettings.stalkEarnedPerSeason = ZERO_BI; urlpWhitelistSettings.save(); @@ -195,7 +196,7 @@ describe("APY Calculations", () => { urbeanSiloAsset.depositedBDV = BigInt.fromString("19556945000000"); urbeanSiloAsset.save(); - let urlpSiloAsset = loadSiloAsset(BEANSTALK, UNRIPE_BEAN_3CRV); + let urlpSiloAsset = loadSiloAsset(BEANSTALK, UNRIPE_LP); urlpSiloAsset.depositedBDV = BigInt.fromString("24417908000000"); urlpSiloAsset.save(); @@ -217,7 +218,7 @@ describe("APY Calculations", () => { siloYield.save(); /// Actual entity-based calculation here - YieldHandler.updateSiloVAPYs(20000, ZERO_BI, 720); + updateSiloVAPYs(BEANSTALK, ZERO_BI, 720); const desiredPrecision = BigDecimal.fromString("0.0001"); const beanResult = loadTokenYield(BEAN_ERC20, 20000, 720); @@ -241,7 +242,7 @@ describe("APY Calculations", () => { test("Token yields - multiple gauge LP, one with no GP", () => { // 0 is beanweth, 1 is beanwsteth - const apy = YieldHandler.calculateGaugeVAPYs( + const apy = calculateGaugeVAPYs( [0, 1], BigDecimal.fromString("100"), // [BigDecimal.fromString("1"), BigDecimal.fromString("499")], diff --git a/projects/subgraph-beanstalk/tests/event-mocking/Field.ts b/projects/subgraph-beanstalk/tests/event-mocking/Field.ts index f1726f9c9e..b05d3a1bc6 100644 --- a/projects/subgraph-beanstalk/tests/event-mocking/Field.ts +++ b/projects/subgraph-beanstalk/tests/event-mocking/Field.ts @@ -1,6 +1,5 @@ import { Address, BigInt, Bytes, ethereum } from "@graphprotocol/graph-ts"; -import { Sow, PlotTransfer, Harvest } from "../../generated/Beanstalk-ABIs/PreReplant"; -import { TemperatureChange } from "../../generated/Beanstalk-ABIs/SeedGauge"; +import { Sow, PlotTransfer, Harvest, TemperatureChange } from "../../generated/Beanstalk-ABIs/SeedGauge"; import { mockBeanstalkEvent } from "../../../subgraph-core/tests/event-mocking/Util"; export function createWeatherChangeEvent(season: BigInt, caseID: BigInt, change: i32): void {} diff --git a/projects/subgraph-beanstalk/tests/event-mocking/Marketplace.ts b/projects/subgraph-beanstalk/tests/event-mocking/Marketplace.ts index c770d2947f..c9ad6482e4 100644 --- a/projects/subgraph-beanstalk/tests/event-mocking/Marketplace.ts +++ b/projects/subgraph-beanstalk/tests/event-mocking/Marketplace.ts @@ -1,20 +1,20 @@ import { Address, BigInt, Bytes, ethereum } from "@graphprotocol/graph-ts"; import { - PodListingCancelled, PodListingCreated as PodListingCreated_v1, PodListingFilled as PodListingFilled_v1, - PodOrderCancelled, PodOrderCreated as PodOrderCreated_v1, PodOrderFilled as PodOrderFilled_v1 } from "../../generated/Beanstalk-ABIs/PreReplant"; import { PodListingCreated as PodListingCreated_v1_1 } from "../../generated/Beanstalk-ABIs/Replanted"; import { - PodListingCreated as PodListingCreated_v2, - PodListingFilled as PodListingFilled_v2, - PodOrderCreated as PodOrderCreated_v2, - PodOrderFilled as PodOrderFilled_v2 -} from "../../generated/Beanstalk-ABIs/MarketV2"; + PodListingCreated, + PodListingFilled, + PodOrderCreated, + PodOrderFilled, + PodOrderCancelled, + PodListingCancelled +} from "../../generated/Beanstalk-ABIs/SeedGauge"; import { mockBeanstalkEvent } from "../../../subgraph-core/tests/event-mocking/Util"; /** ===== Marketplace V1 Events ===== */ @@ -165,8 +165,8 @@ export function createPodListingCreatedEvent_v2( pricingFunction: Bytes, mode: BigInt, pricingType: BigInt -): PodListingCreated_v2 { - let event = changetype(mockBeanstalkEvent()); +): PodListingCreated { + let event = changetype(mockBeanstalkEvent()); event.parameters = new Array(); let param1 = new ethereum.EventParam("account", ethereum.Value.fromAddress(Address.fromString(account))); @@ -191,7 +191,7 @@ export function createPodListingCreatedEvent_v2( event.parameters.push(param9); event.parameters.push(param10); - return event as PodListingCreated_v2; + return event as PodListingCreated; } export function createPodListingFilledEvent_v2( @@ -201,8 +201,8 @@ export function createPodListingFilledEvent_v2( start: BigInt, amount: BigInt, costInBeans: BigInt -): PodListingFilled_v2 { - let event = changetype(mockBeanstalkEvent()); +): PodListingFilled { + let event = changetype(mockBeanstalkEvent()); event.parameters = new Array(); let param1 = new ethereum.EventParam("from", ethereum.Value.fromAddress(Address.fromString(from))); @@ -219,7 +219,7 @@ export function createPodListingFilledEvent_v2( event.parameters.push(param5); event.parameters.push(param6); - return event as PodListingFilled_v2; + return event as PodListingFilled; } export function createPodOrderCreatedEvent_v2( @@ -231,8 +231,8 @@ export function createPodOrderCreatedEvent_v2( minFillAmount: BigInt, pricingFunction: Bytes, pricingType: BigInt -): PodOrderCreated_v2 { - let event = changetype(mockBeanstalkEvent()); +): PodOrderCreated { + let event = changetype(mockBeanstalkEvent()); event.parameters = new Array(); let param1 = new ethereum.EventParam("account", ethereum.Value.fromAddress(Address.fromString(account))); @@ -253,7 +253,7 @@ export function createPodOrderCreatedEvent_v2( event.parameters.push(param7); event.parameters.push(param8); - return event as PodOrderCreated_v2; + return event as PodOrderCreated; } export function createPodOrderFilledEvent_v2( @@ -264,8 +264,8 @@ export function createPodOrderFilledEvent_v2( start: BigInt, amount: BigInt, costInBeans: BigInt -): PodOrderFilled_v2 { - let event = changetype(mockBeanstalkEvent()); +): PodOrderFilled { + let event = changetype(mockBeanstalkEvent()); event.parameters = new Array(); let param1 = new ethereum.EventParam("from", ethereum.Value.fromAddress(Address.fromString(from))); @@ -284,7 +284,7 @@ export function createPodOrderFilledEvent_v2( event.parameters.push(param6); event.parameters.push(param7); - return event as PodOrderFilled_v2; + return event as PodOrderFilled; } /* Cancellation events */ diff --git a/projects/subgraph-beanstalk/tests/event-mocking/Season.ts b/projects/subgraph-beanstalk/tests/event-mocking/Season.ts index 8dd8a86cdf..b04656e82e 100644 --- a/projects/subgraph-beanstalk/tests/event-mocking/Season.ts +++ b/projects/subgraph-beanstalk/tests/event-mocking/Season.ts @@ -1,6 +1,5 @@ import { Address, BigInt, ethereum } from "@graphprotocol/graph-ts"; -import { Incentivization } from "../../generated/Beanstalk-ABIs/PreReplant"; - +import { Incentivization } from "../../generated/Beanstalk-ABIs/SeedGauge"; import { mockBeanstalkEvent } from "../../../subgraph-core/tests/event-mocking/Util"; export function createSunriseEvent(season: BigInt): void {} diff --git a/projects/subgraph-beanstalk/tests/event-mocking/Silo.ts b/projects/subgraph-beanstalk/tests/event-mocking/Silo.ts index 1364d8bbf0..3150b81c4b 100644 --- a/projects/subgraph-beanstalk/tests/event-mocking/Silo.ts +++ b/projects/subgraph-beanstalk/tests/event-mocking/Silo.ts @@ -1,32 +1,66 @@ import { Address, BigInt, Bytes, ethereum } from "@graphprotocol/graph-ts"; -import { newMockEvent } from "matchstick-as/assembly/index"; - import { - AddDeposit, - RemoveDeposit, - RemoveDeposits, + AddDeposit as AddDepositV2, + RemoveDeposit as RemoveDepositV2, + RemoveDeposits as RemoveDepositsV2, AddWithdrawal, RemoveWithdrawal, - RemoveWithdrawals, + RemoveWithdrawals +} from "../../generated/Beanstalk-ABIs/Replanted"; +import { BEAN_DECIMALS } from "../../../subgraph-core/utils/Constants"; +import { mockBeanstalkEvent } from "../../../subgraph-core/tests/event-mocking/Util"; +import { + AddDeposit, + RemoveDeposits, + RemoveDeposit, SeedsBalanceChanged, StalkBalanceChanged, Plant -} from "../../generated/Beanstalk-ABIs/MarketV2"; -import { handleAddDeposit } from "../../src/SiloHandler"; -import { BEAN_DECIMALS } from "../../../subgraph-core/utils/Constants"; +} from "../../generated/Beanstalk-ABIs/SeedGauge"; +export function createAddDepositV2Event( + account: string, + token: string, + season: i32, + amount: i32, + tokenDecimals: i32, + bdv: i32 +): AddDepositV2 { + let addDepositEvent = changetype(mockBeanstalkEvent()); + addDepositEvent.parameters = new Array(); + let accountParam = new ethereum.EventParam("account", ethereum.Value.fromAddress(Address.fromString(account))); + let tokenParam = new ethereum.EventParam("token", ethereum.Value.fromAddress(Address.fromString(token))); + let seasonParam = new ethereum.EventParam("season", ethereum.Value.fromI32(season)); + let amountParam = new ethereum.EventParam( + "amount", + ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(amount).times(BigInt.fromI32(10 ** tokenDecimals))) + ); + let bdvParam = new ethereum.EventParam( + "bdv", + ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(bdv).times(BigInt.fromI32(10 ** BEAN_DECIMALS))) + ); + + addDepositEvent.parameters.push(accountParam); + addDepositEvent.parameters.push(tokenParam); + addDepositEvent.parameters.push(seasonParam); + addDepositEvent.parameters.push(amountParam); + addDepositEvent.parameters.push(bdvParam); -export function handleAddDeposits(events: AddDeposit[]): void { - events.forEach((event) => { - handleAddDeposit(event); - }); + return addDepositEvent as AddDepositV2; } -export function createAddDepositEvent(account: string, token: string, season: i32, amount: i32, tokenDecimals: i32, bdv: i32): AddDeposit { - let addDepositEvent = changetype(newMockEvent()); +export function createAddDepositV3Event( + account: string, + token: string, + stem: BigInt, + amount: i32, + tokenDecimals: i32, + bdv: i32 +): AddDeposit { + let addDepositEvent = changetype(mockBeanstalkEvent()); addDepositEvent.parameters = new Array(); let accountParam = new ethereum.EventParam("account", ethereum.Value.fromAddress(Address.fromString(account))); let tokenParam = new ethereum.EventParam("token", ethereum.Value.fromAddress(Address.fromString(token))); - let seasonParam = new ethereum.EventParam("season", ethereum.Value.fromI32(season)); + let stemParam = new ethereum.EventParam("stem", ethereum.Value.fromSignedBigInt(stem)); let amountParam = new ethereum.EventParam( "amount", ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(amount).times(BigInt.fromI32(10 ** tokenDecimals))) @@ -38,15 +72,15 @@ export function createAddDepositEvent(account: string, token: string, season: i3 addDepositEvent.parameters.push(accountParam); addDepositEvent.parameters.push(tokenParam); - addDepositEvent.parameters.push(seasonParam); + addDepositEvent.parameters.push(stemParam); addDepositEvent.parameters.push(amountParam); addDepositEvent.parameters.push(bdvParam); return addDepositEvent as AddDeposit; } -export function createRemoveDepositEvent(account: string, token: string, season: i32, amount: BigInt): RemoveDeposit { - let removeDepositEvent = changetype(newMockEvent()); +export function createRemoveDepositV2Event(account: string, token: string, season: i32, amount: BigInt): RemoveDepositV2 { + let removeDepositEvent = changetype(mockBeanstalkEvent()); removeDepositEvent.parameters = new Array(); let accountParam = new ethereum.EventParam("account", ethereum.Value.fromAddress(Address.fromString(account))); let tokenParam = new ethereum.EventParam("token", ethereum.Value.fromAddress(Address.fromString(token))); @@ -58,78 +92,149 @@ export function createRemoveDepositEvent(account: string, token: string, season: removeDepositEvent.parameters.push(seasonParam); removeDepositEvent.parameters.push(amountParam); + return removeDepositEvent as RemoveDepositV2; +} + +export function createRemoveDepositV3Event(account: string, token: string, stem: BigInt, amount: BigInt, bdv: BigInt): RemoveDeposit { + let removeDepositEvent = changetype(mockBeanstalkEvent()); + removeDepositEvent.parameters = new Array(); + let accountParam = new ethereum.EventParam("account", ethereum.Value.fromAddress(Address.fromString(account))); + let tokenParam = new ethereum.EventParam("token", ethereum.Value.fromAddress(Address.fromString(token))); + let stemParam = new ethereum.EventParam("stem", ethereum.Value.fromSignedBigInt(stem)); + let amountParam = new ethereum.EventParam("amount", ethereum.Value.fromUnsignedBigInt(amount)); + let bdvParam = new ethereum.EventParam("bdv", ethereum.Value.fromUnsignedBigInt(bdv)); + + removeDepositEvent.parameters.push(accountParam); + removeDepositEvent.parameters.push(tokenParam); + removeDepositEvent.parameters.push(stemParam); + removeDepositEvent.parameters.push(amountParam); + removeDepositEvent.parameters.push(bdvParam); + return removeDepositEvent as RemoveDeposit; } -export function createRemoveDepositsEvent( +export function createRemoveDepositsV2Event( account: string, token: string, seasons: i32[], amounts: BigInt[], amount: BigInt +): RemoveDepositsV2 { + let event = changetype(mockBeanstalkEvent()); + event.parameters = new Array(); + + let seasonsArray: ethereum.Value[] = []; + for (let i = 0; i < seasons.length; ++i) { + seasonsArray.push(ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(seasons[i]))); + } + + let amountsArray: ethereum.Value[] = []; + for (let i = 0; i < amounts.length; ++i) { + amountsArray.push(ethereum.Value.fromUnsignedBigInt(amounts[i])); + } + + let param1 = new ethereum.EventParam("account", ethereum.Value.fromAddress(Address.fromString(account))); + let param2 = new ethereum.EventParam("token", ethereum.Value.fromAddress(Address.fromString(token))); + let param3 = new ethereum.EventParam("seasons", ethereum.Value.fromArray(seasonsArray)); + let param4 = new ethereum.EventParam("amounts", ethereum.Value.fromArray(amountsArray)); + let param5 = new ethereum.EventParam("amount", ethereum.Value.fromUnsignedBigInt(amount)); + + event.parameters.push(param1); + event.parameters.push(param2); + event.parameters.push(param3); + event.parameters.push(param4); + event.parameters.push(param5); + + return event as RemoveDepositsV2; +} + +export function createRemoveDepositsV3Event( + account: string, + token: string, + stems: BigInt[], + amounts: BigInt[], + amount: BigInt, + bdvs: BigInt[] ): RemoveDeposits { - let event = changetype(newMockEvent()); + let event = changetype(mockBeanstalkEvent()); event.parameters = new Array(); + let stemsArray: ethereum.Value[] = []; + for (let i = 0; i < stems.length; ++i) { + stemsArray.push(ethereum.Value.fromSignedBigInt(stems[i])); + } + + let amountsArray: ethereum.Value[] = []; + for (let i = 0; i < amounts.length; ++i) { + amountsArray.push(ethereum.Value.fromUnsignedBigInt(amounts[i])); + } + + let bdvsArray: ethereum.Value[] = []; + for (let i = 0; i < bdvs.length; ++i) { + bdvsArray.push(ethereum.Value.fromUnsignedBigInt(bdvs[i])); + } + let param1 = new ethereum.EventParam("account", ethereum.Value.fromAddress(Address.fromString(account))); - let param2 = new ethereum.EventParam("token", ethereum.Value.fromAddress(Address.fromString(account))); - let param3 = new ethereum.EventParam("seasons", ethereum.Value.fromAddress(Address.fromString(account))); - let param4 = new ethereum.EventParam("amounts", ethereum.Value.fromAddress(Address.fromString(account))); - let param5 = new ethereum.EventParam("amount", ethereum.Value.fromAddress(Address.fromString(account))); + let param2 = new ethereum.EventParam("token", ethereum.Value.fromAddress(Address.fromString(token))); + let param3 = new ethereum.EventParam("stems", ethereum.Value.fromArray(stemsArray)); + let param4 = new ethereum.EventParam("amounts", ethereum.Value.fromArray(amountsArray)); + let param5 = new ethereum.EventParam("amount", ethereum.Value.fromUnsignedBigInt(amount)); + let param6 = new ethereum.EventParam("bdvs", ethereum.Value.fromArray(bdvsArray)); event.parameters.push(param1); event.parameters.push(param2); event.parameters.push(param3); event.parameters.push(param4); event.parameters.push(param5); + event.parameters.push(param6); return event as RemoveDeposits; } export function createAddWithdrawalEvent(account: string, token: string, season: i32, amount: BigInt): AddWithdrawal { - let event = changetype(newMockEvent()); + let event = changetype(mockBeanstalkEvent()); event.parameters = new Array(); return event as AddWithdrawal; } export function createRemoveWithdrawalEvent(account: string, token: string, season: i32, amount: BigInt): RemoveWithdrawal { - let event = changetype(newMockEvent()); + let event = changetype(mockBeanstalkEvent()); event.parameters = new Array(); return event as RemoveWithdrawal; } export function createRemoveWithdrawalsEvent(account: string, token: string, seasons: i32[], amount: BigInt): RemoveWithdrawals { - let event = changetype(newMockEvent()); + let event = changetype(mockBeanstalkEvent()); event.parameters = new Array(); return event as RemoveWithdrawals; } export function createSeedsBalanceChangedEvent(account: string, delta: BigInt): SeedsBalanceChanged { - let event = changetype(newMockEvent()); + let event = changetype(mockBeanstalkEvent()); event.parameters = new Array(); return event as SeedsBalanceChanged; } export function createStalkBalanceChangedEvent(account: string, delta: BigInt, rootDelta: BigInt): StalkBalanceChanged { - let event = changetype(newMockEvent()); + let event = changetype(mockBeanstalkEvent()); event.parameters = new Array(); return event as StalkBalanceChanged; } export function createPlantEvent(account: string, amount: BigInt): Plant { - let event = changetype(newMockEvent()); + let event = changetype(mockBeanstalkEvent()); event.parameters = new Array(); return event as Plant; } export function createWhitelistTokenEvent(token: string, selector: Bytes, seeds: BigInt, stalk: BigInt): WhitelistToken { - let event = changetype(newMockEvent()); + let event = changetype(mockBeanstalkEvent()); event.parameters = new Array(); return event as WhitelistToken; } export function createDewhitelistTokenEvent(token: string): DewhitelistToken { - let event = changetype(newMockEvent()); + let event = changetype(mockBeanstalkEvent()); event.parameters = new Array(); let param1 = new ethereum.EventParam("token", ethereum.Value.fromAddress(Address.fromString(token))); event.parameters.push(param1); diff --git a/projects/subgraph-beanstalk/tests/event-mocking/Whitelist.ts b/projects/subgraph-beanstalk/tests/event-mocking/Whitelist.ts index cbc4944afb..dcd20b5f48 100644 --- a/projects/subgraph-beanstalk/tests/event-mocking/Whitelist.ts +++ b/projects/subgraph-beanstalk/tests/event-mocking/Whitelist.ts @@ -1,8 +1,8 @@ import { Address, BigInt, Bytes, ethereum } from "@graphprotocol/graph-ts"; -import { WhitelistToken as WhitelistToken_V2, DewhitelistToken } from "../../generated/Beanstalk-ABIs/MarketV2"; +import { WhitelistToken as WhitelistToken_V2 } from "../../generated/Beanstalk-ABIs/Replanted"; import { WhitelistToken as WhitelistToken_V3 } from "../../generated/Beanstalk-ABIs/SiloV3"; -import { WhitelistToken as WhitelistToken_V4 } from "../../generated/Beanstalk-ABIs/SeedGauge"; +import { WhitelistToken, DewhitelistToken } from "../../generated/Beanstalk-ABIs/SeedGauge"; import { mockBeanstalkEvent } from "../../../subgraph-core/tests/event-mocking/Util"; export function createWhitelistTokenV2Event(token: string, selector: string, seeds: BigInt, stalk: BigInt): WhitelistToken_V2 { @@ -53,8 +53,8 @@ export function createWhitelistTokenV4Event( lwSelector: string, gaugePoints: BigInt, optimalPercentDepositedBdv: BigInt -): WhitelistToken_V4 { - let event = changetype(mockBeanstalkEvent()); +): WhitelistToken { + let event = changetype(mockBeanstalkEvent()); event.parameters = new Array(); let param1 = new ethereum.EventParam("token", ethereum.Value.fromAddress(Address.fromString(token))); @@ -75,7 +75,7 @@ export function createWhitelistTokenV4Event( event.parameters.push(param7); event.parameters.push(param8); - return event as WhitelistToken_V4; + return event as WhitelistToken; } export function createDewhitelistTokenEvent(token: string): DewhitelistToken { diff --git a/projects/subgraph-beanstalk/tests/utils/Field.ts b/projects/subgraph-beanstalk/tests/utils/Field.ts index 31103182ec..879381bcf5 100644 --- a/projects/subgraph-beanstalk/tests/utils/Field.ts +++ b/projects/subgraph-beanstalk/tests/utils/Field.ts @@ -1,9 +1,9 @@ import { BigInt, ethereum, log } from "@graphprotocol/graph-ts"; import { assert, createMockedFunction } from "matchstick-as/assembly/index"; import { createHarvestEvent, createPlotTransferEvent, createSowEvent } from "../event-mocking/Field"; -import { handleHarvest, handlePlotTransfer, handleSow } from "../../src/FieldHandler"; +import { handleHarvest, handlePlotTransfer, handleSow } from "../../src/handlers/FieldHandler"; import { createIncentivizationEvent } from "../event-mocking/Season"; -import { handleIncentive } from "../../src/SeasonHandler"; +import { handleIncentive } from "../../src/handlers/SeasonHandler"; import { ZERO_BI } from "../../../subgraph-core/utils/Decimals"; import { BEANSTALK } from "../../../subgraph-core/utils/Constants"; diff --git a/projects/subgraph-beanstalk/tests/utils/Marketplace.ts b/projects/subgraph-beanstalk/tests/utils/Marketplace.ts index 19ef5f0ac3..2411a8fc87 100644 --- a/projects/subgraph-beanstalk/tests/utils/Marketplace.ts +++ b/projects/subgraph-beanstalk/tests/utils/Marketplace.ts @@ -1,18 +1,5 @@ import { BigInt, Bytes, ethereum, log } from "@graphprotocol/graph-ts"; import { assert } from "matchstick-as/assembly/index"; -import { - handlePodListingCancelled, - handlePodListingCreated, - handlePodListingCreated_v1_1, - handlePodListingCreated_v2, - handlePodListingFilled, - handlePodListingFilled_v2, - handlePodOrderCancelled, - handlePodOrderCreated, - handlePodOrderCreated_v2, - handlePodOrderFilled, - handlePodOrderFilled_v2 -} from "../../src/MarketplaceHandler"; import { createPodListingCancelledEvent, createPodListingCreatedEvent, @@ -28,22 +15,37 @@ import { } from "../event-mocking/Marketplace"; import { BI_10, ONE_BI, ZERO_BI } from "../../../subgraph-core/utils/Decimals"; import { - PodListingCreated as PodListingCreated_v2, - PodListingFilled as PodListingFilled_v2, - PodOrderCreated as PodOrderCreated_v2, - PodOrderFilled as PodOrderFilled_v2 -} from "../../generated/Beanstalk-ABIs/MarketV2"; + PodListingCreated, + PodListingFilled, + PodOrderCreated, + PodOrderFilled, + PodOrderCancelled, + PodListingCancelled +} from "../../generated/Beanstalk-ABIs/SeedGauge"; import { BEANSTALK } from "../../../subgraph-core/utils/Constants"; import { transferPlot } from "./Field"; import { - PodOrderCancelled, - PodListingCancelled, PodListingCreated as PodListingCreated_v1, PodListingFilled as PodListingFilled_v1, PodOrderCreated as PodOrderCreated_v1, PodOrderFilled as PodOrderFilled_v1 } from "../../generated/Beanstalk-ABIs/PreReplant"; import { PodListingCreated as PodListingCreated_v1_1 } from "../../generated/Beanstalk-ABIs/Replanted"; +import { + handlePodListingCreated_v1, + handlePodListingCreated_v1_1, + handlePodListingFilled_v1, + handlePodOrderCreated_v1, + handlePodOrderFilled_v1 +} from "../../src/handlers/legacy/LegacyMarketplaceHandler"; +import { + handlePodListingCancelled, + handlePodListingCreated, + handlePodListingFilled, + handlePodOrderCancelled, + handlePodOrderCreated, + handlePodOrderFilled +} from "../../src/handlers/MarketplaceHandler"; const pricingFunction = Bytes.fromHexString( "0x0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000012c000000000000000000000000000000000000000000000000000000000000019000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001010101010101010101010101010000" @@ -65,7 +67,7 @@ export function fillListing_v1( transferPlot(from, to, listingIndex.plus(listingStart), podAmount); const event = createPodListingFilledEvent(from, to, listingIndex, listingStart, podAmount); - handlePodListingFilled(event); + handlePodListingFilled_v1(event); // Assert PodFill const podFillId = getPodFillId(event.params.index, event); @@ -87,9 +89,9 @@ export function fillListing_v2( listingStart: BigInt, podAmount: BigInt, costInBeans: BigInt -): PodListingFilled_v2 { +): PodListingFilled { const event = createPodListingFilledEvent_v2(from, to, listingIndex, listingStart, podAmount, costInBeans); - handlePodListingFilled_v2(event); + handlePodListingFilled(event); // Perform plot transfer transferPlot(from, to, listingIndex.plus(listingStart), podAmount); @@ -117,7 +119,7 @@ export function fillOrder_v1( pricePerPod: BigInt ): PodOrderFilled_v1 { const event = createPodOrderFilledEvent(from, to, orderId, index, start, podAmount); - handlePodOrderFilled(event); + handlePodOrderFilled_v1(event); // Perform plot transfer transferPlot(from, to, index.plus(start), podAmount); @@ -143,9 +145,9 @@ export function fillOrder_v2( start: BigInt, podAmount: BigInt, costInBeans: BigInt -): PodOrderFilled_v2 { +): PodOrderFilled { const event = createPodOrderFilledEvent_v2(from, to, orderId, index, start, podAmount, costInBeans); - handlePodOrderFilled_v2(event); + handlePodOrderFilled(event); // Perform plot transfer transferPlot(from, to, index.plus(start), podAmount); @@ -207,7 +209,7 @@ function assertListingCreated_v1_1(event: PodListingCreated_v1_1): void { assert.fieldEquals("PodListing", listingID, "mode", event.params.mode.toString()); } -function assertListingCreated_v2(event: PodListingCreated_v2): void { +function assertListingCreated_v2(event: PodListingCreated): void { let listingID = event.params.account.toHexString() + "-" + event.params.index.toString(); assert.fieldEquals("PodListing", listingID, "plot", event.params.index.toString()); assert.fieldEquals("PodListing", listingID, "farmer", event.params.account.toHexString()); @@ -242,7 +244,7 @@ function assertOrderCreated_v1(account: string, event: PodOrderCreated_v1): void assert.fieldEquals("PodOrder", orderID, "pricePerPod", event.params.pricePerPod.toString()); } -function assertOrderCreated_v2(account: string, event: PodOrderCreated_v2): void { +function assertOrderCreated_v2(account: string, event: PodOrderCreated): void { let orderID = event.params.id.toHexString(); assert.fieldEquals("PodOrder", orderID, "historyID", orderID + "-" + event.block.timestamp.toString() + "-" + event.logIndex.toString()); assert.fieldEquals("PodOrder", orderID, "farmer", account); @@ -265,7 +267,7 @@ export function createListing_v1( maxHarvestableIndex: BigInt ): PodListingCreated_v1 { const event = createPodListingCreatedEvent(account, index, start, listedPods, pricePerPod, maxHarvestableIndex, true); - handlePodListingCreated(event); + handlePodListingCreated_v1(event); assertListingCreated_v1(event); return event; } @@ -290,7 +292,7 @@ export function createListing_v2( listedPods: BigInt, start: BigInt, maxHarvestableIndex: BigInt -): PodListingCreated_v2 { +): PodListingCreated { const event = createPodListingCreatedEvent_v2( account, index, @@ -303,21 +305,21 @@ export function createListing_v2( BigInt.fromI32(0), BigInt.fromI32(1) ); - handlePodListingCreated_v2(event); + handlePodListingCreated(event); assertListingCreated_v2(event); return event; } export function createOrder_v1(account: string, id: Bytes, beans: BigInt, pricePerPod: BigInt, maxPlaceInLine: BigInt): PodOrderCreated_v1 { const event = createPodOrderCreatedEvent(account, id, beans.times(BI_10.pow(6)).div(pricePerPod), pricePerPod, maxPlaceInLine); - handlePodOrderCreated(event); + handlePodOrderCreated_v1(event); assertOrderCreated_v1(account, event); return event; } -export function createOrder_v2(account: string, id: Bytes, beans: BigInt, pricePerPod: BigInt, maxPlaceInLine: BigInt): PodOrderCreated_v2 { +export function createOrder_v2(account: string, id: Bytes, beans: BigInt, pricePerPod: BigInt, maxPlaceInLine: BigInt): PodOrderCreated { const event = createPodOrderCreatedEvent_v2(account, id, beans, pricePerPod, maxPlaceInLine, ONE_BI, pricingFunction, ZERO_BI); - handlePodOrderCreated_v2(event); + handlePodOrderCreated(event); assertOrderCreated_v2(account, event); return event; } diff --git a/projects/subgraph-beanstalk/tests/utils/Season.ts b/projects/subgraph-beanstalk/tests/utils/Season.ts index 775486c734..79ab3b69b1 100644 --- a/projects/subgraph-beanstalk/tests/utils/Season.ts +++ b/projects/subgraph-beanstalk/tests/utils/Season.ts @@ -1,5 +1,5 @@ import { BEANSTALK } from "../../../subgraph-core/utils/Constants"; -import { loadBeanstalk } from "../../src/utils/Beanstalk"; +import { loadBeanstalk } from "../../src/entities/Beanstalk"; export function setSeason(season: u32): void { let beanstalk = loadBeanstalk(BEANSTALK); diff --git a/projects/subgraph-core/abis/Beanstalk/Beanstalk-BIP45.json b/projects/subgraph-core/abis/Beanstalk/Beanstalk-BIP45.json index 859fa13f12..751ef14717 100644 --- a/projects/subgraph-core/abis/Beanstalk/Beanstalk-BIP45.json +++ b/projects/subgraph-core/abis/Beanstalk/Beanstalk-BIP45.json @@ -8292,5 +8292,24 @@ ], "stateMutability": "payable", "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "beans", + "type": "uint256" + } + ], + "name": "Incentivization", + "type": "event" } ] diff --git a/projects/subgraph-core/utils/Constants.ts b/projects/subgraph-core/utils/Constants.ts index 5c6f9cb042..2c68a7d943 100644 --- a/projects/subgraph-core/utils/Constants.ts +++ b/projects/subgraph-core/utils/Constants.ts @@ -7,8 +7,8 @@ export const ADDRESS_ZERO = Address.fromString("0x000000000000000000000000000000 export const BEAN_ERC20_V1 = Address.fromString("0xDC59ac4FeFa32293A95889Dc396682858d52e5Db"); export const BEAN_ERC20 = Address.fromString("0xBEA0000029AD1c77D3d5D23Ba2D8893dB9d1Efab"); export const UNRIPE_BEAN = Address.fromString("0x1bea0050e63e05fbb5d8ba2f10cf5800b6224449"); +export const UNRIPE_LP = Address.fromString("0x1BEA3CcD22F4EBd3d37d731BA31Eeca95713716D"); export const BEAN_3CRV = Address.fromString("0xc9C32cd16Bf7eFB85Ff14e0c8603cc90F6F2eE49"); -export const UNRIPE_BEAN_3CRV = Address.fromString("0x1BEA3CcD22F4EBd3d37d731BA31Eeca95713716D"); export const BEANSTALK_FARMS = Address.fromString("0x21de18b6a8f78ede6d16c50a167f6b222dc08df7"); export const WETH = Address.fromString("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"); export const LUSD = Address.fromString("0x5f98805A4E8be255a32880FDeC7F6728C6568bA0"); @@ -43,9 +43,12 @@ export const DELTA_HUMIDITY = BigDecimal.fromString("0.5"); export const CALCULATIONS_CURVE = Address.fromString("0x25BF7b72815476Dd515044F9650Bf79bAd0Df655"); +export const REPLANT_SEASON = BigInt.fromU32(6075); + // Milestone blocks export const BEANSTALK_BLOCK = BigInt.fromU32(12974075); export const EXPLOIT_BLOCK = BigInt.fromU32(14602790); +export const NEW_BEAN_TOKEN_BLOCK = BigInt.fromU32(15278082); export const REPLANT_SUNRISE_BLOCK = BigInt.fromU32(15289934); export const GAUGE_BIP45_BLOCK = BigInt.fromU32(19927634);