Skip to content

Commit 41e1128

Browse files
authored
Merge pull request #280 from OlympusDAO/base-pol
Improvements to Base Protocol-Owned Liquidity
2 parents eb902f6 + 773350c commit 41e1128

File tree

9 files changed

+140
-40
lines changed

9 files changed

+140
-40
lines changed

subgraphs/base/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Base Subgraph Changelog
22

3+
## 1.0.9 (2025-01-28)
4+
5+
- Removes tracking of USDS, sUSDS and OHM-sUSDS LP
6+
- Adds tracking of USDC and OHM-USDC LP
7+
- Fixes issues with underlying token balances for Uniswap V3 positions
8+
39
## 1.0.2 (2025-01-10)
410

511
- Adds price handler for ERC4626 tokens

subgraphs/base/config.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"id": "QmYgw9fWpiriYBj6NL8U8952Gsrxfvdfe3hxHE25UJm56A",
2+
"id": "QmWj7CDe7VivLqX49g6nXjni8w3XFokY5Pwiau78xyox9p",
33
"org": "olympusdao",
44
"name": "protocol-metrics-base",
5-
"version": "1.0.2"
5+
"version": "1.0.9"
66
}

subgraphs/base/src/contracts/Constants.ts

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,16 @@ export const BLOCKCHAIN = "Base";
66

77
export const ERC20_OHM = "0x060cb087a9730E13aa191f31A6d86bFF8DfcdCC0".toLowerCase();
88
export const ERC20_WETH = "0x4200000000000000000000000000000000000006".toLowerCase();
9-
export const ERC20_USDS = "0x820C137fa70C8691f0e44Dc420a5e53c168921Dc".toLowerCase();
10-
11-
export const ERC4626_SUSDS = "0x5875eEE11Cf8398102FdAd704C9E96607675467a".toLowerCase();
9+
export const ERC20_USDC = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913".toLowerCase();
1210

1311
export const LP_UNISWAP_V2_OHM_WETH = "0x5ab4b9e96aeed4820e4be267f42411d722985482".toLowerCase();
14-
export const LP_UNISWAP_V3_OHM_SUSDS = "0x8cda2eae542a93ea7496f015c6b908e53adcb41a".toLowerCase();
12+
export const LP_UNISWAP_V3_OHM_USDC = "0x183ea22691c54806FE96555436dd312b6BeFAc2F".toLowerCase();
1513

1614
export const ERC20_TOKENS_BASE = new Map<string, TokenDefinition>();
17-
ERC20_TOKENS_BASE.set(ERC20_USDS, new TokenDefinition(ERC20_USDS, TokenCategoryStable, true, false));
15+
ERC20_TOKENS_BASE.set(ERC20_USDC, new TokenDefinition(ERC20_USDC, TokenCategoryStable, true, false));
1816
ERC20_TOKENS_BASE.set(ERC20_WETH, new TokenDefinition(ERC20_WETH, TokenCategoryVolatile, true, true));
19-
ERC20_TOKENS_BASE.set(ERC4626_SUSDS, new TokenDefinition(ERC4626_SUSDS, TokenCategoryStable, true, false));
2017
ERC20_TOKENS_BASE.set(LP_UNISWAP_V2_OHM_WETH, new TokenDefinition(LP_UNISWAP_V2_OHM_WETH, TokenCategoryPOL, true, false));
21-
ERC20_TOKENS_BASE.set(LP_UNISWAP_V3_OHM_SUSDS, new TokenDefinition(LP_UNISWAP_V3_OHM_SUSDS, TokenCategoryPOL, true, false));
18+
ERC20_TOKENS_BASE.set(LP_UNISWAP_V3_OHM_USDC, new TokenDefinition(LP_UNISWAP_V3_OHM_USDC, TokenCategoryPOL, true, false));
2219

2320
export const OHM_TOKENS = [ERC20_OHM];
2421

@@ -73,14 +70,12 @@ export const getWalletAddressesForContract = (contractAddress: string): string[]
7370
export const CONTRACT_NAME_MAP = new Map<string, string>();
7471
CONTRACT_NAME_MAP.set(DAO_MULTISIG, "DAO MS (Base)");
7572
CONTRACT_NAME_MAP.set(ERC20_OHM, "OHM");
76-
CONTRACT_NAME_MAP.set(ERC20_USDS, "USDS");
73+
CONTRACT_NAME_MAP.set(ERC20_USDC, "USDC");
7774
CONTRACT_NAME_MAP.set(ERC20_WETH, "Wrapped Ether");
78-
CONTRACT_NAME_MAP.set(ERC4626_SUSDS, "Savings USDS");
7975
CONTRACT_NAME_MAP.set(LP_UNISWAP_V2_OHM_WETH, "Uniswap V2 OHM-wETH LP");
80-
CONTRACT_NAME_MAP.set(LP_UNISWAP_V3_OHM_SUSDS, "Uniswap V3 OHM-sUSDS LP");
76+
CONTRACT_NAME_MAP.set(LP_UNISWAP_V3_OHM_USDC, "Uniswap V3 OHM-USDC LP");
8177

8278
export const CONTRACT_ABBREVIATION_MAP = new Map<string, string>();
8379
CONTRACT_ABBREVIATION_MAP.set(ERC20_OHM, "OHM");
84-
CONTRACT_ABBREVIATION_MAP.set(ERC20_USDS, "USDS");
80+
CONTRACT_ABBREVIATION_MAP.set(ERC20_USDC, "USDC");
8581
CONTRACT_ABBREVIATION_MAP.set(ERC20_WETH, "wETH");
86-
CONTRACT_ABBREVIATION_MAP.set(ERC4626_SUSDS, "sUSDS");

subgraphs/base/src/price/PriceChainlink.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import { Address, BigDecimal } from "@graphprotocol/graph-ts";
22

3-
import { ERC20_USDS } from "../../../ethereum/src/utils/Constants";
43
import { toDecimal } from "../../../shared/src/utils/Decimals";
54
import { ChainlinkPriceFeed } from "../../generated/TokenRecords-base/ChainlinkPriceFeed";
6-
import { ERC20_WETH } from "../contracts/Constants";
5+
import { ERC20_USDC,ERC20_WETH } from "../contracts/Constants";
76

87
const tokenPriceFeedMap: Map<string, string> = new Map<string, string>();
98
tokenPriceFeedMap.set(ERC20_WETH, "0x71041dddad3595F9CEd3DcCFBe3D1F4b0a16Bb70".toLowerCase());
10-
tokenPriceFeedMap.set(ERC20_USDS, "0x591e79239a7d679378eC8c847e5038150364C78F".toLowerCase()); // DAI feed, 1:1 interchangeable with USDS
9+
tokenPriceFeedMap.set(ERC20_USDC, "0x7e860098F58bBFC8648a4311b374B1D669a2bc6B".toLowerCase());
1110

1211
export function getPriceFeedTokens(): string[] {
1312
return tokenPriceFeedMap.keys();

subgraphs/base/src/price/PriceLookup.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,23 @@
11
import { Address, BigDecimal, BigInt, log } from "@graphprotocol/graph-ts";
22

33
import { PriceHandler, PriceLookupResult } from "../../../shared/src/price/PriceHandler";
4-
import { PriceHandlerERC4626 } from "../../../shared/src/price/PriceHandlerERC4626";
54
import { PriceHandlerUniswapV2 } from "../../../shared/src/price/PriceHandlerUniswapV2";
65
import { PriceHandlerUniswapV3 } from "../../../shared/src/price/PriceHandlerUniswapV3";
76
import { getUSDRate } from "../../../shared/src/price/PriceRouter";
87
import {
98
ERC20_OHM,
10-
ERC20_USDS,
9+
ERC20_USDC,
1110
ERC20_WETH,
12-
ERC4626_SUSDS,
1311
LP_UNISWAP_V2_OHM_WETH,
14-
LP_UNISWAP_V3_OHM_SUSDS,
12+
LP_UNISWAP_V3_OHM_USDC,
1513
UNISWAP_V3_POSITION_MANAGER
1614
} from "../contracts/Constants";
1715
import { getContractName } from "../contracts/Contracts";
1816
import { getBaseTokenRate, isBaseToken } from "./PriceBase";
1917

2018
export const PRICE_HANDLERS: PriceHandler[] = [
2119
new PriceHandlerUniswapV2([ERC20_OHM, ERC20_WETH], LP_UNISWAP_V2_OHM_WETH, getContractName),
22-
new PriceHandlerUniswapV3([ERC20_OHM, ERC4626_SUSDS], LP_UNISWAP_V3_OHM_SUSDS, UNISWAP_V3_POSITION_MANAGER, getContractName),
23-
new PriceHandlerERC4626(ERC4626_SUSDS, ERC20_USDS, getContractName),
20+
new PriceHandlerUniswapV3([ERC20_OHM, ERC20_USDC], LP_UNISWAP_V3_OHM_USDC, UNISWAP_V3_POSITION_MANAGER, getContractName),
2421
];
2522

2623
/**
@@ -70,10 +67,10 @@ export function getPrice(tokenAddress: string, block: BigInt): BigDecimal {
7067
const priceResult = getPriceRecursive(tokenAddress, block, null);
7168

7269
if (priceResult === null) {
73-
log.warning("Unable to determine price for token {} ({}) at block {}", [getContractName(tokenAddress), tokenAddress, block.toString()]);
70+
log.warning("{}: Unable to determine price for token {} ({}) at block {}", [FUNC, getContractName(tokenAddress), tokenAddress, block.toString()]);
7471
return BigDecimal.zero();
7572
}
7673

77-
log.debug("Price for {} ({}) at block {} was: {}", [getContractName(tokenAddress), tokenAddress, block.toString(), priceResult.price.toString()]);
74+
log.debug("{}: Price for {} ({}) at block {} was: {}", [FUNC, getContractName(tokenAddress), tokenAddress, block.toString(), priceResult.price.toString()]);
7875
return priceResult.price;
7976
}

subgraphs/base/subgraph.yaml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
specVersion: 0.0.8
22
description: Olympus Protocol Metrics Subgraph - Base
33
repository: https://github.com/OlympusDAO/olympus-protocol-metrics-subgraph
4+
features:
5+
- grafting
6+
graft:
7+
base: QmYgw9fWpiriYBj6NL8U8952Gsrxfvdfe3hxHE25UJm56A # 1.0.2
8+
block: 25311259 # POL updates
49
schema:
510
file: ../../schema.graphql
611
dataSources:
@@ -21,8 +26,6 @@ dataSources:
2126
abis:
2227
- name: ERC20
2328
file: ../shared/abis/ERC20.json
24-
- name: ERC4626
25-
file: ../shared/abis/ERC4626.json
2629
# Price Lookup
2730
- name: UniswapV2Pair
2831
file: ../shared/abis/UniswapV2Pair.json

subgraphs/shared/src/price/PriceHandlerERC4626.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@ export class PriceHandlerERC4626 implements PriceHandler {
2828
const FUNCTION = `${CLASS}: getVault:`;
2929
const vault = ERC4626.bind(Address.fromString(this.vaultAddress));
3030

31-
if (vault === null || vault.try_asset().reverted) {
32-
log.debug("{} contract ({}) reverted at block {}", [
31+
if (vault === null || vault.try_decimals().reverted) {
32+
log.debug("{} contract {} ({}) reverted at block {}", [
3333
FUNCTION,
3434
this.contractLookup(this.vaultAddress),
35+
this.vaultAddress,
3536
block.toString(),
3637
]);
3738
return null;

subgraphs/shared/src/price/PriceHandlerUniswapV3.ts

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -274,15 +274,17 @@ export class PriceHandlerUniswapV3 implements PriceHandler {
274274
return null;
275275
}
276276

277+
// The tick calculations are based on the following: https://ethereum.stackexchange.com/a/140264
278+
277279
// Ticks
278280
const tickLower = position.getTickLower();
279281
log.debug("getPairBalances: positionId: {}, tickLower: {}", [positionId.toString(), tickLower.toString()]);
280-
const sqrtRatioA: BigInt = this.getSqrtRatioAtTick(tickLower);
282+
const sqrtRatioA: BigDecimal = BigDecimal.fromString(Math.sqrt(1.0001 ** tickLower).toString());
281283
log.debug("getPairBalances: positionId: {}, sqrtRatioA: {}", [positionId.toString(), sqrtRatioA.toString()]);
282284

283285
const tickUpper = position.getTickUpper();
284286
log.debug("getPairBalances: positionId: {}, tickUpper: {}", [positionId.toString(), tickUpper.toString()]);
285-
const sqrtRatioB: BigInt = this.getSqrtRatioAtTick(tickUpper);
287+
const sqrtRatioB: BigDecimal = BigDecimal.fromString(Math.sqrt(1.0001 ** tickUpper).toString());
286288
log.debug("getPairBalances: positionId: {}, sqrtRatioB: {}", [positionId.toString(), sqrtRatioB.toString()]);
287289

288290
// If a position has no liquidity, we don't want to record details
@@ -293,17 +295,28 @@ export class PriceHandlerUniswapV3 implements PriceHandler {
293295
}
294296
log.debug("getPairBalances: positionId: {}, liquidity: {}", [positionId.toString(), liquidity.toString()]);
295297

296-
const sqrtPrice: BigInt = sqrtPriceX96.div(Q96);
298+
const sqrtPrice: BigDecimal = sqrtPriceX96.toBigDecimal().div(Q96.toBigDecimal());
297299
log.debug("getPairBalances: positionId: {}, sqrtPrice: {}", [positionId.toString(), sqrtPrice.toString()]);
298300

299-
const token0Amount: BigInt = this.getToken0Amount(liquidity, sqrtPrice, sqrtRatioA, sqrtRatioB);
300-
const token1Amount: BigInt = this.getToken1Amount(liquidity, sqrtPrice, sqrtRatioA, sqrtRatioB);
301+
let token0Amount: BigDecimal = BigDecimal.zero();
302+
let token1Amount: BigDecimal = BigDecimal.zero();
303+
304+
if (currentTick <= tickLower) {
305+
token0Amount = liquidity.toBigDecimal().times(((sqrtRatioB.minus(sqrtRatioA)).div(sqrtRatioA.times(sqrtRatioB))));
306+
}
307+
else if (currentTick >= tickUpper) {
308+
token1Amount = liquidity.toBigDecimal().times((sqrtRatioB.minus(sqrtRatioA)));
309+
}
310+
else if (currentTick > tickLower && currentTick < tickUpper) {
311+
token0Amount = liquidity.toBigDecimal().times(((sqrtRatioB.minus(sqrtPrice)).div(sqrtPrice.times(sqrtRatioB))));
312+
token1Amount = liquidity.toBigDecimal().times(((sqrtPrice.minus(sqrtRatioA))));
313+
}
301314

302-
const token0Decimals = getDecimals(token0.toHexString(), block);
303-
const token1Decimals = getDecimals(token1.toHexString(), block);
315+
const token0Decimals: i32 = i32(getDecimals(token0.toHexString(), block));
316+
const token1Decimals: i32 = i32(getDecimals(token1.toHexString(), block));
304317

305-
const token0Balance = toDecimal(token0Amount, token0Decimals);
306-
const token1Balance = toDecimal(token1Amount, token1Decimals);
318+
const token0Balance = token0Amount.div(BigInt.fromI32(10).pow(u8(token0Decimals)).toBigDecimal());
319+
const token1Balance = token1Amount.div(BigInt.fromI32(10).pow(u8(token1Decimals)).toBigDecimal());
307320
log.debug("getPairBalances: positionId: {}, token0Balance: {}, token1Balance: {}", [positionId.toString(), token0Balance.toString(), token1Balance.toString()]);
308321

309322
return [token0Balance, token1Balance];

subgraphs/shared/tests/price/PriceHandlerUniswapV3.test.ts

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ const WALLET_ADDRESS = "0x18a390bD45bCc92652b9A91AD51Aed7f1c1358f5".toLowerCase(
8181

8282
const ERC20_OHM = "0x64aa3364F17a4D01c6f1751Fd97C2BD3D7e7f1D5".toLowerCase();
8383
const ERC20_WETH = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2".toLowerCase();
84+
const ERC20_USDC = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913".toLowerCase();
8485

8586
const OHM_WETH_POOL = "0x88051b0eea095007d3bef21ab287be961f3d8598".toLowerCase();
8687
const OHM_WETH_SQRTPRICEX96 = BigInt.fromString("198259033222864761237442349019430");
@@ -90,6 +91,14 @@ const OHM_WETH_POSITION_TICK_LOWER: i32 = -887220;
9091
const OHM_WETH_POSITION_TICK_UPPER: i32 = 887220;
9192
const OHM_WETH_POSITION_LIQUIDITY = BigInt.fromString("346355586036686019");
9293

94+
const OHM_USDC_POOL = "0x183ea22691c54806FE96555436dd312b6BeFAc2F".toLowerCase();
95+
const OHM_USDC_SQRTPRICEX96 = BigInt.fromString("11823183971744406029263508776");
96+
const OHM_USDC_TICK: i32 = -38048;
97+
const OHM_USDC_POSITION_ID = BigInt.fromString("1872809");
98+
const OHM_USDC_POSITION_TICK_LOWER: i32 = -44200;
99+
const OHM_USDC_POSITION_TICK_UPPER: i32 = 887220;
100+
const OHM_USDC_POSITION_LIQUIDITY = BigInt.fromString("11264485942092");
101+
93102
export const mockFpisFraxPair = (): void => {
94103
mockRateUniswapV3(
95104
LP_UNISWAP_V3_FPIS_FRAX,
@@ -118,6 +127,20 @@ export const mockOhmWethPair = (): void => {
118127
);
119128
};
120129

130+
export const mockOhmUsdcPair = (): void => {
131+
mockRateUniswapV3(
132+
OHM_USDC_POOL,
133+
OHM_USDC_SQRTPRICEX96,
134+
OHM_USDC_TICK,
135+
ERC20_OHM,
136+
ERC20_USDC,
137+
9,
138+
6,
139+
toBigInt(BigDecimal.fromString("75504.35396532")),
140+
toBigInt(BigDecimal.fromString("445506.63314")),
141+
);
142+
};
143+
121144
export const mockOhmWethPosition = (): void => {
122145
// positionManager.balanceOf()
123146
createMockedFunction(
@@ -160,6 +183,48 @@ export const mockOhmWethPosition = (): void => {
160183
]);
161184
};
162185

186+
export const mockOhmUsdcPosition = (): void => {
187+
// positionManager.balanceOf()
188+
createMockedFunction(
189+
Address.fromString(UNISWAP_V3_POSITION_MANAGER),
190+
"balanceOf",
191+
"balanceOf(address):(uint256)",
192+
)
193+
.withArgs([ethereum.Value.fromAddress(Address.fromString(WALLET_ADDRESS))])
194+
.returns([ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(1))]);
195+
196+
// positionManager.tokenOfOwnerByIndex()
197+
createMockedFunction(
198+
Address.fromString(UNISWAP_V3_POSITION_MANAGER),
199+
"tokenOfOwnerByIndex",
200+
"tokenOfOwnerByIndex(address,uint256):(uint256)",
201+
)
202+
.withArgs([ethereum.Value.fromAddress(Address.fromString(WALLET_ADDRESS)), ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(0))])
203+
.returns([ethereum.Value.fromUnsignedBigInt(OHM_USDC_POSITION_ID)]);
204+
205+
// positionManager.positions()
206+
createMockedFunction(
207+
Address.fromString(UNISWAP_V3_POSITION_MANAGER),
208+
"positions",
209+
"positions(uint256):(uint96,address,address,address,uint24,int24,int24,uint128,uint256,uint256,uint128,uint128)",
210+
)
211+
.withArgs([ethereum.Value.fromUnsignedBigInt(OHM_USDC_POSITION_ID)])
212+
.returns([
213+
ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(0)), // nonce
214+
ethereum.Value.fromAddress(Address.zero()), // operator
215+
ethereum.Value.fromAddress(Address.fromString(ERC20_OHM)), // token0
216+
ethereum.Value.fromAddress(Address.fromString(ERC20_USDC)), // token1
217+
ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(10000)), // fee
218+
ethereum.Value.fromI32(OHM_USDC_POSITION_TICK_LOWER), // tickLower
219+
ethereum.Value.fromI32(OHM_USDC_POSITION_TICK_UPPER), // tickUpper
220+
ethereum.Value.fromUnsignedBigInt(OHM_USDC_POSITION_LIQUIDITY), // liquidity
221+
ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(0)), // feeGrowthInside0X128
222+
ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(0)), // feeGrowthInside1X128
223+
ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(0)), // tokensOwed0
224+
ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(0)), // tokensOwed1
225+
]);
226+
};
227+
163228
const FPIS_RATE = BigDecimal.fromString("1.13357594386");
164229

165230
describe("getPrice", () => {
@@ -348,7 +413,28 @@ describe("getUnderlyingTokenBalance", () => {
348413
const ohmBalance = handler.getUnderlyingTokenBalance(WALLET_ADDRESS, ERC20_OHM, BLOCK);
349414
const wethBalance = handler.getUnderlyingTokenBalance(WALLET_ADDRESS, ERC20_WETH, BLOCK);
350415

351-
assert.stringEquals("138431.489223295", ohmBalance.toString());
352-
assert.stringEquals("866.581676263788419538", wethBalance.toString());
416+
assert.stringEquals("138410.423", ohmBalance.truncate(4).toString());
417+
assert.stringEquals("866.7135", wethBalance.truncate(4).toString());
418+
});
419+
420+
test("OHM-USDC balances are correct", () => {
421+
const contractLookup: ContractNameLookup = (tokenAddress: string): string => {
422+
if (addressesEqual(tokenAddress, ERC20_USDC)) {
423+
return "USDC";
424+
}
425+
426+
return "OHM";
427+
};
428+
429+
mockOhmUsdcPair();
430+
mockOhmUsdcPosition();
431+
432+
const handler = new PriceHandlerUniswapV3([ERC20_OHM, ERC20_USDC], OHM_USDC_POOL, UNISWAP_V3_POSITION_MANAGER, contractLookup);
433+
434+
const ohmBalance = handler.getUnderlyingTokenBalance(WALLET_ADDRESS, ERC20_OHM, BLOCK);
435+
const usdcBalance = handler.getUnderlyingTokenBalance(WALLET_ADDRESS, ERC20_USDC, BLOCK);
436+
437+
assert.stringEquals("75484.2794", ohmBalance.truncate(4).toString());
438+
assert.stringEquals("445136.3419", usdcBalance.truncate(4).toString());
353439
});
354440
});

0 commit comments

Comments
 (0)