Some checks failed
V2 CI/CD Pipeline / Unit Tests (100% Coverage Required) (push) Has been cancelled
V2 CI/CD Pipeline / Pre-Flight Checks (push) Has been cancelled
V2 CI/CD Pipeline / Build & Dependencies (push) Has been cancelled
V2 CI/CD Pipeline / Code Quality & Linting (push) Has been cancelled
V2 CI/CD Pipeline / Integration Tests (push) Has been cancelled
V2 CI/CD Pipeline / Performance Benchmarks (push) Has been cancelled
V2 CI/CD Pipeline / Decimal Precision Validation (push) Has been cancelled
V2 CI/CD Pipeline / Modularity Validation (push) Has been cancelled
V2 CI/CD Pipeline / Final Validation Summary (push) Has been cancelled
**Curve StableSwap Parser** (`curve.go`): - TokenExchange event parsing (address,int128,uint256,int128,uint256) - TokenExchangeUnderlying event support for wrapped tokens - Coin index (int128) to token address mapping - Handles 2-coin and multi-coin pools - Typical use: USDC/USDT, DAI/USDC stablecoin swaps - Low slippage due to amplification coefficient (A parameter) - Fee: typically 0.04% (4 basis points) **Key Features:** - Buyer address extraction from indexed topics - Coin ID to token mapping via pool cache - Both directions: token0→token1 and token1→token0 - Buyer is both sender and recipient (Curve pattern) - Support for 6-decimal stablecoins (USDC, USDT) **Testing** (`curve_test.go`): - TokenExchange and TokenExchangeUnderlying signature validation - Swap direction tests (USDC→USDT, USDT→USDC) - Multi-event receipts with mixed protocols - Decimal scaling validation (6 decimals → 18 decimals) - Pool not found error handling **Type System Fix:** - Exported ScaleToDecimals() function in pkg/types/pool.go - Updated all callers to use exported function - Fixed test function name (TestScaleToDecimals) - Consistent across all parsers (V2, V3, Curve) **Use Cases:** 1. Stablecoin arbitrage (Curve vs Uniswap pricing) 2. Low-slippage large swaps (Curve specialization) 3. Multi-coin pool support (3pool, 4pool) 4. Underlying vs wrapped token detection **Task:** P2-018 (Curve StableSwap parser) **Coverage:** 100% (enforced in CI/CD) **Protocol:** Curve StableSwap on Arbitrum 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
125 lines
3.1 KiB
Go
125 lines
3.1 KiB
Go
package types
|
|
|
|
import (
|
|
"math/big"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
)
|
|
|
|
// PoolInfo contains comprehensive pool information
|
|
type PoolInfo struct {
|
|
// Pool identification
|
|
Address common.Address
|
|
Protocol ProtocolType
|
|
PoolType string // e.g., "constant-product", "stable", "weighted"
|
|
|
|
// Token information
|
|
Token0 common.Address
|
|
Token1 common.Address
|
|
Token0Decimals uint8
|
|
Token1Decimals uint8
|
|
Token0Symbol string
|
|
Token1Symbol string
|
|
|
|
// Reserves/Liquidity
|
|
Reserve0 *big.Int
|
|
Reserve1 *big.Int
|
|
Liquidity *big.Int
|
|
|
|
// Fee information
|
|
Fee uint32 // Fee in basis points (e.g., 30 = 0.3%)
|
|
FeeGrowth0 *big.Int // UniswapV3 fee growth
|
|
FeeGrowth1 *big.Int // UniswapV3 fee growth
|
|
|
|
// Protocol-specific data
|
|
SqrtPriceX96 *big.Int // UniswapV3/Camelot V3
|
|
Tick *int32 // UniswapV3/Camelot V3
|
|
TickSpacing int32 // UniswapV3/Camelot V3
|
|
AmpCoefficient *big.Int // Curve amplification coefficient
|
|
|
|
// Pool state
|
|
IsActive bool
|
|
BlockNumber uint64
|
|
LastUpdate uint64
|
|
}
|
|
|
|
// Validate checks if the pool info is valid
|
|
func (p *PoolInfo) Validate() error {
|
|
if p.Address == (common.Address{}) {
|
|
return ErrInvalidPoolAddress
|
|
}
|
|
|
|
if p.Token0 == (common.Address{}) {
|
|
return ErrInvalidToken0Address
|
|
}
|
|
|
|
if p.Token1 == (common.Address{}) {
|
|
return ErrInvalidToken1Address
|
|
}
|
|
|
|
if p.Token0Decimals == 0 || p.Token0Decimals > 18 {
|
|
return ErrInvalidToken0Decimals
|
|
}
|
|
|
|
if p.Token1Decimals == 0 || p.Token1Decimals > 18 {
|
|
return ErrInvalidToken1Decimals
|
|
}
|
|
|
|
if p.Protocol == ProtocolUnknown {
|
|
return ErrUnknownProtocol
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetTokenPair returns the token pair as a sorted tuple
|
|
func (p *PoolInfo) GetTokenPair() (common.Address, common.Address) {
|
|
if p.Token0.Big().Cmp(p.Token1.Big()) < 0 {
|
|
return p.Token0, p.Token1
|
|
}
|
|
return p.Token1, p.Token0
|
|
}
|
|
|
|
// CalculatePrice calculates the price of token0 in terms of token1
|
|
func (p *PoolInfo) CalculatePrice() *big.Float {
|
|
if p.Reserve0 == nil || p.Reserve1 == nil || p.Reserve0.Sign() == 0 {
|
|
return big.NewFloat(0)
|
|
}
|
|
|
|
// Scale reserves to 18 decimals for consistent calculation
|
|
reserve0Scaled := ScaleToDecimals(p.Reserve0, p.Token0Decimals, 18)
|
|
reserve1Scaled := ScaleToDecimals(p.Reserve1, p.Token1Decimals, 18)
|
|
|
|
// Price = Reserve1 / Reserve0
|
|
reserve0Float := new(big.Float).SetInt(reserve0Scaled)
|
|
reserve1Float := new(big.Float).SetInt(reserve1Scaled)
|
|
|
|
price := new(big.Float).Quo(reserve1Float, reserve0Float)
|
|
return price
|
|
}
|
|
|
|
// ScaleToDecimals scales an amount from one decimal precision to another
|
|
func ScaleToDecimals(amount *big.Int, fromDecimals, toDecimals uint8) *big.Int {
|
|
if fromDecimals == toDecimals {
|
|
return new(big.Int).Set(amount)
|
|
}
|
|
|
|
if fromDecimals < toDecimals {
|
|
// Scale up
|
|
multiplier := new(big.Int).Exp(
|
|
big.NewInt(10),
|
|
big.NewInt(int64(toDecimals-fromDecimals)),
|
|
nil,
|
|
)
|
|
return new(big.Int).Mul(amount, multiplier)
|
|
}
|
|
|
|
// Scale down
|
|
divisor := new(big.Int).Exp(
|
|
big.NewInt(10),
|
|
big.NewInt(int64(fromDecimals-toDecimals)),
|
|
nil,
|
|
)
|
|
return new(big.Int).Div(amount, divisor)
|
|
}
|