Files
zeekayandHanzo AI 15131f6af4
CI/CD / render (push) Waiting to run
chain letters are lowercase
The primary-network chains answer to either case; one spelling beats two.
/v1/chain/c/rpc, /v1/chain/p, /v1/chain/x across the estate.

Co-authored-by: Hanzo AI <[email protected]>
2026-09-08 15:23:20 -07:00

196 lines
6.0 KiB
Go

package main
import (
"fmt"
"math/big"
"os"
"strconv"
"strings"
"github.com/luxfi/geth/common"
)
// ChainConfig holds the configuration for a single EVM chain.
type ChainConfig struct {
Name string
ChainID *big.Int
RPC string
// Token addresses (may differ per chain — C-chain has unique nonces)
WLUX common.Address
LETH common.Address
LBTC common.Address
LUSDC common.Address
// AMM contracts
AMMV2Router common.Address
AMMV2Factory common.Address
// Whether AMM swaps work on this chain (app chain EVM may lack MCOPY)
SwapsEnabled bool
}
// Config holds all bot configuration loaded from environment variables.
type Config struct {
Mnemonic string
// Chains to operate on
Chains []ChainConfig
// Trading parameters
TradeIntervalMinSec int
TradeIntervalMaxSec int
TradeAmountMinETH float64
TradeAmountMaxETH float64
NumWallets int
// Wallet indices to use (start..start+NumWallets-1)
WalletStartIndex int
}
func envOrDefault(key, def string) string {
if v := os.Getenv(key); v != "" {
return v
}
return def
}
func envIntOrDefault(key string, def int) int {
if v := os.Getenv(key); v != "" {
n, err := strconv.Atoi(v)
if err == nil {
return n
}
}
return def
}
func envFloatOrDefault(key string, def float64) float64 {
if v := os.Getenv(key); v != "" {
f, err := strconv.ParseFloat(v, 64)
if err == nil {
return f
}
}
return def
}
// App-chain shared addresses (deployer had nonce=0 on each fresh chain)
var appChainTokens = struct {
WLUX, LETH, LBTC, LUSDC common.Address
AMMV2Router, AMMV2Factory common.Address
}{
WLUX: common.HexToAddress("0x548f54dfb32ea6ce4fa3515236696cf3d1b7d26a"),
LETH: common.HexToAddress("0xe0f7e9a0cb1688cca453995fd6e19ae4fbd9cbfd"),
LBTC: common.HexToAddress("0x7d7cc8d05bb0f38d80b5ce44b4b069a6fb769468"),
LUSDC: common.HexToAddress("0xc5e4a6f54be469551a342872c1ab83ab46f61b22"),
AMMV2Factory: common.HexToAddress("0x84cf0a13db1be8e1f0676405cfcbc8b09692fd1c"),
AMMV2Router: common.HexToAddress("0x2382f7a49fa48e1f91bec466c32e1d7f13ec8206"),
}
// allChains returns all supported chain configurations.
// C-chain addresses are from the user-provided contract addresses.
// App chains use the shared deployed addresses from e2e-exchange.py.
func allChains() map[string]ChainConfig {
return map[string]ChainConfig{
"lux": {
Name: "Lux C-Chain",
ChainID: big.NewInt(96369),
RPC: envOrDefault("RPC_LUX", "https://api.lux.network/mainnet/v1/chain/c/rpc"),
WLUX: common.HexToAddress("0x3C18bB6B17eb3F0879d4653e0120a531aF4d86E3"),
LETH: common.HexToAddress("0x5a88986958ea76Dd043f834542724F081cA1443B"),
LBTC: common.HexToAddress("0x8a3fad1c7FB94461621351aa6A983B6f814F039c"),
LUSDC: common.HexToAddress("0x57f9E717dc080a6A76fB6F77BecA8C9C1D266B96"),
AMMV2Factory: common.HexToAddress("0xb06B31521Afc434F87Fe4852c98FC15A26c92aE8"),
AMMV2Router: common.HexToAddress("0x6A1a32BF731d504122EA318cE7Bd8D92b2284C0d"),
SwapsEnabled: true, // C-chain supports Cancun opcodes
},
"zoo": {
Name: "Zoo Chain",
ChainID: big.NewInt(200200),
RPC: envOrDefault("RPC_ZOO", "https://api.lux.network/mainnet/v1/chain/Z/rpc"),
WLUX: appChainTokens.WLUX,
LETH: appChainTokens.LETH,
LBTC: appChainTokens.LBTC,
LUSDC: appChainTokens.LUSDC,
AMMV2Factory: appChainTokens.AMMV2Factory,
AMMV2Router: appChainTokens.AMMV2Router,
SwapsEnabled: false, // MCOPY opcode not supported
},
"hanzo": {
Name: "Hanzo Chain",
ChainID: big.NewInt(36963),
RPC: envOrDefault("RPC_HANZO", "https://api.lux.network/mainnet/v1/chain/H/rpc"),
WLUX: appChainTokens.WLUX,
LETH: appChainTokens.LETH,
LBTC: appChainTokens.LBTC,
LUSDC: appChainTokens.LUSDC,
AMMV2Factory: appChainTokens.AMMV2Factory,
AMMV2Router: appChainTokens.AMMV2Router,
SwapsEnabled: false,
},
"spc": {
Name: "SPC Chain",
ChainID: big.NewInt(36911),
RPC: envOrDefault("RPC_SPC", "https://api.lux.network/mainnet/v1/chain/S/rpc"),
WLUX: appChainTokens.WLUX,
LETH: appChainTokens.LETH,
LBTC: appChainTokens.LBTC,
LUSDC: appChainTokens.LUSDC,
AMMV2Factory: appChainTokens.AMMV2Factory,
AMMV2Router: appChainTokens.AMMV2Router,
SwapsEnabled: false,
},
"pars": {
Name: "Pars Chain",
ChainID: big.NewInt(494949),
RPC: envOrDefault("RPC_PARS", "https://api.lux.network/mainnet/v1/chain/T/rpc"),
WLUX: appChainTokens.WLUX,
LETH: appChainTokens.LETH,
LBTC: appChainTokens.LBTC,
LUSDC: appChainTokens.LUSDC,
AMMV2Factory: appChainTokens.AMMV2Factory,
AMMV2Router: appChainTokens.AMMV2Router,
SwapsEnabled: false,
},
}
}
// LoadConfig reads configuration from environment variables.
func LoadConfig() (*Config, error) {
mnemonic := os.Getenv("MNEMONIC")
if mnemonic == "" {
return nil, fmt.Errorf("MNEMONIC environment variable is required")
}
cfg := &Config{
Mnemonic: mnemonic,
TradeIntervalMinSec: envIntOrDefault("TRADE_INTERVAL_MIN", 30),
TradeIntervalMaxSec: envIntOrDefault("TRADE_INTERVAL_MAX", 300),
TradeAmountMinETH: envFloatOrDefault("TRADE_AMOUNT_MIN", 0.001),
TradeAmountMaxETH: envFloatOrDefault("TRADE_AMOUNT_MAX", 0.1),
NumWallets: envIntOrDefault("NUM_WALLETS", 10),
WalletStartIndex: envIntOrDefault("WALLET_START_INDEX", 2),
}
// Parse CHAINS env var to select which chains to operate on
chainsEnv := envOrDefault("CHAINS", "lux,zoo,hanzo,spc,pars")
chainNames := strings.Split(chainsEnv, ",")
all := allChains()
for _, name := range chainNames {
name = strings.TrimSpace(strings.ToLower(name))
if cc, ok := all[name]; ok {
cfg.Chains = append(cfg.Chains, cc)
} else {
return nil, fmt.Errorf("unknown chain: %s (valid: lux,zoo,hanzo,spc,pars)", name)
}
}
if len(cfg.Chains) == 0 {
return nil, fmt.Errorf("no chains configured")
}
return cfg, nil
}