Files
zeekay 121a85162f k8s(financial): bump lux-financial to v1.2.0
Expanded solutions (10 new industries) and products (Compliance & Data
section) to cover full docs.lux.financial capability surface.
2026-04-20 22:33:19 -07:00

145 lines
3.6 KiB
Go

package main
import (
"context"
"fmt"
"log"
"math/big"
"sync"
"github.com/luxfi/geth/ethclient"
)
// ChainClient holds a connection to a single EVM chain.
type ChainClient struct {
Config ChainConfig
Client *ethclient.Client
AMM *AMMClient
}
// ChainManager manages connections to multiple EVM chains.
type ChainManager struct {
mu sync.RWMutex
clients map[string]*ChainClient // keyed by chain name (lowercase)
}
// NewChainManager creates a ChainManager and connects to all configured chains.
func NewChainManager(chains []ChainConfig) (*ChainManager, error) {
cm := &ChainManager{
clients: make(map[string]*ChainClient),
}
for _, cc := range chains {
client, err := ethclient.Dial(cc.RPC)
if err != nil {
log.Printf("[%s] WARNING: failed to connect to %s: %v", cc.Name, cc.RPC, err)
continue
}
// Verify chain ID
ctx := context.Background()
chainID, err := client.ChainID(ctx)
if err != nil {
log.Printf("[%s] WARNING: failed to get chain ID: %v", cc.Name, err)
client.Close()
continue
}
if chainID.Cmp(cc.ChainID) != 0 {
log.Printf("[%s] WARNING: chain ID mismatch: expected %s, got %s",
cc.Name, cc.ChainID.String(), chainID.String())
client.Close()
continue
}
amm := NewAMMClient(client, cc)
cm.clients[cc.Name] = &ChainClient{
Config: cc,
Client: client,
AMM: amm,
}
log.Printf("[%s] Connected (chainID=%s, rpc=%s)", cc.Name, chainID.String(), cc.RPC)
}
if len(cm.clients) == 0 {
return nil, fmt.Errorf("failed to connect to any chain")
}
return cm, nil
}
// Get returns the client for a named chain.
func (cm *ChainManager) Get(name string) *ChainClient {
cm.mu.RLock()
defer cm.mu.RUnlock()
return cm.clients[name]
}
// All returns all connected chain clients.
func (cm *ChainManager) All() []*ChainClient {
cm.mu.RLock()
defer cm.mu.RUnlock()
result := make([]*ChainClient, 0, len(cm.clients))
for _, cc := range cm.clients {
result = append(result, cc)
}
return result
}
// Close closes all chain connections.
func (cm *ChainManager) Close() {
cm.mu.Lock()
defer cm.mu.Unlock()
for name, cc := range cm.clients {
cc.Client.Close()
log.Printf("[%s] Disconnected", name)
}
}
// GetNativeBalance returns the native LUX balance on a chain.
func (cc *ChainClient) GetNativeBalance(ctx context.Context, wallet *Wallet) (*big.Int, error) {
return cc.Client.BalanceAt(ctx, wallet.Address, nil)
}
// PrintBalances logs the native and token balances for a wallet on this chain.
func (cc *ChainClient) PrintBalances(ctx context.Context, wallet *Wallet) {
native, err := cc.GetNativeBalance(ctx, wallet)
if err != nil {
log.Printf("[%s] %s native balance error: %v", cc.Config.Name, wallet.ShortAddr(), err)
return
}
wlux, _ := cc.AMM.GetTokenBalance(ctx, cc.Config.WLUX, wallet.Address)
leth, _ := cc.AMM.GetTokenBalance(ctx, cc.Config.LETH, wallet.Address)
lbtc, _ := cc.AMM.GetTokenBalance(ctx, cc.Config.LBTC, wallet.Address)
lusdc, _ := cc.AMM.GetTokenBalance(ctx, cc.Config.LUSDC, wallet.Address)
log.Printf("[%s] %s balances: native=%s WLUX=%s LETH=%s LBTC=%s LUSDC=%s",
cc.Config.Name,
wallet.ShortAddr(),
formatWei(native),
formatWei(wlux),
formatWei(leth),
formatWei(lbtc),
formatWei(lusdc),
)
}
// formatWei formats a wei value as a human-readable string with 4 decimal places.
func formatWei(wei *big.Int) string {
if wei == nil {
return "0"
}
// Convert to float for display
f := new(big.Float).SetInt(wei)
divisor := new(big.Float).SetInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil))
f.Quo(f, divisor)
result, _ := f.Float64()
return fmt.Sprintf("%.4f", result)
}