mirror of
https://github.com/luxfi/graph.git
synced 2026-09-24 00:26:07 +00:00
a chain's factories come from its config, never from a default
One process indexes six chains, and the AMM factories were resolved by falling back to Lux mainnet's when a Config left them blank. Every chain that did not name its own therefore matched PoolCreated against Lux's factory and denominated prices in WLUX. The zoo subgraph registered no Zoo pool and quoted Lux's coin under Zoo's slug. A default is only honest when one answer is right everywhere. 0x9999 is; a factory address is not. FactoryV2, FactoryV3 and Native now come from Config alone, so a chain that names none indexes none — the failure is visible instead of wrong. The hardcoded per-network tables go with them: they had no readers, and Zoo's entry had drifted to Lux's addresses. ZUSD joins the stable anchors, so Zoo's book prices in dollars. Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
@@ -684,6 +684,30 @@ func TestHandleV2_RejectsNonFactory(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// A chain whose factories were never declared must index no AMM at all. The
|
||||
// failure this pins down is not a crash: it is a Zoo indexer that quietly
|
||||
// adopted Lux's factory address and served Lux's pools as Zoo's.
|
||||
func TestUndeclaredFactory_IndexesNothing(t *testing.T) {
|
||||
token0 := "0x0000000000000000000000000000000000000011"
|
||||
token1 := "0x0000000000000000000000000000000000000022"
|
||||
pool := "0x00000000000000000000000000000000000000c3"
|
||||
|
||||
s := newMemSQLiteStore(t)
|
||||
idx := NewWithConfig(Config{RPC: "http://unused"}, s)
|
||||
|
||||
// The address a previous build reached for when none was configured.
|
||||
const otherChainFactory = "0x80bBc7C4C7a59C899D1B37BC14539A22D5830a84"
|
||||
idx.processLog(context.Background(), poolCreatedLog(otherChainFactory, token0, token1, 3000, pool))
|
||||
if p, _ := s.GetPool(nil, pool); p != nil {
|
||||
t.Fatal("an undeclared chain must not adopt another chain's factory")
|
||||
}
|
||||
|
||||
idx.processLog(context.Background(), swapV3Log(pool, token0, "0xspoof", 1000, -2500))
|
||||
if sw, _ := s.GetSwaps(nil, 10, "timestamp", "desc", nil); sw != nil && len(sw.([]interface{})) != 0 {
|
||||
t.Fatalf("a swap from an unregistered pool must not be recorded, got %d", len(sw.([]interface{})))
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleV3_RejectsNonFactory(t *testing.T) {
|
||||
const rogue = "0x000000000000000000000000000000000000dEaD"
|
||||
token0 := "0x0000000000000000000000000000000000000011"
|
||||
|
||||
+4
-32
@@ -190,35 +190,7 @@ const (
|
||||
// (CLOB) graph schema only ingests genuine fills.
|
||||
const LXSettleAddress = "0x0000000000000000000000000000000000009999"
|
||||
|
||||
// Known factory addresses per network.
|
||||
type NetworkConfig struct {
|
||||
Name string `json:"name" yaml:"name"`
|
||||
ChainID int64 `json:"chainId" yaml:"chain_id"`
|
||||
FactoryV2 string `json:"factoryV2" yaml:"factory_v2"`
|
||||
FactoryV3 string `json:"factoryV3" yaml:"factory_v3"`
|
||||
WETH string `json:"weth" yaml:"weth"` // wrapped native token
|
||||
StableTokens []string `json:"stableTokens" yaml:"stable_tokens"`
|
||||
}
|
||||
|
||||
// Lux network configs — same addresses as exchange subgraphs.
|
||||
var LuxMainnet = NetworkConfig{
|
||||
Name: "lux",
|
||||
ChainID: 96369,
|
||||
FactoryV2: "0xD173926A10A0C4eCd3A51B1422270b65Df0551c1",
|
||||
FactoryV3: "0x80bBc7C4C7a59C899D1B37BC14539A22D5830a84",
|
||||
WETH: "0x4888e4a2ee0f03051c72d2bd3acf755ed3498b3e", // WLUX
|
||||
StableTokens: []string{
|
||||
"0x848Cff46eb323f323b6Bbe1Df274E40793d7f2c2", // LUSD
|
||||
},
|
||||
}
|
||||
|
||||
var ZooMainnet = NetworkConfig{
|
||||
Name: "zoo",
|
||||
ChainID: 200200,
|
||||
FactoryV2: "0xF034942c1140125b5c278aE9cEE1B488e915B2FE",
|
||||
FactoryV3: "0x80bBc7C4C7a59C899D1B37BC14539A22D5830a84",
|
||||
WETH: "0x5491216406daB99b7032b83765F36790E27F8A61", // WLUX
|
||||
StableTokens: []string{
|
||||
"0xb2ee1CE7b84853b83AA08702aD0aD4D79711882D", // LUSDC
|
||||
},
|
||||
}
|
||||
// Which factories are canonical, and what the native unit is, are properties of
|
||||
// the CHAIN being indexed — they arrive in Config, from whoever knows which
|
||||
// chain this is. Naming one chain's addresses here would make every other
|
||||
// chain quote its neighbour's pools.
|
||||
|
||||
+9
-19
@@ -26,9 +26,11 @@ type Status struct {
|
||||
IndexedEvents uint64 `json:"indexedEvents"`
|
||||
}
|
||||
|
||||
// Config tunes an Indexer. The zero value is valid: PoolManager defaults to the
|
||||
// canonical 0x9999 settlement address, FactoryV2/FactoryV3 to the canonical Lux
|
||||
// mainnet AMM factories, and StartBlock to 0 (index from genesis).
|
||||
// Config tunes an Indexer. PoolManager defaults to the 0x9999 settlement
|
||||
// address, which every chain shares, and StartBlock to 0 (index from genesis).
|
||||
// FactoryV2, FactoryV3 and Native have no defaults: they differ per chain, and
|
||||
// a guess would be a claim that one chain's pools belong to another. Left
|
||||
// empty, the matching surface stays empty.
|
||||
type Config struct {
|
||||
RPC string
|
||||
// PoolManager is the DEX settlement precompile address (0x9999). Logs from
|
||||
@@ -49,7 +51,7 @@ type Config struct {
|
||||
// format quotes every token against it — `token.derivedETH` is a price in
|
||||
// native units and `bundle.ethPriceUSD` is what one native unit is worth —
|
||||
// so this address is what makes a price expressible at all. Lower-cased on
|
||||
// construction. Empty => Lux mainnet's.
|
||||
// construction. Empty => no native anchor, so no token quotes a price.
|
||||
Native string
|
||||
// StartBlock is the genesis-relative block to (re)index from. On a clean
|
||||
// chain relaunch the indexer rewinds to this value (see poll's reorg guard).
|
||||
@@ -140,24 +142,12 @@ func NewWithConfig(cfg Config, store *storage.Store) *Indexer {
|
||||
if pm == "" {
|
||||
pm = LXSettleAddress
|
||||
}
|
||||
fv2 := cfg.FactoryV2
|
||||
if fv2 == "" {
|
||||
fv2 = LuxMainnet.FactoryV2
|
||||
}
|
||||
fv3 := cfg.FactoryV3
|
||||
if fv3 == "" {
|
||||
fv3 = LuxMainnet.FactoryV3
|
||||
}
|
||||
native := cfg.Native
|
||||
if native == "" {
|
||||
native = LuxMainnet.WETH
|
||||
}
|
||||
idx := &Indexer{
|
||||
rpc: cfg.RPC,
|
||||
poolManager: strings.ToLower(pm),
|
||||
factoryV2: strings.ToLower(fv2),
|
||||
factoryV3: strings.ToLower(fv3),
|
||||
native: strings.ToLower(native),
|
||||
factoryV2: strings.ToLower(cfg.FactoryV2),
|
||||
factoryV3: strings.ToLower(cfg.FactoryV3),
|
||||
native: strings.ToLower(cfg.Native),
|
||||
label: cfg.Label,
|
||||
startBlock: cfg.StartBlock,
|
||||
store: store,
|
||||
|
||||
@@ -73,7 +73,7 @@ const selBalanceOf = "0x70a08231"
|
||||
var stableSymbols = map[string]bool{
|
||||
"USDC": true, "USDT": true, "DAI": true, "LUSD": true, "USDS": true,
|
||||
"BUSD": true, "FRAX": true, "TUSD": true, "USDP": true, "PYUSD": true,
|
||||
"USDE": true, "GUSD": true,
|
||||
"USDE": true, "GUSD": true, "ZUSD": true,
|
||||
}
|
||||
|
||||
// priceRelaxRounds bounds the outward relaxation from the stable anchors. Each
|
||||
|
||||
Reference in New Issue
Block a user