Files
maker/coherence_test.go
zeekayandHanzo Dev 1b2589e91f test: follow the grid conversions to their one home
priceToGrid/gridToPrice were two of the five copies grid.go replaced.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-29 22:41:04 -07:00

204 lines
6.7 KiB
Go

// Copyright (C) 2019-2026, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package maker
import (
"context"
"math/big"
"testing"
"github.com/luxfi/geth/common"
)
func TestPriceGridRoundTrip(t *testing.T) {
cases := []struct {
price float64
grid uint64
}{
{1.0, 100_000_000},
{3500.25, 350_025_000_000},
{0.004, 400_000},
{59507.75, 5_950_775_000_000},
}
for _, c := range cases {
if g := GridOf(c.price); g != c.grid {
t.Errorf("GridOf(%v) = %d, want %d", c.price, g, c.grid)
}
if p := PriceOfBig(new(big.Int).SetUint64(c.grid)); !nearly(p, c.price, 1e-6) {
t.Errorf("PriceOfBig(%d) = %v, want %v", c.grid, p, c.price)
}
}
if GridOf(0) != 0 || GridOf(-5) != 0 {
t.Error("non-positive price must grid to 0")
}
if PriceOfBig(big.NewInt(0)) != 0 {
t.Error("zero grid must price to 0")
}
}
func TestParseCoherenceMarkets(t *testing.T) {
eth := "0x0000000000000000000000000000000000000011"
btc := "0x0000000000000000000000000000000000000012"
lux := "0x0000000000000000000000000000000000000013"
usd := "0x00000000000000000000000000000000000000ff"
wlux := "0x00000000000000000000000000000000000000ee"
spec := "ETH/USD=ETHUSD:" + eth + ":" + usd +
",BTC/USD=XBTUSD:" + btc + ":" + usd + ":50000" +
",LUX/USD=fixed:12.5:" + lux + ":" + usd +
",LETH/LUX=ratio:ETHUSD/12.5:" + eth + ":" + wlux
markets, kraken, fixed, ratio, err := ParseCoherenceMarkets(spec)
if err != nil {
t.Fatalf("parse: %v", err)
}
if len(markets) != 4 {
t.Fatalf("got %d markets, want 4", len(markets))
}
if markets[0].Symbol != "ETH/USD" || markets[0].KrakenPair != "ETHUSD" ||
markets[0].Base != common.HexToAddress(eth) || markets[0].Quote != common.HexToAddress(usd) {
t.Errorf("ETH market mis-parsed: %+v", markets[0])
}
if markets[1].Size != 50000 {
t.Errorf("BTC size = %d, want 50000", markets[1].Size)
}
if markets[2].KrakenPair != "" || markets[2].Base != common.HexToAddress(lux) {
t.Errorf("LUX market mis-parsed: %+v", markets[2])
}
if markets[3].Symbol != "LETH/LUX" || markets[3].KrakenPair != "" ||
markets[3].Base != common.HexToAddress(eth) || markets[3].Quote != common.HexToAddress(wlux) {
t.Errorf("LETH/LUX market mis-parsed: %+v", markets[3])
}
if kraken["ETH/USD"] != "ETHUSD" || kraken["BTC/USD"] != "XBTUSD" {
t.Errorf("kraken pairs wrong: %v", kraken)
}
if fixed["LUX/USD"] != 12.5 {
t.Errorf("fixed LUX = %v, want 12.5", fixed["LUX/USD"])
}
if _, ok := fixed["ETH/USD"]; ok {
t.Error("ETH/USD must not be a fixed market")
}
r, ok := ratio["LETH/LUX"]
if !ok || r.Pair != "ETHUSD" || r.Denom != 12.5 {
t.Errorf("ratio LETH/LUX = %+v (ok=%v), want {ETHUSD 12.5}", r, ok)
}
if _, ok := ratio["ETH/USD"]; ok {
t.Error("ETH/USD must not be a ratio market")
}
}
func TestParseCoherenceMarketsErrors(t *testing.T) {
bad := []string{
"",
"ETH/USD", // no '='
"ETH/USD=ETHUSD", // no base/quote
"ETH/USD=ETHUSD:0x0000000000000000000000000000000000000000:0x00000000000000000000000000000000000000ff", // zero base
"LETH/LUX=ratio:ETHUSD:0x00000000000000000000000000000000000000aa:0x00000000000000000000000000000000000000ee", // ratio missing /denom
"LETH/LUX=ratio:ETHUSD/0:0x00000000000000000000000000000000000000aa:0x00000000000000000000000000000000000000ee", // ratio non-positive denom
"LETH/LUX=ratio:ETHUSD/x:0x00000000000000000000000000000000000000aa:0x00000000000000000000000000000000000000ee", // ratio bad denom
}
for _, spec := range bad {
if _, _, _, _, err := ParseCoherenceMarkets(spec); err == nil {
t.Errorf("spec %q: want error, got nil", spec)
}
}
}
func TestCoherenceOracle(t *testing.T) {
o := NewCoherenceOracle(nil, map[string]float64{"LUX/USD": 12.5}, nil, nil)
if p, err := o.GetMidPrice(context.Background(), "LUX/USD"); err != nil || p != 12.5 {
t.Fatalf("fixed LUX/USD = %v, %v; want 12.5, nil", p, err)
}
if _, err := o.GetMidPrice(context.Background(), "UNKNOWN"); err == nil {
t.Error("unknown symbol must error (never a fabricated price)")
}
}
// TestCoherenceOracleRatio verifies the cross-rate source divides the Kraken leg by
// the denom, using a stub feed so the test needs no network.
func TestCoherenceOracleRatio(t *testing.T) {
o := &compositeOracle{
kraken: fixedFeed{"LETH/LUX": 3800}, // stub: Kraken(ETHUSD) leg = 3800
fixed: nil,
ratio: map[string]RatioRef{"LETH/LUX": {Pair: "ETHUSD", Denom: 12.5}},
}
p, err := o.GetMidPrice(context.Background(), "LETH/LUX")
if err != nil || !nearly(p, 3800.0/12.5, 1e-9) {
t.Fatalf("ratio LETH/LUX = %v, %v; want %v, nil", p, err, 3800.0/12.5)
}
// A ratio symbol with no feed must error, never fabricate.
o2 := &compositeOracle{ratio: map[string]RatioRef{"LETH/LUX": {Pair: "ETHUSD", Denom: 12.5}}}
if _, err := o2.GetMidPrice(context.Background(), "LETH/LUX"); err == nil {
t.Error("ratio with no kraken feed must error")
}
}
// fixedFeed is a stub MidProvider returning a constant mid per symbol.
type fixedFeed map[string]float64
func (f fixedFeed) GetMidPrice(_ context.Context, symbol string) (float64, error) {
if p, ok := f[symbol]; ok {
return p, nil
}
return 0, context.Canceled
}
func TestFixedMid(t *testing.T) {
var f FixedMid = 42.5
if p, _ := f.GetMidPrice(context.Background(), "anything"); p != 42.5 {
t.Errorf("FixedMid = %v, want 42.5", p)
}
}
func TestPoolKeyForOrientation(t *testing.T) {
a := common.HexToAddress("0x00000000000000000000000000000000000000aa")
b := common.HexToAddress("0x00000000000000000000000000000000000000bb")
k1 := PoolKeyFor(a, b)
k2 := PoolKeyFor(b, a) // reversed input must yield the same key (currency0 < currency1)
if k1.Currency0.Address != a || k1.Currency1.Address != b {
t.Errorf("orientation wrong: %v", k1)
}
if k1.ID() != k2.ID() {
t.Error("PoolKeyFor must be order-independent")
}
}
func TestDecodeBytes32ArrayU64(t *testing.T) {
// ABI bytes32[] of [3, 7]: offset(0x20), len(2), word(3), word(7).
enc := make([]byte, 32*4)
new(big.Int).SetUint64(0x20).FillBytes(enc[0:32])
new(big.Int).SetUint64(2).FillBytes(enc[32:64])
new(big.Int).SetUint64(3).FillBytes(enc[64:96])
new(big.Int).SetUint64(7).FillBytes(enc[96:128])
ids, err := decodeBytes32ArrayU64(enc)
if err != nil {
t.Fatalf("decode: %v", err)
}
if len(ids) != 2 || ids[0] != 3 || ids[1] != 7 {
t.Errorf("decoded %v, want [3 7]", ids)
}
if got, _ := decodeBytes32ArrayU64(make([]byte, 32)); got != nil {
t.Errorf("short input -> nil, got %v", got)
}
}
func TestDiffNew(t *testing.T) {
before := toSet([]uint64{1, 2, 3})
got := diffNew(before, []uint64{1, 2, 3, 9, 10})
if len(got) != 2 || got[0] != 9 || got[1] != 10 {
t.Errorf("diffNew = %v, want [9 10]", got)
}
}
func nearly(a, b, tol float64) bool {
d := a - b
if d < 0 {
d = -d
}
bb := b
if bb < 0 {
bb = -bb
}
return d <= tol*(1+bb)
}