diff --git a/apps/server/.wundergraph/metricHelper.ts b/apps/server/.wundergraph/metricHelper.ts index 14cfd61..21bc55b 100644 --- a/apps/server/.wundergraph/metricHelper.ts +++ b/apps/server/.wundergraph/metricHelper.ts @@ -442,6 +442,35 @@ const getSupplyCategories = (records: TokenSupply[], ohmIndex: number): [SupplyC // TokenRecord metrics // +/** + * Determines if the given record is a variant of OHM. + * + * @param record + * @returns + */ +const isOHM = (record: TokenRecord): boolean => { + return getOhmAddresses().includes(record.token.toLowerCase()) || getOhmAddresses().includes(record.tokenAddress.toLowerCase()); +} + +/** + * Determines if the given record is sourced from a buyback address. + * + * @param record + * @returns + */ +const isBuybackAddress = (record: TokenRecord): boolean => { + if (!record.block) { + return false; + } + + // If before the inclusion block, ignore + if (Number(record.block) < 20514801) { + return false; + } + + return record.sourceAddress.toLowerCase() == "0xf7deb867e65306be0cb33918ac1b8f89a72109db".toLowerCase(); +} + /** * Calculates the market value or liquid backing for the given records. * @@ -466,6 +495,17 @@ const getTreasuryAssetValue = ( return [previousTotalValue, previousAllRecords, previousChainValues, previousChainRecords]; } + // If it is OHM and liquidBacking is specified, ignore + const isTokenOhm = isOHM(currentRecord); + if (liquidBacking && isTokenOhm) { + return [previousTotalValue, previousAllRecords, previousChainValues, previousChainRecords]; + } + + // If it is OHM and not in the buyback addresses, ignore + if (isTokenOhm && !isBuybackAddress(currentRecord)) { + return [previousTotalValue, previousAllRecords, previousChainValues, previousChainRecords]; + } + const currentValue: number = liquidBacking ? +currentRecord.valueExcludingOhm : +currentRecord.value; const newTotalValue: number = previousTotalValue + currentValue; diff --git a/apps/server/.wundergraph/wundergraph.config.ts b/apps/server/.wundergraph/wundergraph.config.ts index 75f8466..ed3159d 100644 --- a/apps/server/.wundergraph/wundergraph.config.ts +++ b/apps/server/.wundergraph/wundergraph.config.ts @@ -28,7 +28,7 @@ const resolveSubgraphUrl = (url: string): string => { const treasuryEthereum = introspect.graphql({ apiNamespace: "treasuryEthereum", - url: resolveSubgraphUrl("https://gateway-arbitrum.network.thegraph.com/api/[api-key]/deployments/id/Qmd7wgnNTijSpV1JDM3yUE8Exy4EGG1jw3yyQKdTKvYiig"), // 5.1.3 + url: resolveSubgraphUrl("https://gateway-arbitrum.network.thegraph.com/api/[api-key]/deployments/id/Qmam2fnfYzj6srEGC49XxsFyMwngs7xKwjGWZc7jnEU97h"), // 5.2.6 schemaExtension: schemaExtension, }); diff --git a/apps/server/CHANGELOG.md b/apps/server/CHANGELOG.md new file mode 100644 index 0000000..6be80ba --- /dev/null +++ b/apps/server/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +## [v1.4.0] + +- Amend treasury market value to include in market value calculations the value of OHM (and variants) in protocol buyback addresses diff --git a/apps/server/package.json b/apps/server/package.json index dd81f14..e132136 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -1,6 +1,6 @@ { "name": "@olympusdao/treasury-subgraph", - "version": "1.3.0", + "version": "1.4.0", "engines": { "node": ">= 18.0.0" }, @@ -34,6 +34,6 @@ "pretest:ci": "yarn build:local", "test": "jest", "test:ci": "WG_LOG_LEVEL=error jest --runInBand --ci", - "test:local": "dotenv -e ../../.env jest" + "test:local": "jest -e ../../.env" } -} +} \ No newline at end of file diff --git a/apps/server/tests/metrics.test.ts b/apps/server/tests/metrics.test.ts index 44996ca..60576f2 100644 --- a/apps/server/tests/metrics.test.ts +++ b/apps/server/tests/metrics.test.ts @@ -1,13 +1,16 @@ import { addDays } from "date-fns"; import { createTestServer } from "../.wundergraph/generated/testing"; import { getISO8601DateString } from "./dateHelper"; -import { CHAIN_ARBITRUM, CHAIN_ETHEREUM, CHAIN_FANTOM, CHAIN_POLYGON, TOKEN_SUPPLY_TYPE_BONDS_DEPOSITS, TOKEN_SUPPLY_TYPE_BONDS_PREMINTED, TOKEN_SUPPLY_TYPE_BONDS_VESTING_DEPOSITS, TOKEN_SUPPLY_TYPE_BOOSTED_LIQUIDITY_VAULT, TOKEN_SUPPLY_TYPE_LENDING, TOKEN_SUPPLY_TYPE_LIQUIDITY, TOKEN_SUPPLY_TYPE_OFFSET, TOKEN_SUPPLY_TYPE_TOTAL_SUPPLY, TOKEN_SUPPLY_TYPE_TREASURY } from "../.wundergraph/constants"; +import { CHAIN_ARBITRUM, CHAIN_BASE, CHAIN_ETHEREUM, CHAIN_FANTOM, CHAIN_POLYGON, TOKEN_SUPPLY_TYPE_BONDS_DEPOSITS, TOKEN_SUPPLY_TYPE_BONDS_PREMINTED, TOKEN_SUPPLY_TYPE_BONDS_VESTING_DEPOSITS, TOKEN_SUPPLY_TYPE_BOOSTED_LIQUIDITY_VAULT, TOKEN_SUPPLY_TYPE_LENDING, TOKEN_SUPPLY_TYPE_LIQUIDITY, TOKEN_SUPPLY_TYPE_OFFSET, TOKEN_SUPPLY_TYPE_TOTAL_SUPPLY, TOKEN_SUPPLY_TYPE_TREASURY } from "../.wundergraph/constants"; import { getSupplyBalanceForTypes } from "./metricsHelper"; import { TokenRecord, filterReduce, filter as filterTokenRecords, getFirstRecord as getFirstTokenRecord } from "./tokenRecordHelper"; import { TokenSupply, filter as filterTokenSupplies } from "./tokenSupplyHelper"; import { ProtocolMetric } from "./protocolMetricHelper"; import { parseNumber } from "./numberHelper"; +const BUYBACK_MS = "0xf7deb867e65306be0cb33918ac1b8f89a72109db".toLowerCase(); +const DAO_WALLET = "0x245cc372c84b3645bf0ffe6538620b04a217988b".toLowerCase(); + const wg = createTestServer(); beforeAll(async () => { @@ -19,7 +22,7 @@ afterAll(async () => { }); beforeEach(async () => { - // + // }); const getStartDate = (days: number = -5): string => { @@ -70,7 +73,7 @@ describe("paginated", () => { expect(recordsNotNull[0].date).toEqual(getISO8601DateString(new Date())); // Last date expect(recordsNotNull[recordsNotNull.length - 1].date).toEqual(startDateString); - }); + }, 60 * 1000); test("subsequent results are equal", async () => { const result = await wg.client().query({ @@ -111,7 +114,7 @@ describe("paginated", () => { }); expect(resultTwo.data).toEqual(records); - }, 30 * 1000); + }, 90 * 1000); test("crossChainDataComplete true", async () => { const result = await wg.client().query({ @@ -165,7 +168,7 @@ describe("paginated", () => { expect(treasuryLiquidBackingRecords?.Arbitrum.length).toBeGreaterThan(0); expect(treasuryLiquidBackingRecords?.Ethereum.length).toBeGreaterThan(0); - }); + }, 60 * 1000); test("includeRecords false", async () => { const result = await wg.client().query({ @@ -208,6 +211,9 @@ describe("latest", () => { const arbitrumRawResult = rawResult.data?.treasuryArbitrum_tokenRecords[0]; const arbitrumRawBlock: number = parseNumber(arbitrumRawResult?.block); const arbitrumRawTimestamp: number = parseNumber(arbitrumRawResult?.timestamp); + const baseRawResult = rawResult.data?.treasuryBase_tokenRecords[0]; + const baseRawBlock: number = parseNumber(baseRawResult?.block); + const baseRawTimestamp: number = parseNumber(baseRawResult?.timestamp); const ethereumRawResult = rawResult.data?.treasuryEthereum_tokenRecords[0]; const ethereumRawBlock: number = parseNumber(ethereumRawResult?.block); const ethereumRawTimestamp: number = parseNumber(ethereumRawResult?.timestamp); @@ -230,12 +236,14 @@ describe("latest", () => { // Check that the block is the same expect(record?.blocks.Arbitrum).toEqual(arbitrumRawBlock); + expect(record?.blocks.Base).toEqual(baseRawBlock); expect(record?.blocks.Ethereum).toEqual(ethereumRawBlock); expect(record?.blocks.Fantom).toEqual(fantomRawBlock); expect(record?.blocks.Polygon).toEqual(polygonRawBlock); // Check that the timestamp is the same expect(record?.timestamps.Arbitrum).toEqual(arbitrumRawTimestamp); + expect(record?.timestamps.Base).toEqual(baseRawTimestamp); expect(record?.timestamps.Ethereum).toEqual(ethereumRawTimestamp); expect(record?.timestamps.Fantom).toEqual(fantomRawTimestamp); expect(record?.timestamps.Polygon).toEqual(polygonRawTimestamp); @@ -267,6 +275,9 @@ describe("earliest", () => { const arbitrumRawResult = rawResult.data?.treasuryArbitrum_tokenRecords[0]; const arbitrumRawBlock: number = parseNumber(arbitrumRawResult?.block); const arbitrumRawTimestamp: number = parseNumber(arbitrumRawResult?.timestamp); + const baseRawResult = rawResult.data?.treasuryBase_tokenRecords[0]; + const baseRawBlock: number = parseNumber(baseRawResult?.block); + const baseRawTimestamp: number = parseNumber(baseRawResult?.timestamp); const ethereumRawResult = rawResult.data?.treasuryEthereum_tokenRecords[0]; const ethereumRawBlock: number = parseNumber(ethereumRawResult?.block); const ethereumRawTimestamp: number = parseNumber(ethereumRawResult?.timestamp); @@ -289,12 +300,14 @@ describe("earliest", () => { // Check that the block is the same expect(record?.blocks.Arbitrum).toEqual(arbitrumRawBlock); + expect(record?.blocks.Base).toEqual(baseRawBlock); expect(record?.blocks.Ethereum).toEqual(ethereumRawBlock); expect(record?.blocks.Fantom).toEqual(fantomRawBlock); expect(record?.blocks.Polygon).toEqual(polygonRawBlock); // Check that the timestamp is the same expect(record?.timestamps.Arbitrum).toEqual(arbitrumRawTimestamp); + expect(record?.timestamps.Base).toEqual(baseRawTimestamp); expect(record?.timestamps.Ethereum).toEqual(ethereumRawTimestamp); expect(record?.timestamps.Fantom).toEqual(fantomRawTimestamp); expect(record?.timestamps.Polygon).toEqual(polygonRawTimestamp); @@ -331,6 +344,9 @@ describe("atBlock", () => { const arbitrumRawResult = getFirstTokenRecord(rawResult.data, CHAIN_ARBITRUM, startDate); const arbitrumRawBlock: number = parseNumber(arbitrumRawResult?.block); const arbitrumRawTimestamp: number = parseNumber(arbitrumRawResult?.timestamp); + const baseRawResult = getFirstTokenRecord(rawResult.data, CHAIN_BASE, startDate); + const baseRawBlock: number = parseNumber(baseRawResult?.block); + const baseRawTimestamp: number = parseNumber(baseRawResult?.timestamp); const ethereumRawResult = getFirstTokenRecord(rawResult.data, CHAIN_ETHEREUM, startDate); const ethereumRawBlock: number = parseNumber(ethereumRawResult?.block); const ethereumRawTimestamp: number = parseNumber(ethereumRawResult?.timestamp); @@ -346,6 +362,7 @@ describe("atBlock", () => { operationName: "atBlock/metrics", input: { arbitrumBlock: arbitrumRawBlock, + baseBlock: baseRawBlock, ethereumBlock: ethereumRawBlock, fantomBlock: fantomRawBlock, polygonBlock: polygonRawBlock, @@ -359,12 +376,14 @@ describe("atBlock", () => { // Check that the block is the same expect(record?.blocks.Arbitrum).toEqual(arbitrumRawBlock); + expect(record?.blocks.Base).toEqual(baseRawBlock); expect(record?.blocks.Ethereum).toEqual(ethereumRawBlock); expect(record?.blocks.Fantom).toEqual(fantomRawBlock); expect(record?.blocks.Polygon).toEqual(polygonRawBlock); // Check that the timestamp is the same expect(record?.timestamps.Arbitrum).toEqual(arbitrumRawTimestamp); + expect(record?.timestamps.Base).toEqual(baseRawTimestamp); expect(record?.timestamps.Ethereum).toEqual(ethereumRawTimestamp); expect(record?.timestamps.Fantom).toEqual(fantomRawTimestamp); expect(record?.timestamps.Polygon).toEqual(polygonRawTimestamp); @@ -410,12 +429,14 @@ describe("metrics", () => { // Raw data has an array property for each chain const arbitrumTokenRecords = filterTokenRecords(combinedTokenRecords, CHAIN_ARBITRUM); + const baseTokenRecords = filterTokenRecords(combinedTokenRecords, CHAIN_BASE); const ethereumTokenRecords = filterTokenRecords(combinedTokenRecords, CHAIN_ETHEREUM); const fantomTokenRecords = filterTokenRecords(combinedTokenRecords, CHAIN_FANTOM); const polygonTokenRecords = filterTokenRecords(combinedTokenRecords, CHAIN_POLYGON); // Raw data has an array property for each chain const arbitrumTokenSupplies = filterTokenSupplies(combinedTokenSupplies, CHAIN_ARBITRUM); + const baseTokenSupplies = filterTokenSupplies(combinedTokenSupplies, CHAIN_BASE); const ethereumTokenSupplies = filterTokenSupplies(combinedTokenSupplies, CHAIN_ETHEREUM); const fantomTokenSupplies = filterTokenSupplies(combinedTokenSupplies, CHAIN_FANTOM); const polygonTokenSupplies = filterTokenSupplies(combinedTokenSupplies, CHAIN_POLYGON); @@ -428,6 +449,7 @@ describe("metrics", () => { const includedTypes = [TOKEN_SUPPLY_TYPE_TOTAL_SUPPLY]; const expectedSupply = getSupplyBalanceForTypes(combinedTokenSupplies, includedTypes, ohmIndex)[0]; const expectedArbitrumSupply = getSupplyBalanceForTypes(arbitrumTokenSupplies, includedTypes, ohmIndex)[0]; + const expectedBaseSupply = getSupplyBalanceForTypes(baseTokenSupplies, includedTypes, ohmIndex)[0]; const expectedEthereumSupply = getSupplyBalanceForTypes(ethereumTokenSupplies, includedTypes, ohmIndex)[0]; const expectedFantomSupply = getSupplyBalanceForTypes(fantomTokenSupplies, includedTypes, ohmIndex)[0]; const expectedPolygonSupply = getSupplyBalanceForTypes(polygonTokenSupplies, includedTypes, ohmIndex)[0]; @@ -447,6 +469,7 @@ describe("metrics", () => { expect(record).not.toBeNull(); expect(record?.ohmTotalSupply).toBeCloseTo(expectedSupply); expect(record?.ohmTotalSupplyComponents.Arbitrum).toBeCloseTo(expectedArbitrumSupply); + expect(record?.ohmTotalSupplyComponents.Base).toBeCloseTo(expectedBaseSupply); expect(record?.ohmTotalSupplyComponents.Ethereum).toBeCloseTo(expectedEthereumSupply); expect(record?.ohmTotalSupplyComponents.Fantom).toBeCloseTo(expectedFantomSupply); expect(record?.ohmTotalSupplyComponents.Polygon).toBeCloseTo(expectedPolygonSupply); @@ -457,12 +480,14 @@ describe("metrics", () => { // Raw data has an array property for each chain const arbitrumTokenRecords = filterTokenRecords(combinedTokenRecords, CHAIN_ARBITRUM); + const baseTokenRecords = filterTokenRecords(combinedTokenRecords, CHAIN_BASE); const ethereumTokenRecords = filterTokenRecords(combinedTokenRecords, CHAIN_ETHEREUM); const fantomTokenRecords = filterTokenRecords(combinedTokenRecords, CHAIN_FANTOM); const polygonTokenRecords = filterTokenRecords(combinedTokenRecords, CHAIN_POLYGON); // Raw data has an array property for each chain const arbitrumTokenSupplies = filterTokenSupplies(combinedTokenSupplies, CHAIN_ARBITRUM); + const baseTokenSupplies = filterTokenSupplies(combinedTokenSupplies, CHAIN_BASE); const ethereumTokenSupplies = filterTokenSupplies(combinedTokenSupplies, CHAIN_ETHEREUM); const fantomTokenSupplies = filterTokenSupplies(combinedTokenSupplies, CHAIN_FANTOM); const polygonTokenSupplies = filterTokenSupplies(combinedTokenSupplies, CHAIN_POLYGON); @@ -483,6 +508,7 @@ describe("metrics", () => { ]; const expectedSupply = getSupplyBalanceForTypes(combinedTokenSupplies, includedTypes, ohmIndex)[0]; const expectedArbitrumSupply = getSupplyBalanceForTypes(arbitrumTokenSupplies, includedTypes, ohmIndex)[0]; + const expectedBaseSupply = getSupplyBalanceForTypes(baseTokenSupplies, includedTypes, ohmIndex)[0]; const expectedEthereumSupply = getSupplyBalanceForTypes(ethereumTokenSupplies, includedTypes, ohmIndex)[0]; const expectedFantomSupply = getSupplyBalanceForTypes(fantomTokenSupplies, includedTypes, ohmIndex)[0]; const expectedPolygonSupply = getSupplyBalanceForTypes(polygonTokenSupplies, includedTypes, ohmIndex)[0]; @@ -502,6 +528,7 @@ describe("metrics", () => { expect(record).not.toBeNull(); expect(record?.ohmCirculatingSupply).toBeCloseTo(expectedSupply); expect(record?.ohmCirculatingSupplyComponents.Arbitrum).toBeCloseTo(expectedArbitrumSupply); + expect(record?.ohmCirculatingSupplyComponents.Base).toBeCloseTo(expectedBaseSupply); expect(record?.ohmCirculatingSupplyComponents.Ethereum).toBeCloseTo(expectedEthereumSupply); expect(record?.ohmCirculatingSupplyComponents.Fantom).toBeCloseTo(expectedFantomSupply); expect(record?.ohmCirculatingSupplyComponents.Polygon).toBeCloseTo(expectedPolygonSupply); @@ -512,12 +539,14 @@ describe("metrics", () => { // Raw data has an array property for each chain const arbitrumTokenRecords = filterTokenRecords(combinedTokenRecords, CHAIN_ARBITRUM); + const baseTokenRecords = filterTokenRecords(combinedTokenRecords, CHAIN_BASE); const ethereumTokenRecords = filterTokenRecords(combinedTokenRecords, CHAIN_ETHEREUM); const fantomTokenRecords = filterTokenRecords(combinedTokenRecords, CHAIN_FANTOM); const polygonTokenRecords = filterTokenRecords(combinedTokenRecords, CHAIN_POLYGON); // Raw data has an array property for each chain const arbitrumTokenSupplies = filterTokenSupplies(combinedTokenSupplies, CHAIN_ARBITRUM); + const baseTokenSupplies = filterTokenSupplies(combinedTokenSupplies, CHAIN_BASE); const ethereumTokenSupplies = filterTokenSupplies(combinedTokenSupplies, CHAIN_ETHEREUM); const fantomTokenSupplies = filterTokenSupplies(combinedTokenSupplies, CHAIN_FANTOM); const polygonTokenSupplies = filterTokenSupplies(combinedTokenSupplies, CHAIN_POLYGON); @@ -539,6 +568,7 @@ describe("metrics", () => { ]; const expectedSupply = getSupplyBalanceForTypes(combinedTokenSupplies, includedTypes, ohmIndex)[0]; const expectedArbitrumSupply = getSupplyBalanceForTypes(arbitrumTokenSupplies, includedTypes, ohmIndex)[0]; + const expectedBaseSupply = getSupplyBalanceForTypes(baseTokenSupplies, includedTypes, ohmIndex)[0]; const expectedEthereumSupply = getSupplyBalanceForTypes(ethereumTokenSupplies, includedTypes, ohmIndex)[0]; const expectedFantomSupply = getSupplyBalanceForTypes(fantomTokenSupplies, includedTypes, ohmIndex)[0]; const expectedPolygonSupply = getSupplyBalanceForTypes(polygonTokenSupplies, includedTypes, ohmIndex)[0]; @@ -558,6 +588,7 @@ describe("metrics", () => { expect(record).not.toBeNull(); expect(record?.ohmFloatingSupply).toBeCloseTo(expectedSupply); expect(record?.ohmFloatingSupplyComponents.Arbitrum).toBeCloseTo(expectedArbitrumSupply); + expect(record?.ohmFloatingSupplyComponents.Base).toBeCloseTo(expectedBaseSupply); expect(record?.ohmFloatingSupplyComponents.Ethereum).toBeCloseTo(expectedEthereumSupply); expect(record?.ohmFloatingSupplyComponents.Fantom).toBeCloseTo(expectedFantomSupply); expect(record?.ohmFloatingSupplyComponents.Polygon).toBeCloseTo(expectedPolygonSupply); @@ -568,12 +599,14 @@ describe("metrics", () => { // Raw data has an array property for each chain const arbitrumTokenRecords = filterTokenRecords(combinedTokenRecords, CHAIN_ARBITRUM); + const baseTokenRecords = filterTokenRecords(combinedTokenRecords, CHAIN_BASE); const ethereumTokenRecords = filterTokenRecords(combinedTokenRecords, CHAIN_ETHEREUM); const fantomTokenRecords = filterTokenRecords(combinedTokenRecords, CHAIN_FANTOM); const polygonTokenRecords = filterTokenRecords(combinedTokenRecords, CHAIN_POLYGON); // Raw data has an array property for each chain const arbitrumTokenSupplies = filterTokenSupplies(combinedTokenSupplies, CHAIN_ARBITRUM); + const baseTokenSupplies = filterTokenSupplies(combinedTokenSupplies, CHAIN_BASE); const ethereumTokenSupplies = filterTokenSupplies(combinedTokenSupplies, CHAIN_ETHEREUM); const fantomTokenSupplies = filterTokenSupplies(combinedTokenSupplies, CHAIN_FANTOM); const polygonTokenSupplies = filterTokenSupplies(combinedTokenSupplies, CHAIN_POLYGON); @@ -597,6 +630,7 @@ describe("metrics", () => { ]; const expectedSupply = getSupplyBalanceForTypes(combinedTokenSupplies, includedTypes, ohmIndex)[0]; const expectedArbitrumSupply = getSupplyBalanceForTypes(arbitrumTokenSupplies, includedTypes, ohmIndex)[0]; + const expectedBaseSupply = getSupplyBalanceForTypes(baseTokenSupplies, includedTypes, ohmIndex)[0]; const expectedEthereumSupply = getSupplyBalanceForTypes(ethereumTokenSupplies, includedTypes, ohmIndex)[0]; const expectedFantomSupply = getSupplyBalanceForTypes(fantomTokenSupplies, includedTypes, ohmIndex)[0]; const expectedPolygonSupply = getSupplyBalanceForTypes(polygonTokenSupplies, includedTypes, ohmIndex)[0]; @@ -616,6 +650,7 @@ describe("metrics", () => { expect(record).not.toBeNull(); expect(record?.ohmBackedSupply).toBeCloseTo(expectedSupply); expect(record?.ohmBackedSupplyComponents.Arbitrum).toBeCloseTo(expectedArbitrumSupply); + expect(record?.ohmBackedSupplyComponents.Base).toBeCloseTo(expectedBaseSupply); expect(record?.ohmBackedSupplyComponents.Ethereum).toBeCloseTo(expectedEthereumSupply); expect(record?.ohmBackedSupplyComponents.Fantom).toBeCloseTo(expectedFantomSupply); expect(record?.ohmBackedSupplyComponents.Polygon).toBeCloseTo(expectedPolygonSupply); @@ -644,12 +679,14 @@ describe("metrics", () => { // Raw data has an array property for each chain const arbitrumTokenRecords = filterTokenRecords(combinedTokenRecords, CHAIN_ARBITRUM); + const baseTokenRecords = filterTokenRecords(combinedTokenRecords, CHAIN_BASE); const ethereumTokenRecords = filterTokenRecords(combinedTokenRecords, CHAIN_ETHEREUM); const fantomTokenRecords = filterTokenRecords(combinedTokenRecords, CHAIN_FANTOM); const polygonTokenRecords = filterTokenRecords(combinedTokenRecords, CHAIN_POLYGON); const marketValue = filterReduce(combinedTokenRecords, () => true); const marketValueArbitrum = filterReduce(arbitrumTokenRecords, () => true); + const marketValueBase = filterReduce(baseTokenRecords, () => true); const marketValueEthereum = filterReduce(ethereumTokenRecords, () => true); const marketValueFantom = filterReduce(fantomTokenRecords, () => true); const marketValuePolygon = filterReduce(polygonTokenRecords, () => true); @@ -659,6 +696,7 @@ describe("metrics", () => { operationName: "paginated/metrics", input: { startDate: START_DATE, + includeRecords: true, }, }); @@ -669,22 +707,59 @@ describe("metrics", () => { expect(record).not.toBeNull(); expect(record?.treasuryMarketValue).toBeCloseTo(marketValue); expect(record?.treasuryMarketValueComponents.Arbitrum).toBeCloseTo(marketValueArbitrum); + expect(record?.treasuryMarketValueComponents.Base).toBeCloseTo(marketValueBase); expect(record?.treasuryMarketValueComponents.Ethereum).toBeCloseTo(marketValueEthereum); expect(record?.treasuryMarketValueComponents.Fantom).toBeCloseTo(marketValueFantom); expect(record?.treasuryMarketValueComponents.Polygon).toBeCloseTo(marketValuePolygon); - }); + + // Ensure that it excludes OHM in treasury addresses + expect(record?.treasuryMarketValueRecords?.Ethereum.filter( + (record) => record.tokenAddress.includes("OHM") && record.sourceAddress.toLowerCase() == DAO_WALLET + ).length).toEqual(0); + + const isOHM = (value: TokenRecord): boolean => { + if (value.token == "OHM V1") { + return true; + } + + if (value.token == "OHM V2") { + return true; + } + + if (value.token == "gOHM") { + return true; + } + + return false; + } + + + // Market value should not include the value of any raw OHM + const marketValueExcludeOhm = filterReduce(combinedTokenRecords, (value) => !isOHM(value), false); + // Only OHM in the buyback address should be included + const buybackOhmValue = filterReduce(combinedTokenRecords, (value) => isOHM(value) && value.sourceAddress.toLowerCase() == BUYBACK_MS, false); + + expect(record?.treasuryMarketValue).toEqual(marketValueExcludeOhm + buybackOhmValue); + + const buybackOhmRecords = record?.treasuryMarketValueRecords?.Ethereum.filter( + (record) => isOHM(record) && record.sourceAddress.toLowerCase() == BUYBACK_MS + ) || []; + expect(buybackOhmRecords.length).toBeGreaterThan(0); + }, 30 * 1000); test("treasury liquid backing is accurate", async () => { const [combinedTokenRecords, combinedTokenSupplies, combinedProtocolMetrics] = await getRecords(START_DATE); // Raw data has an array property for each chain const arbitrumTokenRecords = filterTokenRecords(combinedTokenRecords, CHAIN_ARBITRUM); + const baseTokenRecords = filterTokenRecords(combinedTokenRecords, CHAIN_BASE); const ethereumTokenRecords = filterTokenRecords(combinedTokenRecords, CHAIN_ETHEREUM); const fantomTokenRecords = filterTokenRecords(combinedTokenRecords, CHAIN_FANTOM); const polygonTokenRecords = filterTokenRecords(combinedTokenRecords, CHAIN_POLYGON); const liquidBackingValue = filterReduce(combinedTokenRecords, (value) => value.isLiquid == true, true); const liquidBackingValueArbitrum = filterReduce(arbitrumTokenRecords, (value) => value.isLiquid == true, true); + const liquidBackingValueBase = filterReduce(baseTokenRecords, (value) => value.isLiquid == true, true); const liquidBackingValueEthereum = filterReduce(ethereumTokenRecords, (value) => value.isLiquid == true, true); const liquidBackingValueFantom = filterReduce(fantomTokenRecords, (value) => value.isLiquid == true, true); const liquidBackingValuePolygon = filterReduce(polygonTokenRecords, (value) => value.isLiquid == true, true); @@ -694,6 +769,7 @@ describe("metrics", () => { operationName: "paginated/metrics", input: { startDate: START_DATE, + includeRecords: true, }, }); @@ -704,9 +780,20 @@ describe("metrics", () => { expect(record).not.toBeNull(); expect(record?.treasuryLiquidBacking).toBeCloseTo(liquidBackingValue); expect(record?.treasuryLiquidBackingComponents.Arbitrum).toBeCloseTo(liquidBackingValueArbitrum); + expect(record?.treasuryLiquidBackingComponents.Base).toBeCloseTo(liquidBackingValueBase); expect(record?.treasuryLiquidBackingComponents.Ethereum).toBeCloseTo(liquidBackingValueEthereum); expect(record?.treasuryLiquidBackingComponents.Fantom).toBeCloseTo(liquidBackingValueFantom); expect(record?.treasuryLiquidBackingComponents.Polygon).toBeCloseTo(liquidBackingValuePolygon); + + // Ensure that it excludes OHM in treasury addresses + expect(record?.treasuryMarketValueRecords?.Ethereum.filter( + (record) => record.tokenAddress.includes("OHM") && record.sourceAddress.toLowerCase() == DAO_WALLET + ).length).toEqual(0); + + // Ensure that it excludes OHM in the buyback addresses + expect(record?.treasuryMarketValueRecords?.Ethereum.filter( + (record) => record.tokenAddress.includes("OHM") && record.sourceAddress.toLowerCase() == BUYBACK_MS + ).length).toEqual(0); }); test("OHM index", async () => { diff --git a/apps/server/tests/metricsHelper.ts b/apps/server/tests/metricsHelper.ts index 024daef..5aef9b4 100644 --- a/apps/server/tests/metricsHelper.ts +++ b/apps/server/tests/metricsHelper.ts @@ -5,6 +5,7 @@ type TokenSupply = TokenSuppliesResponseData["treasuryEthereum_tokenSupplies"][0 const OHM_ADDRESSES: string[] = [ "0x64aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d5".toLowerCase(), // Mainnet "0xf0cb2dc0db5e6c66B9a70Ac27B06b878da017028".toLowerCase(), // Arbitrum + "0x060cb087a9730e13aa191f31a6d86bff8dfcdcc0".toLowerCase(), // Base ]; const GOHM_ADDRESSES: string[] = [ diff --git a/apps/server/tests/tokenRecordHelper.test.ts b/apps/server/tests/tokenRecordHelper.test.ts index 4a4c1d5..b5aa6b9 100644 --- a/apps/server/tests/tokenRecordHelper.test.ts +++ b/apps/server/tests/tokenRecordHelper.test.ts @@ -61,6 +61,7 @@ describe("filterCompleteRecords", () => { getSampleRecord("2", "2021-01-02", 2), getSampleRecord("1", "2021-01-01", 1), ], + treasuryBase_tokenRecords: [], treasuryEthereum_tokenRecords: [ getSampleRecord("3", "2021-01-03", 3), getSampleRecord("2", "2021-01-02", 2), @@ -90,6 +91,7 @@ describe("filterCompleteRecords", () => { getSampleRecord("2", "2021-01-02", 2), getSampleRecord("1", "2021-01-01", 1), ], + treasuryBase_tokenRecords: [], treasuryEthereum_tokenRecords: [ getSampleRecord("2", "2021-01-02", 2), getSampleRecord("1", "2021-01-01", 1), @@ -118,6 +120,7 @@ describe("filterCompleteRecords", () => { getSampleRecord("2", "2021-01-02", 2), getSampleRecord("1", "2021-01-01", 1), ], + treasuryBase_tokenRecords: [], treasuryEthereum_tokenRecords: [], treasuryFantom_tokenRecords: [], treasuryPolygon_tokenRecords: [] @@ -137,6 +140,7 @@ describe("filterCompleteRecords", () => { getSampleRecord("2", "2021-01-02", 2), getSampleRecord("1", "2021-01-01", 1), ], + treasuryBase_tokenRecords: [], treasuryFantom_tokenRecords: [], treasuryPolygon_tokenRecords: [] }; diff --git a/apps/server/tests/tokenRecords.test.ts b/apps/server/tests/tokenRecords.test.ts index 2c435b1..a9d33d1 100644 --- a/apps/server/tests/tokenRecords.test.ts +++ b/apps/server/tests/tokenRecords.test.ts @@ -1,7 +1,7 @@ import { addDays } from "date-fns"; import { createTestServer } from "../.wundergraph/generated/testing"; import { getISO8601DateString } from "./dateHelper"; -import { CHAIN_ARBITRUM, CHAIN_ETHEREUM, CHAIN_FANTOM, CHAIN_POLYGON } from "../.wundergraph/constants"; +import { CHAIN_ARBITRUM, CHAIN_BASE, CHAIN_ETHEREUM, CHAIN_FANTOM, CHAIN_POLYGON } from "../.wundergraph/constants"; import { getFirstRecord } from "./tokenRecordHelper"; import { parseNumber } from "./numberHelper"; @@ -294,6 +294,8 @@ describe("atBlock", () => { // Raw data has an array property for each chain const arbitrumRawResult = getFirstRecord(rawResult.data, CHAIN_ARBITRUM, startDate); const arbitrumRawBlock = parseNumber(arbitrumRawResult?.block); + const baseRawResult = getFirstRecord(rawResult.data, CHAIN_BASE, startDate); + const baseRawBlock = parseNumber(baseRawResult?.block); const ethereumRawResult = getFirstRecord(rawResult.data, CHAIN_ETHEREUM, startDate); const ethereumRawBlock = parseNumber(ethereumRawResult?.block); const fantomRawResult = getFirstRecord(rawResult.data, CHAIN_FANTOM, startDate); @@ -306,6 +308,7 @@ describe("atBlock", () => { operationName: "atBlock/tokenRecords", input: { arbitrumBlock: arbitrumRawBlock, + baseBlock: baseRawBlock, ethereumBlock: ethereumRawBlock, fantomBlock: fantomRawBlock, polygonBlock: polygonRawBlock, @@ -315,12 +318,14 @@ describe("atBlock", () => { // Latest records is collapsed into a flat array const records = result.data; const arbitrumResult = getFirstRecord(records, CHAIN_ARBITRUM); + const baseResult = getFirstRecord(records, CHAIN_BASE); const ethereumResult = getFirstRecord(records, CHAIN_ETHEREUM); const fantomResult = getFirstRecord(records, CHAIN_FANTOM); const polygonResult = getFirstRecord(records, CHAIN_POLYGON); // Check that the block is the same expect(parseNumber(arbitrumResult?.block)).toEqual(arbitrumRawBlock); + expect(parseNumber(baseResult?.block)).toEqual(baseRawBlock); expect(parseNumber(ethereumResult?.block)).toEqual(ethereumRawBlock); expect(parseNumber(fantomResult?.block)).toEqual(fantomRawBlock); expect(parseNumber(polygonResult?.block)).toEqual(polygonRawBlock); diff --git a/apps/server/tests/tokenSupplies.test.ts b/apps/server/tests/tokenSupplies.test.ts index 9f75559..2564ea2 100644 --- a/apps/server/tests/tokenSupplies.test.ts +++ b/apps/server/tests/tokenSupplies.test.ts @@ -1,7 +1,7 @@ import { addDays } from "date-fns"; import { createTestServer } from "../.wundergraph/generated/testing"; import { getISO8601DateString } from "./dateHelper"; -import { CHAIN_ARBITRUM, CHAIN_ETHEREUM, CHAIN_FANTOM, CHAIN_POLYGON } from "../.wundergraph/constants"; +import { CHAIN_ARBITRUM, CHAIN_BASE, CHAIN_ETHEREUM, CHAIN_FANTOM, CHAIN_POLYGON } from "../.wundergraph/constants"; import { getFirstRecord } from "./tokenSupplyHelper"; import { parseNumber } from "./numberHelper"; @@ -186,12 +186,13 @@ describe("latest", () => { // Raw data has an array property for each chain const arbitrumRawResult = rawResult.data?.treasuryArbitrum_tokenSupplies[0]; + const baseRawResult = rawResult.data?.treasuryBase_tokenSupplies[0]; const ethereumRawResult = rawResult.data?.treasuryEthereum_tokenSupplies[0]; const fantomRawResult = rawResult.data?.treasuryFantom_tokenSupplies[0]; const polygonRawResult = rawResult.data?.treasuryPolygon_tokenSupplies[0]; // Calculate the expected count based on how many of the raw results were defined. This is because there may not be TokenSupply records on every chain. - const expectedCount = [arbitrumRawResult, ethereumRawResult, fantomRawResult, polygonRawResult].filter((result) => result !== undefined).length; + const expectedCount = [arbitrumRawResult, baseRawResult, ethereumRawResult, fantomRawResult, polygonRawResult].filter((result) => result !== undefined).length; // Grab the results from the latest operation const result = await wg.client().query({ @@ -201,12 +202,14 @@ describe("latest", () => { // Latest records is collapsed into a flat array const records = result.data; const arbitrumResult = getFirstRecord(records, CHAIN_ARBITRUM); + const baseResult = getFirstRecord(records, CHAIN_BASE); const ethereumResult = getFirstRecord(records, CHAIN_ETHEREUM); const fantomResult = getFirstRecord(records, CHAIN_FANTOM); const polygonResult = getFirstRecord(records, CHAIN_POLYGON); // Check that the block is the same expect(arbitrumResult?.block).toEqual(arbitrumRawResult?.block); + expect(baseResult?.block).toEqual(baseRawResult?.block); expect(ethereumResult?.block).toEqual(ethereumRawResult?.block); expect(fantomResult?.block).toEqual(fantomRawResult?.block); expect(polygonResult?.block).toEqual(polygonRawResult?.block); @@ -240,12 +243,13 @@ describe("earliest", () => { // Raw data has an array property for each chain const arbitrumRawResult = rawResult.data?.treasuryArbitrum_tokenSupplies[0]; + const baseRawResult = rawResult.data?.treasuryBase_tokenSupplies[0]; const ethereumRawResult = rawResult.data?.treasuryEthereum_tokenSupplies[0]; const fantomRawResult = rawResult.data?.treasuryFantom_tokenSupplies[0]; const polygonRawResult = rawResult.data?.treasuryPolygon_tokenSupplies[0]; // Calculate the expected count based on how many of the raw results were defined. This is because there may not be TokenSupply records on every chain. - const expectedCount = [arbitrumRawResult, ethereumRawResult, fantomRawResult, polygonRawResult].filter((result) => result !== undefined).length; + const expectedCount = [arbitrumRawResult, baseRawResult, ethereumRawResult, fantomRawResult, polygonRawResult].filter((result) => result !== undefined).length; // Grab the results from the earliest operation const result = await wg.client().query({ @@ -255,12 +259,14 @@ describe("earliest", () => { // Latest records is collapsed into a flat array const records = result.data; const arbitrumResult = getFirstRecord(records, CHAIN_ARBITRUM); + const baseResult = getFirstRecord(records, CHAIN_BASE); const ethereumResult = getFirstRecord(records, CHAIN_ETHEREUM); const fantomResult = getFirstRecord(records, CHAIN_FANTOM); const polygonResult = getFirstRecord(records, CHAIN_POLYGON); // Check that the block is the same expect(arbitrumResult?.block).toEqual(arbitrumRawResult?.block); + expect(baseResult?.block).toEqual(baseRawResult?.block); expect(ethereumResult?.block).toEqual(ethereumRawResult?.block); expect(fantomResult?.block).toEqual(fantomRawResult?.block); expect(polygonResult?.block).toEqual(polygonRawResult?.block); @@ -299,6 +305,7 @@ describe("atBlock", () => { // Raw data has an array property for each chain const arbitrumRawBlock = getFirstRecord(rawResult.data, CHAIN_ARBITRUM, startDate)?.block; + const baseRawBlock = getFirstRecord(rawResult.data, CHAIN_BASE, startDate)?.block; const ethereumRawBlock = getFirstRecord(rawResult.data, CHAIN_ETHEREUM, startDate)?.block; const fantomRawBlock = getFirstRecord(rawResult.data, CHAIN_FANTOM, startDate)?.block; const polygonRawBlock = getFirstRecord(rawResult.data, CHAIN_POLYGON, startDate)?.block; @@ -308,6 +315,7 @@ describe("atBlock", () => { operationName: "atBlock/tokenSupplies", input: { arbitrumBlock: parseNumber(arbitrumRawBlock), + baseBlock: parseNumber(baseRawBlock), ethereumBlock: parseNumber(ethereumRawBlock), fantomBlock: parseNumber(fantomRawBlock), polygonBlock: parseNumber(polygonRawBlock), @@ -317,12 +325,14 @@ describe("atBlock", () => { // Latest records is collapsed into a flat array const records = result.data; const arbitrumResult = getFirstRecord(records, CHAIN_ARBITRUM); + const baseResult = getFirstRecord(records, CHAIN_BASE); const ethereumResult = getFirstRecord(records, CHAIN_ETHEREUM); const fantomResult = getFirstRecord(records, CHAIN_FANTOM); const polygonResult = getFirstRecord(records, CHAIN_POLYGON); // Check that the block is the same expect(arbitrumResult?.block).toEqual(arbitrumRawBlock); + expect(baseResult?.block).toEqual(baseRawBlock); expect(ethereumResult?.block).toEqual(ethereumRawBlock); expect(fantomResult?.block).toEqual(fantomRawBlock); expect(polygonResult?.block).toEqual(polygonRawBlock); diff --git a/apps/server/tests/tokenSupplyHelper.test.ts b/apps/server/tests/tokenSupplyHelper.test.ts index 7646509..6902ba1 100644 --- a/apps/server/tests/tokenSupplyHelper.test.ts +++ b/apps/server/tests/tokenSupplyHelper.test.ts @@ -56,6 +56,7 @@ describe("filterCompleteRecords", () => { getSampleRecord("2", "2021-01-02", 2), getSampleRecord("1", "2021-01-01", 1), ], + treasuryBase_tokenSupplies: [], treasuryEthereum_tokenSupplies: [ getSampleRecord("3", "2021-01-03", 3), getSampleRecord("2", "2021-01-02", 2), @@ -85,6 +86,7 @@ describe("filterCompleteRecords", () => { getSampleRecord("2", "2021-01-02", 2), getSampleRecord("1", "2021-01-01", 1), ], + treasuryBase_tokenSupplies: [], treasuryEthereum_tokenSupplies: [ getSampleRecord("2", "2021-01-02", 2), getSampleRecord("1", "2021-01-01", 1), @@ -113,6 +115,7 @@ describe("filterCompleteRecords", () => { getSampleRecord("2", "2021-01-02", 2), getSampleRecord("1", "2021-01-01", 1), ], + treasuryBase_tokenSupplies: [], treasuryEthereum_tokenSupplies: [], treasuryFantom_tokenSupplies: [], treasuryPolygon_tokenSupplies: [] @@ -132,6 +135,7 @@ describe("filterCompleteRecords", () => { getSampleRecord("2", "2021-01-02", 2), getSampleRecord("1", "2021-01-01", 1), ], + treasuryBase_tokenSupplies: [], treasuryFantom_tokenSupplies: [], treasuryPolygon_tokenSupplies: [] };