Skip to content

Commit ec3a56b

Browse files
authored
support volumes (#76)
1 parent 9c712fa commit ec3a56b

7 files changed

Lines changed: 248 additions & 28 deletions

File tree

abis/AggregatorHook.json

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,6 +1077,43 @@
10771077
],
10781078
"anonymous": false
10791079
},
1080+
{
1081+
"type": "event",
1082+
"name": "HookSwap",
1083+
"inputs": [
1084+
{
1085+
"name": "poolId",
1086+
"type": "bytes32",
1087+
"indexed": true,
1088+
"internalType": "PoolId"
1089+
},
1090+
{
1091+
"name": "sender",
1092+
"type": "address",
1093+
"indexed": true,
1094+
"internalType": "address"
1095+
},
1096+
{
1097+
"name": "amount0",
1098+
"type": "int256",
1099+
"indexed": false,
1100+
"internalType": "int256"
1101+
},
1102+
{
1103+
"name": "amount1",
1104+
"type": "int256",
1105+
"indexed": false,
1106+
"internalType": "int256"
1107+
},
1108+
{
1109+
"name": "swapFee",
1110+
"type": "uint24",
1111+
"indexed": false,
1112+
"internalType": "uint24"
1113+
}
1114+
],
1115+
"anonymous": false
1116+
},
10801117
{
10811118
"type": "event",
10821119
"name": "TokenJarUpdated",

networks.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,10 @@
229229
"PositionManager": {
230230
"address": "0x3fc79444f8eacc1894775493ff3fa41f1e35ce11",
231231
"startBlock": 6606180
232+
},
233+
"AggregatorHook": {
234+
"address": "0x7169a78a59f136876e724b648fbb339a42f46888",
235+
"startBlock": 6606180
232236
}
233237
}
234238
}

scripts/generate-subgraph.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,29 @@ const contractTemplates = {
111111
],
112112
},
113113
},
114+
AggregatorHook: {
115+
kind: 'ethereum/contract',
116+
mapping: {
117+
kind: 'ethereum/events',
118+
apiVersion: '0.0.7',
119+
language: 'wasm/assemblyscript',
120+
file: './src/mappings/poolManager.mapping.ts',
121+
entities: ['Pool', 'Token'],
122+
abis: [
123+
{ name: 'ERC20', file: './abis/ERC20.json' },
124+
{ name: 'ERC20SymbolBytes', file: './abis/ERC20SymbolBytes.json' },
125+
{ name: 'ERC20NameBytes', file: './abis/ERC20NameBytes.json' },
126+
{ name: 'PoolManager', file: './abis/PoolManager.json' },
127+
{ name: 'AggregatorHook', file: './abis/AggregatorHook.json' },
128+
],
129+
eventHandlers: [
130+
{
131+
event: 'HookSwap(indexed bytes32,indexed address,int256,int256,uint24)',
132+
handler: 'handleHookSwap',
133+
},
134+
],
135+
},
136+
},
114137
}
115138

116139
// Base subgraph configuration
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export { handleModifyLiquidity } from './modifyLiquidity'
22
export { handleInitialize } from './poolManager'
33
export { handleSwap } from './swap'
4+
export { handleHookSwap } from './swap'

src/mappings/swap.ts

Lines changed: 145 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Address, BigDecimal, BigInt, log } from '@graphprotocol/graph-ts'
22

3-
import { AggregatorHook } from '../types/PoolManager/AggregatorHook'
3+
import { AggregatorHook, HookSwap as HookSwapEvent } from '../types/PoolManager/AggregatorHook'
44
import { Swap as SwapEvent } from '../types/PoolManager/PoolManager'
55
import { Bundle, Pool, PoolManager, Swap, Token } from '../types/schema'
66
import { getSubgraphConfig, getUSDStableStableHookAddresses, SubgraphConfig } from '../utils/chains'
@@ -24,6 +24,128 @@ export function handleSwap(event: SwapEvent): void {
2424
handleSwapHelper(event)
2525
}
2626

27+
export function handleHookSwap(event: HookSwapEvent): void {
28+
const usdStableStableHookAddresses = getUSDStableStableHookAddresses()
29+
const bundle = Bundle.load('1')
30+
31+
if (bundle === null) {
32+
return
33+
}
34+
35+
const poolId = event.params.poolId.toHexString()
36+
const pool = Pool.load(poolId)
37+
if (pool === null) {
38+
log.warning('Pool not found for HookSwap: {}', [poolId])
39+
return
40+
}
41+
42+
if (!usdStableStableHookAddresses.includes(pool.hooks.toLowerCase())) {
43+
return
44+
}
45+
46+
const token0 = Token.load(pool.token0)
47+
const token1 = Token.load(pool.token1)
48+
if (token0 === null || token1 === null) {
49+
return
50+
}
51+
52+
const amount0 = convertTokenToDecimal(event.params.amount0, token0.decimals)
53+
const amount1 = convertTokenToDecimal(event.params.amount1, token1.decimals)
54+
const amount0Abs = amount0.lt(ZERO_BD) ? amount0.times(BigDecimal.fromString('-1')) : amount0
55+
const amount1Abs = amount1.lt(ZERO_BD) ? amount1.times(BigDecimal.fromString('-1')) : amount1
56+
57+
const amountTotalUSDTracked = amount0Abs.plus(amount1Abs).div(BigDecimal.fromString('2'))
58+
const amountTotalUSDUntracked = amountTotalUSDTracked
59+
60+
pool.feeTier = BigInt.fromI32(event.params.swapFee)
61+
62+
const feesUSD = amountTotalUSDTracked.times(pool.feeTier.toBigDecimal()).div(BigDecimal.fromString('1000000'))
63+
64+
pool.volumeToken0 = pool.volumeToken0.plus(amount0Abs)
65+
pool.volumeToken1 = pool.volumeToken1.plus(amount1Abs)
66+
pool.volumeUSD = pool.volumeUSD.plus(amountTotalUSDTracked)
67+
pool.untrackedVolumeUSD = pool.untrackedVolumeUSD.plus(amountTotalUSDUntracked)
68+
pool.feesUSD = pool.feesUSD.plus(feesUSD)
69+
70+
token0.volume = token0.volume.plus(amount0Abs)
71+
token0.volumeUSD = token0.volumeUSD.plus(amountTotalUSDTracked)
72+
token0.untrackedVolumeUSD = token0.untrackedVolumeUSD.plus(amountTotalUSDUntracked)
73+
token0.feesUSD = token0.feesUSD.plus(feesUSD)
74+
75+
token1.volume = token1.volume.plus(amount1Abs)
76+
token1.volumeUSD = token1.volumeUSD.plus(amountTotalUSDTracked)
77+
token1.untrackedVolumeUSD = token1.untrackedVolumeUSD.plus(amountTotalUSDUntracked)
78+
token1.feesUSD = token1.feesUSD.plus(feesUSD)
79+
80+
const poolDayData = updatePoolDayData(poolId, event)
81+
const poolHourData = updatePoolHourData(poolId, event)
82+
const token0DayData = updateTokenDayData(token0, event)
83+
const token1DayData = updateTokenDayData(token1, event)
84+
const token0HourData = updateTokenHourData(token0, event)
85+
const token1HourData = updateTokenHourData(token1, event)
86+
87+
// HookSwap volume is internal to the aggregator hook, so we intentionally do not
88+
// update global protocol aggregates (PoolManager / UniswapDayData) here.
89+
90+
poolDayData.volumeUSD = poolDayData.volumeUSD.plus(amountTotalUSDTracked)
91+
poolDayData.volumeToken0 = poolDayData.volumeToken0.plus(amount0Abs)
92+
poolDayData.volumeToken1 = poolDayData.volumeToken1.plus(amount1Abs)
93+
poolDayData.feesUSD = poolDayData.feesUSD.plus(feesUSD)
94+
95+
poolHourData.volumeUSD = poolHourData.volumeUSD.plus(amountTotalUSDTracked)
96+
poolHourData.volumeToken0 = poolHourData.volumeToken0.plus(amount0Abs)
97+
poolHourData.volumeToken1 = poolHourData.volumeToken1.plus(amount1Abs)
98+
poolHourData.feesUSD = poolHourData.feesUSD.plus(feesUSD)
99+
100+
token0DayData.volume = token0DayData.volume.plus(amount0Abs)
101+
token0DayData.volumeUSD = token0DayData.volumeUSD.plus(amountTotalUSDTracked)
102+
token0DayData.untrackedVolumeUSD = token0DayData.untrackedVolumeUSD.plus(amountTotalUSDTracked)
103+
token0DayData.feesUSD = token0DayData.feesUSD.plus(feesUSD)
104+
105+
token0HourData.volume = token0HourData.volume.plus(amount0Abs)
106+
token0HourData.volumeUSD = token0HourData.volumeUSD.plus(amountTotalUSDTracked)
107+
token0HourData.untrackedVolumeUSD = token0HourData.untrackedVolumeUSD.plus(amountTotalUSDTracked)
108+
token0HourData.feesUSD = token0HourData.feesUSD.plus(feesUSD)
109+
110+
token1DayData.volume = token1DayData.volume.plus(amount1Abs)
111+
token1DayData.volumeUSD = token1DayData.volumeUSD.plus(amountTotalUSDTracked)
112+
token1DayData.untrackedVolumeUSD = token1DayData.untrackedVolumeUSD.plus(amountTotalUSDTracked)
113+
token1DayData.feesUSD = token1DayData.feesUSD.plus(feesUSD)
114+
115+
token1HourData.volume = token1HourData.volume.plus(amount1Abs)
116+
token1HourData.volumeUSD = token1HourData.volumeUSD.plus(amountTotalUSDTracked)
117+
token1HourData.untrackedVolumeUSD = token1HourData.untrackedVolumeUSD.plus(amountTotalUSDTracked)
118+
token1HourData.feesUSD = token1HourData.feesUSD.plus(feesUSD)
119+
120+
const transaction = loadTransaction(event)
121+
// Persist HookSwap as Swap. Fields not emitted by HookSwap are set to zero.
122+
const swap = new Swap(transaction.id + '-' + event.logIndex.toString())
123+
swap.transaction = transaction.id
124+
swap.timestamp = transaction.timestamp
125+
swap.pool = pool.id
126+
swap.token0 = pool.token0
127+
swap.token1 = pool.token1
128+
swap.sender = event.params.sender
129+
swap.origin = event.transaction.from
130+
swap.amount0 = amount0
131+
swap.amount1 = amount1
132+
swap.amountUSD = amountTotalUSDTracked
133+
swap.tick = BigInt.zero()
134+
swap.sqrtPriceX96 = BigInt.zero()
135+
swap.logIndex = event.logIndex
136+
137+
pool.save()
138+
token0.save()
139+
token1.save()
140+
swap.save()
141+
poolDayData.save()
142+
poolHourData.save()
143+
token0DayData.save()
144+
token1DayData.save()
145+
token0HourData.save()
146+
token1HourData.save()
147+
}
148+
27149
export function handleSwapHelper(event: SwapEvent, subgraphConfig: SubgraphConfig = getSubgraphConfig()): void {
28150
const poolManagerAddress = subgraphConfig.poolManagerAddress
29151
const stablecoinWrappedNativePoolId = subgraphConfig.stablecoinWrappedNativePoolId
@@ -56,7 +178,7 @@ export function handleSwapHelper(event: SwapEvent, subgraphConfig: SubgraphConfi
56178

57179
// Update the pool feeTier with the fee from the swap event
58180
// This is important for dynamic fee pools where we want to keep store the actual last fee rather storing the dynamic flag (8388608)
59-
pool.feeTier = BigInt.fromI32(event.params.fee as i32)
181+
pool.feeTier = BigInt.fromI32(event.params.fee)
60182

61183
// need absolute amounts for volume
62184
let amount0Abs = amount0
@@ -117,7 +239,7 @@ export function handleSwapHelper(event: SwapEvent, subgraphConfig: SubgraphConfi
117239
// Update the pool with the new active liquidity, price, and tick.
118240
pool.liquidity = event.params.liquidity
119241
pool.sqrtPrice = event.params.sqrtPriceX96
120-
pool.tick = BigInt.fromI32(event.params.tick as i32)
242+
pool.tick = BigInt.fromI32(event.params.tick)
121243
if (!isUSDStableStableHookPool) {
122244
pool.totalValueLockedToken0 = pool.totalValueLockedToken0.plus(amount0)
123245
pool.totalValueLockedToken1 = pool.totalValueLockedToken1.plus(amount1)
@@ -183,23 +305,6 @@ export function handleSwapHelper(event: SwapEvent, subgraphConfig: SubgraphConfi
183305
token0.totalValueLockedUSD = token0.totalValueLocked.times(token0.derivedETH).times(bundle.ethPriceUSD)
184306
token1.totalValueLockedUSD = token1.totalValueLocked.times(token1.derivedETH).times(bundle.ethPriceUSD)
185307

186-
// create Swap event
187-
const transaction = loadTransaction(event)
188-
const swap = new Swap(transaction.id + '-' + event.logIndex.toString())
189-
swap.transaction = transaction.id
190-
swap.timestamp = transaction.timestamp
191-
swap.pool = pool.id
192-
swap.token0 = pool.token0
193-
swap.token1 = pool.token1
194-
swap.sender = event.params.sender
195-
swap.origin = event.transaction.from
196-
swap.amount0 = amount0
197-
swap.amount1 = amount1
198-
swap.amountUSD = amountTotalUSDTracked
199-
swap.tick = BigInt.fromI32(event.params.tick as i32)
200-
swap.sqrtPriceX96 = event.params.sqrtPriceX96
201-
swap.logIndex = event.logIndex
202-
203308
// interval data
204309
const uniswapDayData = updateUniswapDayData(event, poolManagerAddress)
205310
const poolDayData = updatePoolDayData(event.params.id.toHexString(), event)
@@ -244,14 +349,33 @@ export function handleSwapHelper(event: SwapEvent, subgraphConfig: SubgraphConfi
244349
token1HourData.untrackedVolumeUSD = token1HourData.untrackedVolumeUSD.plus(amountTotalUSDTracked)
245350
token1HourData.feesUSD = token1HourData.feesUSD.plus(feesUSD)
246351

247-
swap.save()
248352
token0DayData.save()
249353
token1DayData.save()
250354
uniswapDayData.save()
251355
poolDayData.save()
252356
poolHourData.save()
253357
token0HourData.save()
254358
token1HourData.save()
359+
if (!isUSDStableStableHookPool) {
360+
// For aggregator hook pools, HookSwap emits the canonical swap amounts, so skip
361+
// persisting the PoolManager Swap entity to avoid duplicate swap records.
362+
const transaction = loadTransaction(event)
363+
const swap = new Swap(transaction.id + '-' + event.logIndex.toString())
364+
swap.transaction = transaction.id
365+
swap.timestamp = transaction.timestamp
366+
swap.pool = pool.id
367+
swap.token0 = pool.token0
368+
swap.token1 = pool.token1
369+
swap.sender = event.params.sender
370+
swap.origin = event.transaction.from
371+
swap.amount0 = amount0
372+
swap.amount1 = amount1
373+
swap.amountUSD = amountTotalUSDTracked
374+
swap.tick = BigInt.fromI32(event.params.tick)
375+
swap.sqrtPriceX96 = event.params.sqrtPriceX96
376+
swap.logIndex = event.logIndex
377+
swap.save()
378+
}
255379
poolManager.save()
256380
pool.save()
257381
token0.save()

src/utils/chains.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -772,7 +772,7 @@ export function getSubgraphConfig(): SubgraphConfig {
772772
// Hooks that represent USD-stable <> USD-stable aggregator pools where pool price should be treated as parity.
773773
export function getUSDStableStableHookAddresses(): string[] {
774774
if (dataSource.network() == TEMPO_NETWORK_NAME) {
775-
return ['0x717c31c3ea5f9070297f239fafd63d21afdaa888']
775+
return ['0x7169a78a59f136876e724b648fbb339a42f46888']
776776
}
777777
return []
778778
}

subgraph.yaml

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ features:
99
dataSources:
1010
- source:
1111
abi: PoolManager
12-
address: "0x248083fb965359d82b06c1f5322480dcfc1ad857"
13-
startBlock: 28974969
12+
address: "0x33620f62c5b9b2086dd6b62f4a297a9f30347029"
13+
startBlock: 6606180
1414
name: PoolManager
15-
network: linea
15+
network: tempo
1616
kind: ethereum/contract
1717
mapping:
1818
kind: ethereum/events
@@ -31,6 +31,8 @@ dataSources:
3131
file: ./abis/ERC20NameBytes.json
3232
- name: PoolManager
3333
file: ./abis/PoolManager.json
34+
- name: AggregatorHook
35+
file: ./abis/AggregatorHook.json
3436
eventHandlers:
3537
- event: Initialize(indexed bytes32,indexed address,indexed
3638
address,uint24,int24,address,uint160,int24)
@@ -43,10 +45,10 @@ dataSources:
4345
handler: handleSwap
4446
- source:
4547
abi: PositionManager
46-
address: "0xddcad5775b2816a87495f207731b3571d7ee3c76"
47-
startBlock: 28974972
48+
address: "0x3fc79444f8eacc1894775493ff3fa41f1e35ce11"
49+
startBlock: 6606180
4850
name: PositionManager
49-
network: linea
51+
network: tempo
5052
kind: ethereum/contract
5153
mapping:
5254
kind: ethereum/events
@@ -65,3 +67,32 @@ dataSources:
6567
handler: handleUnsubscription
6668
- event: Transfer(indexed address,indexed address,indexed uint256)
6769
handler: handleTransfer
70+
- source:
71+
abi: AggregatorHook
72+
address: "0x7169a78a59f136876e724b648fbb339a42f46888"
73+
startBlock: 6606180
74+
name: AggregatorHook
75+
network: tempo
76+
kind: ethereum/contract
77+
mapping:
78+
kind: ethereum/events
79+
apiVersion: 0.0.7
80+
language: wasm/assemblyscript
81+
file: ./src/mappings/poolManager.mapping.ts
82+
entities:
83+
- Pool
84+
- Token
85+
abis:
86+
- name: ERC20
87+
file: ./abis/ERC20.json
88+
- name: ERC20SymbolBytes
89+
file: ./abis/ERC20SymbolBytes.json
90+
- name: ERC20NameBytes
91+
file: ./abis/ERC20NameBytes.json
92+
- name: PoolManager
93+
file: ./abis/PoolManager.json
94+
- name: AggregatorHook
95+
file: ./abis/AggregatorHook.json
96+
eventHandlers:
97+
- event: HookSwap(indexed bytes32,indexed address,int256,int256,uint24)
98+
handler: handleHookSwap

0 commit comments

Comments
 (0)