mirror of
https://github.com/luxfi/precompile.git
synced 2026-09-24 00:26:21 +00:00
V3 precompile composes the existing (untested) dex tick/sqrtPrice/swap math — TickMath, SqrtPriceMath, SwapMath, liquidity — ABI-compatible with Uniswap V3 (initialize/mint/burn/collect/swap + views). 19/19 tests vs v3-core vectors (getSqrtRatioAtTick(0)=2^96, ComputeSwapStep, e2e mint→swap→burn→collect w/ conservation). Router quoteV2 resolves to the native constant-product AMM. Native, deterministic (big.Int, no float/GPU — runs in consensus).
96 lines
5.4 KiB
Go
96 lines
5.4 KiB
Go
// Copyright (C) 2026, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package v3
|
|
|
|
import (
|
|
"math/big"
|
|
|
|
"github.com/luxfi/crypto"
|
|
"github.com/luxfi/geth/common"
|
|
ethtypes "github.com/luxfi/geth/core/types"
|
|
)
|
|
|
|
// Event topic0 hashes, derived from the canonical signature strings (the single
|
|
// source of truth, mirroring swap/events.go and dex/events.go).
|
|
//
|
|
// These mirror Uniswap V3's Pool events (Initialize/Mint/Burn/Collect/Swap) with
|
|
// ONE structural change: because this precompile is a SINGLETON serving many
|
|
// pools (V3's pool is one-contract-per-pair, impossible for a precompile address),
|
|
// every event carries `bytes32 indexed poolId` as its first (indexed) topic so a
|
|
// subscriber can filter per pool. Owner is the second indexed topic on the
|
|
// liquidity events; the int24 ticks live in data (the EVM allows at most 3 indexed
|
|
// topics + topic0, and poolId+owner consume two).
|
|
var (
|
|
initializeEventSig = common.BytesToHash(crypto.Keccak256([]byte("Initialize(bytes32,address,address,uint24,int24,uint160,int24)")))
|
|
mintEventSig = common.BytesToHash(crypto.Keccak256([]byte("Mint(bytes32,address,int24,int24,uint128,uint256,uint256)")))
|
|
burnEventSig = common.BytesToHash(crypto.Keccak256([]byte("Burn(bytes32,address,int24,int24,uint128,uint256,uint256)")))
|
|
collectEventSig = common.BytesToHash(crypto.Keccak256([]byte("Collect(bytes32,address,int24,int24,uint128,uint128)")))
|
|
swapEventSig = common.BytesToHash(crypto.Keccak256([]byte("Swap(bytes32,address,int256,int256,uint160,uint128,int24)")))
|
|
)
|
|
|
|
type logSink interface{ AddLog(*ethtypes.Log) }
|
|
|
|
// addressTopic / addressWord left-pads a 20-byte address into a 32-byte word.
|
|
func addressWord(a common.Address) []byte { return common.BytesToHash(a.Bytes()).Bytes() }
|
|
|
|
// emitInitialize: Initialize(bytes32 indexed poolId, address currency0,
|
|
// address currency1, uint24 fee, int24 tickSpacing, uint160 sqrtPriceX96, int24 tick).
|
|
func emitInitialize(db logSink, poolId common.Hash, c0, c1 common.Address, fee uint32, tickSpacing, tick int32, sqrtPriceX96 *big.Int) {
|
|
data := make([]byte, 0, 6*32)
|
|
data = append(data, addressWord(c0)...)
|
|
data = append(data, addressWord(c1)...)
|
|
data = append(data, uintWord(big.NewInt(int64(fee))).Bytes()...)
|
|
data = append(data, intWord(big.NewInt(int64(tickSpacing))).Bytes()...)
|
|
data = append(data, uintWord(sqrtPriceX96).Bytes()...)
|
|
data = append(data, intWord(big.NewInt(int64(tick))).Bytes()...)
|
|
db.AddLog(ðtypes.Log{Address: v3Addr, Topics: []common.Hash{initializeEventSig, poolId}, Data: data})
|
|
}
|
|
|
|
// emitMint: Mint(bytes32 indexed poolId, address indexed owner, int24 tickLower,
|
|
// int24 tickUpper, uint128 amount, uint256 amount0, uint256 amount1).
|
|
func emitMint(db logSink, poolId common.Hash, owner common.Address, tickLower, tickUpper int32, amount, amount0, amount1 *big.Int) {
|
|
data := make([]byte, 0, 5*32)
|
|
data = append(data, intWord(big.NewInt(int64(tickLower))).Bytes()...)
|
|
data = append(data, intWord(big.NewInt(int64(tickUpper))).Bytes()...)
|
|
data = append(data, uintWord(amount).Bytes()...)
|
|
data = append(data, uintWord(amount0).Bytes()...)
|
|
data = append(data, uintWord(amount1).Bytes()...)
|
|
db.AddLog(ðtypes.Log{Address: v3Addr, Topics: []common.Hash{mintEventSig, poolId, common.BytesToHash(owner.Bytes())}, Data: data})
|
|
}
|
|
|
|
// emitBurn: Burn(bytes32 indexed poolId, address indexed owner, int24 tickLower,
|
|
// int24 tickUpper, uint128 amount, uint256 amount0, uint256 amount1).
|
|
func emitBurn(db logSink, poolId common.Hash, owner common.Address, tickLower, tickUpper int32, amount, amount0, amount1 *big.Int) {
|
|
data := make([]byte, 0, 5*32)
|
|
data = append(data, intWord(big.NewInt(int64(tickLower))).Bytes()...)
|
|
data = append(data, intWord(big.NewInt(int64(tickUpper))).Bytes()...)
|
|
data = append(data, uintWord(amount).Bytes()...)
|
|
data = append(data, uintWord(amount0).Bytes()...)
|
|
data = append(data, uintWord(amount1).Bytes()...)
|
|
db.AddLog(ðtypes.Log{Address: v3Addr, Topics: []common.Hash{burnEventSig, poolId, common.BytesToHash(owner.Bytes())}, Data: data})
|
|
}
|
|
|
|
// emitCollect: Collect(bytes32 indexed poolId, address indexed owner,
|
|
// int24 tickLower, int24 tickUpper, uint128 amount0, uint128 amount1).
|
|
func emitCollect(db logSink, poolId common.Hash, owner common.Address, tickLower, tickUpper int32, amount0, amount1 *big.Int) {
|
|
data := make([]byte, 0, 4*32)
|
|
data = append(data, intWord(big.NewInt(int64(tickLower))).Bytes()...)
|
|
data = append(data, intWord(big.NewInt(int64(tickUpper))).Bytes()...)
|
|
data = append(data, uintWord(amount0).Bytes()...)
|
|
data = append(data, uintWord(amount1).Bytes()...)
|
|
db.AddLog(ðtypes.Log{Address: v3Addr, Topics: []common.Hash{collectEventSig, poolId, common.BytesToHash(owner.Bytes())}, Data: data})
|
|
}
|
|
|
|
// emitSwap: Swap(bytes32 indexed poolId, address indexed sender, int256 amount0,
|
|
// int256 amount1, uint160 sqrtPriceX96, uint128 liquidity, int24 tick).
|
|
func emitSwap(db logSink, poolId common.Hash, sender common.Address, amount0, amount1, sqrtPriceX96, liquidity *big.Int, tick int32) {
|
|
data := make([]byte, 0, 5*32)
|
|
data = append(data, intWord(amount0).Bytes()...)
|
|
data = append(data, intWord(amount1).Bytes()...)
|
|
data = append(data, uintWord(sqrtPriceX96).Bytes()...)
|
|
data = append(data, uintWord(liquidity).Bytes()...)
|
|
data = append(data, intWord(big.NewInt(int64(tick))).Bytes()...)
|
|
db.AddLog(ðtypes.Log{Address: v3Addr, Topics: []common.Hash{swapEventSig, poolId, common.BytesToHash(sender.Bytes())}, Data: data})
|
|
}
|