diff --git a/.github/workflows/query.yml b/.github/workflows/query.yml index c36f50b1..b0b11341 100644 --- a/.github/workflows/query.yml +++ b/.github/workflows/query.yml @@ -54,7 +54,7 @@ jobs: - name: Setup Node uses: actions/setup-node@v3 with: - node-version: "18" + node-version: "20" cache: "yarn" - name: Install run: | @@ -91,7 +91,7 @@ jobs: - name: Setup Node uses: actions/setup-node@v3 with: - node-version: "18" + node-version: "20" cache: "yarn" # Looks for an existing comment, so it can be updated - name: Find Existing Comment diff --git a/subgraphs/ethereum/CHANGELOG.md b/subgraphs/ethereum/CHANGELOG.md index a634287d..5b97444d 100644 --- a/subgraphs/ethereum/CHANGELOG.md +++ b/subgraphs/ethereum/CHANGELOG.md @@ -1,5 +1,10 @@ # Subgraph Changelog +## 5.6.4 (2025-04-16) + +- Adds support for Guardian Olympus sUSDS Vault +- Exclude balances from the Convex Allocator going forward (bricked) + ## 5.5.4 (2025-02-18) - Adds support for indexing native ETH diff --git a/subgraphs/ethereum/config.json b/subgraphs/ethereum/config.json index cd4e406c..f630e5f7 100644 --- a/subgraphs/ethereum/config.json +++ b/subgraphs/ethereum/config.json @@ -1,6 +1,6 @@ { - "id": "QmebupLuGiu5wcS7P9BYpKCFsAFEjFR3dybb3xELqttSv5", + "id": "QmdeAd2f79EHqki57gBcd8jbNK2xGyaYCHSV4YK4vouPCe", "org": "olympusdao", "name": "olympus-protocol-metrics", - "version": "5.5.4" + "version": "5.6.4" } diff --git a/subgraphs/ethereum/src/utils/Constants.ts b/subgraphs/ethereum/src/utils/Constants.ts index 796dea82..01732b74 100644 --- a/subgraphs/ethereum/src/utils/Constants.ts +++ b/subgraphs/ethereum/src/utils/Constants.ts @@ -316,6 +316,7 @@ ERC20_TOKENS.set(NATIVE_ETH, new TokenDefinition(NATIVE_ETH, TokenCategoryVolati export const ERC4626_SDAI = "0x83F20F44975D03b1b09e64809B757c47f942BEeA".toLowerCase(); export const ERC4626_SUSDS = "0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD".toLowerCase(); +export const ERC4626_GAUNTLET_SUSDS_VAULT = "0x3365184e87d2Bd75961780454A5810BEc956F0dD".toLowerCase(); /** * Mapping between the contract address of an ERC4626 token and the TokenDefinition. @@ -325,6 +326,7 @@ export const ERC4626_SUSDS = "0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD".toLowe export const ERC4626_TOKENS = new Map(); ERC4626_TOKENS.set(ERC4626_SDAI, new TokenDefinition(ERC4626_SDAI, TokenCategoryStable, true, false)); ERC4626_TOKENS.set(ERC4626_SUSDS, new TokenDefinition(ERC4626_SUSDS, TokenCategoryStable, true, false)); +ERC4626_TOKENS.set(ERC4626_GAUNTLET_SUSDS_VAULT, new TokenDefinition(ERC4626_GAUNTLET_SUSDS_VAULT, TokenCategoryStable, true, false)); /** * Mapping between the non-staked token and the token staked in Convex. @@ -571,6 +573,7 @@ LIQUIDITY_POOL_TOKEN_LOOKUP.set(ERC4626_SDAI, [new PairHandler(PairHandlerTypes. LIQUIDITY_POOL_TOKEN_LOOKUP.set(ERC20_SUSHI, [new PairHandler(PairHandlerTypes.UniswapV2, PAIR_UNISWAP_V2_SUSHI_ETH)]); LIQUIDITY_POOL_TOKEN_LOOKUP.set(ERC20_STETH, [new PairHandler(PairHandlerTypes.Curve, PAIR_CURVE_ETH_STETH)]); LIQUIDITY_POOL_TOKEN_LOOKUP.set(ERC4626_SUSDS, [new PairHandler(PairHandlerTypes.ERC4626, ERC4626_SUSDS)]); +LIQUIDITY_POOL_TOKEN_LOOKUP.set(ERC4626_GAUNTLET_SUSDS_VAULT, [new PairHandler(PairHandlerTypes.ERC4626, ERC4626_GAUNTLET_SUSDS_VAULT)]); LIQUIDITY_POOL_TOKEN_LOOKUP.set(ERC20_SYN, [new PairHandler(PairHandlerTypes.UniswapV2, PAIR_UNISWAP_V2_SYN_FRAX)]); LIQUIDITY_POOL_TOKEN_LOOKUP.set(ERC20_THOR, [new PairHandler(PairHandlerTypes.UniswapV2, PAIR_UNISWAP_V2_THOR_ETH)]); LIQUIDITY_POOL_TOKEN_LOOKUP.set(ERC20_TOKE, [new PairHandler(PairHandlerTypes.UniswapV2, PAIR_UNISWAP_V2_TOKE_ETH)]); @@ -1048,6 +1051,7 @@ CONTRACT_NAME_MAP.set(ERC20_WSTETH, "wstETH"); CONTRACT_NAME_MAP.set(ERC20_XSUSHI, "SUSHI - Staked"); CONTRACT_NAME_MAP.set(ERC4626_SDAI, "Savings DAI"); CONTRACT_NAME_MAP.set(ERC4626_SUSDS, "Savings USDS"); +CONTRACT_NAME_MAP.set(ERC4626_GAUNTLET_SUSDS_VAULT, "Gauntlet sUSDS Vault"); CONTRACT_NAME_MAP.set(ETHBOND_CONTRACT1, "ETH Bond 1"); CONTRACT_NAME_MAP.set(EULER_ADDRESS, "Euler Finance"); CONTRACT_NAME_MAP.set(EULER_ADDRESS, "Euler Protocol"); diff --git a/subgraphs/ethereum/src/utils/ContractHelper.ts b/subgraphs/ethereum/src/utils/ContractHelper.ts index c8dbd59b..041d17e8 100644 --- a/subgraphs/ethereum/src/utils/ContractHelper.ts +++ b/subgraphs/ethereum/src/utils/ContractHelper.ts @@ -73,7 +73,7 @@ import { TOKE_STAKING, } from "./Constants"; import { getUSDRate } from "./Price"; -import { CONVEX_ALLOCATORS, getWalletAddressesForContract } from "./ProtocolAddresses"; +import { getConvexAllocators, getWalletAddressesForContract } from "./ProtocolAddresses"; /** * The Graph recommends only binding a contract once @@ -1767,7 +1767,7 @@ export function getConvexStakedRecords( const records: TokenRecord[] = []; // Loop through allocators - const convexAllocators = CONVEX_ALLOCATORS; + const convexAllocators = getConvexAllocators(blockNumber); for (let i = 0; i < convexAllocators.length; i++) { const allocatorAddress = convexAllocators[i]; diff --git a/subgraphs/ethereum/src/utils/Price.ts b/subgraphs/ethereum/src/utils/Price.ts index e373f4c9..da26cd25 100644 --- a/subgraphs/ethereum/src/utils/Price.ts +++ b/subgraphs/ethereum/src/utils/Price.ts @@ -96,6 +96,8 @@ function getPairHandlerNonOhmValue( const result = getUniswapV3PairTotalValue(pairHandler.getContract(), true, blockNumber); const totalValue = result.totalValue; const ohmBalance = result.ohmBalance; + log.debug("getPairHandlerNonOhmValue: totalValue {}, ohmBalance {}", [totalValue.toString(), ohmBalance.toString()]); + if (totalValue.equals(BigDecimal.zero())) { return null; } diff --git a/subgraphs/ethereum/src/utils/ProtocolAddresses.ts b/subgraphs/ethereum/src/utils/ProtocolAddresses.ts index d5120bec..b6d76e37 100644 --- a/subgraphs/ethereum/src/utils/ProtocolAddresses.ts +++ b/subgraphs/ethereum/src/utils/ProtocolAddresses.ts @@ -68,6 +68,26 @@ export const CONVEX_ALLOCATORS = [ DAO_WALLET, ]; +export const getConvexAllocators = (blockNumber: BigInt): string[] => { + // If before the exclusion block, return all allocators + if (blockNumber.lt(BigInt.fromString(CONVEX_ALLOCATOR_DEATH))) { + return CONVEX_ALLOCATORS; + } + + // Otherwise remove the bricked allocator + const allocators = CONVEX_ALLOCATORS.slice(0); + for (let i = 0; i < allocators.length; i++) { + if (allocators[i].toLowerCase() == CONVEX_CVX_ALLOCATOR.toLowerCase()) { + log.debug("getConvexAllocators: removing bricked allocator: {}", [CONVEX_CVX_ALLOCATOR]); + allocators.splice(i, 1); + break; + } + } + + // Return the allocators + return allocators; +} + /** * This set of wallet addresses is common across many tokens, * and can be used for balance lookups. @@ -117,6 +137,8 @@ TREASURY_BLACKLIST.set(ERC20_SOHM_V1, PROTOCOL_ADDRESSES); TREASURY_BLACKLIST.set(ERC20_SOHM_V2, PROTOCOL_ADDRESSES); TREASURY_BLACKLIST.set(ERC20_SOHM_V3, PROTOCOL_ADDRESSES); +const CONVEX_ALLOCATOR_DEATH = "22278800"; + /** * Some wallets (e.g. {DAO_WALLET}) have specific treasury assets mixed into them. * For this reason, the wallets to be used differ on a per-contract basis. @@ -148,6 +170,23 @@ export const getWalletAddressesForContract = (contractAddress: string, blockNumb walletAddresses.push(clearinghouseAddresses[i].toHexString().toLowerCase()); } + // If after the exclusion block, remove the convex allocator + // Reason: funds in it are bricked + if (blockNumber.ge(BigInt.fromString(CONVEX_ALLOCATOR_DEATH))) { + for (let i = 0; i < walletAddresses.length; i++) { + // Check address + if (walletAddresses[i].toLowerCase() != CONVEX_CVX_ALLOCATOR.toLowerCase()) continue; + + // Check exclusion block + if (blockNumber.lt(BigInt.fromString(CONVEX_ALLOCATOR_DEATH))) continue; + + // Remove the address in-place + walletAddresses.splice(i, 1); + log.debug("getWalletAddressesForContract: removed convex allocator: {}", [CONVEX_CVX_ALLOCATOR]); + break; + } + } + // If the contract isn't on the blacklist, return as normal if (!TREASURY_BLACKLIST.has(contractAddress.toLowerCase())) { log.debug("getWalletAddressesForContract: token {} is not on treasury blacklist", [contractAddress]); diff --git a/subgraphs/ethereum/subgraph.yaml b/subgraphs/ethereum/subgraph.yaml index b84d01da..890ebd3b 100644 --- a/subgraphs/ethereum/subgraph.yaml +++ b/subgraphs/ethereum/subgraph.yaml @@ -4,8 +4,8 @@ repository: https://github.com/OlympusDAO/olympus-protocol-metrics-subgraph features: - grafting graft: - base: QmdGqRrQD4FehyTYTmoK9RvveuR3e4vPDsyuqAYF4Nrmfv # 5.4.10 - block: 21810000 # Inclusion of native ETH + base: QmebupLuGiu5wcS7P9BYpKCFsAFEjFR3dybb3xELqttSv5 # 5.5.4 + block: 22270000 # Exclusion of Convex Allocator schema: file: ../../schema.graphql dataSources: diff --git a/subgraphs/ethereum/tests/erc4626.test.ts b/subgraphs/ethereum/tests/erc4626.test.ts index d0d73d8b..887102a1 100644 --- a/subgraphs/ethereum/tests/erc4626.test.ts +++ b/subgraphs/ethereum/tests/erc4626.test.ts @@ -3,7 +3,7 @@ import { assert, beforeEach, clearStore, createMockedFunction, describe, log,tes import { toBigInt, toDecimal } from "../../shared/src/utils/Decimals"; import { TREASURY_ADDRESS_V3 } from "../../shared/src/Wallets"; -import { ERC20_USDS, ERC4626_SUSDS } from "../src/utils/Constants"; +import { ERC20_USDS, ERC4626_GAUNTLET_SUSDS_VAULT, ERC4626_SUSDS } from "../src/utils/Constants"; import { getAllERC4626Balances } from "../src/utils/ERC4626"; import { getWalletAddressesForContract } from "../src/utils/ProtocolAddresses"; import { mockClearinghouseRegistryAddressNull, mockTreasuryAddressNull } from "./bophadesHelper"; @@ -51,6 +51,7 @@ const mockERC4626Token = ( const mockERC4626Tokens = (): void => { mockERC4626Token(SDAI, DAI, SDAI_ASSETS_TO_SHARES, 18); mockERC4626Token(ERC4626_SUSDS, ERC20_USDS, SUSDS_ASSETS_TO_SHARES, 18); + mockERC4626Token(ERC4626_GAUNTLET_SUSDS_VAULT, ERC20_USDS, SUSDS_ASSETS_TO_SHARES, 18); }; const mockPriceFeeds = (): void => { @@ -74,6 +75,9 @@ describe("ERC4626", () => { mockZeroWalletBalances( ERC4626_SUSDS, getWalletAddressesForContract(ERC4626_SUSDS, BLOCK_NUMBER)); + mockZeroWalletBalances( + ERC4626_GAUNTLET_SUSDS_VAULT, + getWalletAddressesForContract(ERC4626_GAUNTLET_SUSDS_VAULT, BLOCK_NUMBER)); }); test("handles contract revert", () => { @@ -83,6 +87,7 @@ describe("ERC4626", () => { // ERC4626 contract reverts mockERC4626Reverts(SDAI); mockERC4626Reverts(ERC4626_SUSDS); + mockERC4626Reverts(ERC4626_GAUNTLET_SUSDS_VAULT); // Mock balance mockWalletBalance(SDAI, TREASURY_ADDRESS_V3, toBigInt(BigDecimal.fromString("100"), 18)); diff --git a/subgraphs/ethereum/tests/pairHelper.ts b/subgraphs/ethereum/tests/pairHelper.ts index 048a39f5..2c15d581 100644 --- a/subgraphs/ethereum/tests/pairHelper.ts +++ b/subgraphs/ethereum/tests/pairHelper.ts @@ -58,6 +58,7 @@ import { ERC20_USDC, ERC20_WETH, ERC20_WSTETH, + ERC4626_SUSDS, FRAX_LOCKING_CONTRACTS, getContractName, LQTY_STAKING, @@ -82,6 +83,7 @@ import { PAIR_UNISWAP_V3_LQTY_LUSD, PAIR_UNISWAP_V3_LQTY_WETH, PAIR_UNISWAP_V3_LUSD_USDC, + PAIR_UNISWAP_V3_OHM_SUSDS, PAIR_UNISWAP_V3_WETH_BTRFLY_V1, PAIR_UNISWAP_V3_WETH_BTRFLY_V2, PAIR_UNISWAP_V3_WETH_OHM, @@ -336,6 +338,7 @@ export const mockUniswapV3PairsZero = (): void => { mockRateUniswapV3(PAIR_UNISWAP_V3_WETH_BTRFLY_V1, BigInt.zero(), ERC20_WETH, ERC20_BTRFLY_V1, ERC20_STANDARD_DECIMALS, ERC20_STANDARD_DECIMALS, BigInt.zero(), BigInt.zero(), true); mockRateUniswapV3(PAIR_UNISWAP_V3_WETH_BTRFLY_V2, BigInt.zero(), ERC20_WETH, ERC20_BTRFLY_V2, ERC20_STANDARD_DECIMALS, ERC20_STANDARD_DECIMALS, BigInt.zero(), BigInt.zero(), true); mockRateUniswapV3(PAIR_UNISWAP_V3_WETH_OHM, BigInt.zero(), ERC20_WETH, ERC20_OHM_V2, ERC20_STANDARD_DECIMALS, OHM_V2_DECIMALS, BigInt.zero(), BigInt.zero(), true); + mockRateUniswapV3(PAIR_UNISWAP_V3_OHM_SUSDS, BigInt.zero(), ERC20_OHM_V2, ERC4626_SUSDS, OHM_V2_DECIMALS, ERC20_STANDARD_DECIMALS, BigInt.zero(), BigInt.zero(), true); } export const mockFxsEthRate = (): void => { diff --git a/subgraphs/ethereum/tests/price.test.ts b/subgraphs/ethereum/tests/price.test.ts index 04e20053..bcf54fe6 100644 --- a/subgraphs/ethereum/tests/price.test.ts +++ b/subgraphs/ethereum/tests/price.test.ts @@ -216,6 +216,7 @@ describe("OHM-USD rate", () => { mockERC20Balance(ERC20_WETH, PAIR_UNISWAP_V3_WETH_OHM, toBigInt(ethBalance, ERC20_STANDARD_DECIMALS)); // 919.574080927401380445 * 1898.01397374 / 130454.081369749 = 13.3791479512 + // TODO figure out why this is not being returned const calculatedRate = BigDecimal.fromString("13.3835"); assert.stringEquals(