mirror of
https://github.com/luxfi/graph.git
synced 2026-09-24 00:26:07 +00:00
Every price chart on the site read "Missing chart data". Not because the
trades were missing — they were on disk, and the swaps query serves them —
but because tokenDayDatas, poolDayDatas and uniswapDayDatas each answered
[] from a hardcoded stub, and a client draws nothing under three points.
Underneath that was a clock problem. eth_getLogs says which block a log was
in and never when, so a swap's `timestamp` held the block NUMBER — trade
history dated every fill to January 1970, and a day series was not merely
empty but impossible. The poll now reads the header of each block it saw an
event in, and declines to advance past one it could not read: a block is
indexed when it has been read whole. Rows an older build wrote are dated
from the same headers, which is what lets the series cover the history
already indexed rather than starting the day it ships.
The series itself is a second fold over the trade stream the valuation pass
already builds — one read, one valuation, two questions. A pool's candle is
its own execution ratio, exact for every day. A token's is that ratio carried
to USD through the other leg, so it is exact against a stablecoin and exact
in shape against a volatile one. Value locked is not in the trades at all, so
each day records it while it is today.
Recomputing the whole history every thirty seconds must not rewrite it, so a
day is written only when it moves: in the steady state that is one day per
subject that traded.
Filtering learned two things a chart needs. `where: {token: "0x…"}` names a
reference, and the row carries the token itself so the client can select
through it — so a filter now compares against the referenced id. And an
address arrives checksummed as often as lower-cased, so exact matches on text
ignore case.
pairDayDatas and the hour series stay stubs. Nothing asks for them.
Tests: 18 deliberate mutations — an off-by-one day boundary, high taking the
smallest price, the where filter dropped, the limit applied before the sort,
trades ordered by id, a log index read as decimal, a candle priced off its own
leg, and the call sites that write the series and read the block times removed
outright — each one turns a test red.
Co-authored-by: Hanzo Dev <[email protected]>
259 lines
9.2 KiB
Go
259 lines
9.2 KiB
Go
package engine
|
|
|
|
// Entity types matching Uniswap V2/V3 subgraph schemas exactly.
|
|
// Field names match the GraphQL schema so the exchange frontend
|
|
// queries work without modification.
|
|
|
|
// Factory — V3 factory or V2 UniswapFactory
|
|
type Factory struct {
|
|
ID string `json:"id"`
|
|
PoolCount int64 `json:"poolCount"`
|
|
PairCount int64 `json:"pairCount"` // v2
|
|
TxCount int64 `json:"txCount"`
|
|
TotalVolumeUSD string `json:"totalVolumeUSD"`
|
|
TotalVolumeETH string `json:"totalVolumeETH"`
|
|
TotalFeesUSD string `json:"totalFeesUSD"`
|
|
TotalValueLockedUSD string `json:"totalValueLockedUSD"`
|
|
TotalLiquidityUSD string `json:"totalLiquidityUSD"` // v2 compat
|
|
TotalValueLockedETH string `json:"totalValueLockedETH"`
|
|
}
|
|
|
|
// Bundle — native token price in USD
|
|
type Bundle struct {
|
|
ID string `json:"id"`
|
|
EthPriceUSD string `json:"ethPriceUSD"`
|
|
EthPrice string `json:"ethPrice"` // v2 compat
|
|
LuxPriceUSD string `json:"luxPriceUSD"`
|
|
}
|
|
|
|
// Token — ERC20 token
|
|
type Token struct {
|
|
ID string `json:"id"`
|
|
Symbol string `json:"symbol"`
|
|
Name string `json:"name"`
|
|
Decimals int64 `json:"decimals"`
|
|
TotalSupply string `json:"totalSupply"`
|
|
Volume string `json:"volume"`
|
|
VolumeUSD string `json:"volumeUSD"`
|
|
UntrackedVolumeUSD string `json:"untrackedVolumeUSD"`
|
|
FeesUSD string `json:"feesUSD"`
|
|
TxCount int64 `json:"txCount"`
|
|
PoolCount int64 `json:"poolCount"`
|
|
TotalValueLocked string `json:"totalValueLocked"`
|
|
TotalValueLockedUSD string `json:"totalValueLockedUSD"`
|
|
TotalLiquidity string `json:"totalLiquidity"` // v2
|
|
DerivedETH string `json:"derivedETH"`
|
|
TradeVolume string `json:"tradeVolume"` // v2
|
|
TradeVolumeUSD string `json:"tradeVolumeUSD"` // v2
|
|
}
|
|
|
|
// Pool — V3 concentrated liquidity pool
|
|
type Pool struct {
|
|
ID string `json:"id"`
|
|
CreatedAtTimestamp int64 `json:"createdAtTimestamp"`
|
|
CreatedAtBlockNumber int64 `json:"createdAtBlockNumber"`
|
|
Token0 *Token `json:"token0"`
|
|
Token1 *Token `json:"token1"`
|
|
FeeTier int64 `json:"feeTier"`
|
|
Liquidity string `json:"liquidity"`
|
|
SqrtPrice string `json:"sqrtPrice"`
|
|
Token0Price string `json:"token0Price"`
|
|
Token1Price string `json:"token1Price"`
|
|
Tick int64 `json:"tick"`
|
|
VolumeToken0 string `json:"volumeToken0"`
|
|
VolumeToken1 string `json:"volumeToken1"`
|
|
VolumeUSD string `json:"volumeUSD"`
|
|
FeesUSD string `json:"feesUSD"`
|
|
TxCount int64 `json:"txCount"`
|
|
TotalValueLockedToken0 string `json:"totalValueLockedToken0"`
|
|
TotalValueLockedToken1 string `json:"totalValueLockedToken1"`
|
|
TotalValueLockedETH string `json:"totalValueLockedETH"`
|
|
TotalValueLockedUSD string `json:"totalValueLockedUSD"`
|
|
}
|
|
|
|
// Pair — V2 constant product AMM pair
|
|
type Pair struct {
|
|
ID string `json:"id"`
|
|
Token0 *Token `json:"token0"`
|
|
Token1 *Token `json:"token1"`
|
|
Reserve0 string `json:"reserve0"`
|
|
Reserve1 string `json:"reserve1"`
|
|
TotalSupply string `json:"totalSupply"`
|
|
ReserveETH string `json:"reserveETH"`
|
|
ReserveUSD string `json:"reserveUSD"`
|
|
TrackedReserveETH string `json:"trackedReserveETH"`
|
|
Token0Price string `json:"token0Price"`
|
|
Token1Price string `json:"token1Price"`
|
|
VolumeToken0 string `json:"volumeToken0"`
|
|
VolumeToken1 string `json:"volumeToken1"`
|
|
VolumeUSD string `json:"volumeUSD"`
|
|
TxCount int64 `json:"txCount"`
|
|
CreatedAtTimestamp int64 `json:"createdAtTimestamp"`
|
|
CreatedAtBlockNumber int64 `json:"createdAtBlockNumber"`
|
|
}
|
|
|
|
// Swap — trade event (V2 and V3)
|
|
type Swap struct {
|
|
ID string `json:"id"`
|
|
Transaction string `json:"transaction"`
|
|
Timestamp int64 `json:"timestamp"`
|
|
Pool string `json:"pool"`
|
|
Pair string `json:"pair"` // v2
|
|
Token0 string `json:"token0"`
|
|
Token1 string `json:"token1"`
|
|
Sender string `json:"sender"`
|
|
Recipient string `json:"recipient"`
|
|
Origin string `json:"origin"`
|
|
Amount0 string `json:"amount0"`
|
|
Amount1 string `json:"amount1"`
|
|
AmountUSD string `json:"amountUSD"`
|
|
Amount0In string `json:"amount0In"` // v2
|
|
Amount0Out string `json:"amount0Out"` // v2
|
|
Amount1In string `json:"amount1In"` // v2
|
|
Amount1Out string `json:"amount1Out"` // v2
|
|
}
|
|
|
|
// Mint — liquidity provision event
|
|
type Mint struct {
|
|
ID string `json:"id"`
|
|
Transaction string `json:"transaction"`
|
|
Timestamp int64 `json:"timestamp"`
|
|
Pool string `json:"pool"`
|
|
Pair string `json:"pair"` // v2
|
|
Sender string `json:"sender"`
|
|
Owner string `json:"owner"`
|
|
Amount0 string `json:"amount0"`
|
|
Amount1 string `json:"amount1"`
|
|
AmountUSD string `json:"amountUSD"`
|
|
TickLower int64 `json:"tickLower"` // v3
|
|
TickUpper int64 `json:"tickUpper"` // v3
|
|
LogIndex int64 `json:"logIndex"`
|
|
}
|
|
|
|
// Burn — liquidity removal event
|
|
type Burn struct {
|
|
ID string `json:"id"`
|
|
Transaction string `json:"transaction"`
|
|
Timestamp int64 `json:"timestamp"`
|
|
Pool string `json:"pool"`
|
|
Pair string `json:"pair"` // v2
|
|
Owner string `json:"owner"`
|
|
Amount0 string `json:"amount0"`
|
|
Amount1 string `json:"amount1"`
|
|
AmountUSD string `json:"amountUSD"`
|
|
TickLower int64 `json:"tickLower"` // v3
|
|
TickUpper int64 `json:"tickUpper"` // v3
|
|
LogIndex int64 `json:"logIndex"`
|
|
}
|
|
|
|
// Tick — V3 price tick
|
|
type Tick struct {
|
|
ID string `json:"id"`
|
|
PoolAddress string `json:"poolAddress"`
|
|
TickIdx int64 `json:"tickIdx"`
|
|
LiquidityGross string `json:"liquidityGross"`
|
|
LiquidityNet string `json:"liquidityNet"`
|
|
Price0 string `json:"price0"`
|
|
Price1 string `json:"price1"`
|
|
}
|
|
|
|
// Time-series entities
|
|
|
|
type TokenDayData struct {
|
|
ID string `json:"id"`
|
|
Date int64 `json:"date"`
|
|
Token *Token `json:"token"`
|
|
Volume string `json:"volume"`
|
|
VolumeUSD string `json:"volumeUSD"`
|
|
TotalValueLocked string `json:"totalValueLocked"`
|
|
TotalValueLockedUSD string `json:"totalValueLockedUSD"`
|
|
PriceUSD string `json:"priceUSD"`
|
|
Open string `json:"open"`
|
|
High string `json:"high"`
|
|
Low string `json:"low"`
|
|
Close string `json:"close"`
|
|
}
|
|
|
|
type PoolDayData struct {
|
|
ID string `json:"id"`
|
|
Date int64 `json:"date"`
|
|
Pool *Pool `json:"pool"`
|
|
Liquidity string `json:"liquidity"`
|
|
VolumeUSD string `json:"volumeUSD"`
|
|
FeesUSD string `json:"feesUSD"`
|
|
TotalValueLockedUSD string `json:"tvlUSD"`
|
|
TxCount int64 `json:"txCount"`
|
|
Open string `json:"open"`
|
|
High string `json:"high"`
|
|
Low string `json:"low"`
|
|
Close string `json:"close"`
|
|
}
|
|
|
|
type PairDayData struct {
|
|
ID string `json:"id"`
|
|
Date int64 `json:"date"`
|
|
Pair *Pair `json:"pair"`
|
|
VolumeUSD string `json:"dailyVolumeUSD"`
|
|
ReserveUSD string `json:"reserveUSD"`
|
|
TxCount int64 `json:"dailyTxns"`
|
|
}
|
|
|
|
// FactoryDayData is the protocol total for one day — the series behind the
|
|
// explore page's TVL and 24h-volume tiles, which ask for it under the v2-era
|
|
// root name `uniswapDayDatas` but read the v3 field names.
|
|
type FactoryDayData struct {
|
|
ID string `json:"id"`
|
|
Date int64 `json:"date"`
|
|
VolumeUSD string `json:"volumeUSD"`
|
|
TotalValueLockedUSD string `json:"tvlUSD"`
|
|
TxCount int64 `json:"txCount"`
|
|
}
|
|
|
|
// Transaction — on-chain transaction
|
|
type Transaction struct {
|
|
ID string `json:"id"`
|
|
BlockNumber int64 `json:"blockNumber"`
|
|
Timestamp int64 `json:"timestamp"`
|
|
GasUsed string `json:"gasUsed"`
|
|
GasPrice string `json:"gasPrice"`
|
|
}
|
|
|
|
// Collect — V3 fee collection event
|
|
type Collect struct {
|
|
ID string `json:"id"`
|
|
Transaction string `json:"transaction"`
|
|
Timestamp int64 `json:"timestamp"`
|
|
Pool string `json:"pool"`
|
|
Owner string `json:"owner"`
|
|
Amount0 string `json:"amount0"`
|
|
Amount1 string `json:"amount1"`
|
|
TickLower int64 `json:"tickLower"`
|
|
TickUpper int64 `json:"tickUpper"`
|
|
}
|
|
|
|
// Flash — V3 flash loan event
|
|
type Flash struct {
|
|
ID string `json:"id"`
|
|
Transaction string `json:"transaction"`
|
|
Timestamp int64 `json:"timestamp"`
|
|
Pool string `json:"pool"`
|
|
Sender string `json:"sender"`
|
|
Recipient string `json:"recipient"`
|
|
Amount0 string `json:"amount0"`
|
|
Amount1 string `json:"amount1"`
|
|
Paid0 string `json:"paid0"`
|
|
Paid1 string `json:"paid1"`
|
|
}
|
|
|
|
// PairHourData — hourly V2 pair snapshot
|
|
type PairHourData struct {
|
|
ID string `json:"id"`
|
|
PeriodStartUnix int64 `json:"periodStartUnix"`
|
|
Pair *Pair `json:"pair"`
|
|
Reserve0 string `json:"reserve0"`
|
|
Reserve1 string `json:"reserve1"`
|
|
ReserveUSD string `json:"reserveUSD"`
|
|
VolumeUSD string `json:"volumeUSD"`
|
|
TxCount int64 `json:"txCount"`
|
|
}
|