Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

pair_test.gno

3.27 Kb · 102 lines
  1package pair
  2
  3import (
  4	"testing"
  5
  6	"gno.land/p/nt/uassert/v0"
  7)
  8
  9// The pricing function, on its own, is the part worth pinning numerically: it
 10// is pure, it is where the 128-bit arithmetic lives, and every instance in the
 11// ecosystem runs this exact code.
 12func TestAmountOut(t *testing.T) {
 13	cases := []struct {
 14		name                    string
 15		in, resIn, resOut, want int64
 16	}{
 17		{"balanced pool, small trade", 1_000, 1_000_000, 1_000_000, 996},
 18		{"balanced pool, 1%", 10_000, 1_000_000, 1_000_000, 9_871},
 19		{"balanced pool, 10%", 100_000, 1_000_000, 1_000_000, 90_661},
 20		{"balanced pool, 50%", 500_000, 1_000_000, 1_000_000, 332_665},
 21		{"skewed pool", 100_000, 1_000_000, 4_000_000, 362_644},
 22		{"near drain", 1_000, 1, 1_000_000, 998_997},
 23		{"dust rounds to zero", 1, 1_000_000, 1_000_000, 0},
 24		{"reserves at the cap", MaxReserve / 2, MaxReserve / 2, MaxReserve, 4_604_758_097_518_382},
 25	}
 26	for _, tc := range cases {
 27		got := AmountOut(tc.in, tc.resIn, tc.resOut)
 28		uassert.Equal(t, tc.want, got, tc.name)
 29	}
 30}
 31
 32// The fee is what stays behind: k must never decrease across a swap.
 33func TestConstantProductNeverRegresses(t *testing.T) {
 34	resIn, resOut := int64(1_000_000), int64(4_000_000)
 35	for _, in := range []int64{1, 1_000, 100_000, 999_999} {
 36		out := AmountOut(in, resIn, resOut)
 37		uassert.True(t, cmpProd(resIn+in, resOut-out, resIn, resOut) >= 0,
 38			"k must not regress")
 39	}
 40}
 41
 42func TestAmountOutGuards(t *testing.T) {
 43	mustPanic(t, "pair: amountIn must be > 0", func() {
 44		AmountOut(0, 1_000, 1_000)
 45	})
 46	mustPanic(t, "pair: pair has an empty reserve", func() {
 47		AmountOut(1, 0, 1_000)
 48	})
 49	mustPanic(t, "pair: reserve above cap", func() {
 50		AmountOut(1, MaxReserve+1, 1_000)
 51	})
 52	mustPanic(t, "pair: reserve cap exceeded", func() {
 53		AmountOut(2, MaxReserve-1, 1_000)
 54	})
 55}
 56
 57// mulDiv is the only place a 128-bit intermediate is required, and the only
 58// place bits.Div64 could panic. Both ends are asserted rather than argued.
 59func TestMulDiv(t *testing.T) {
 60	uassert.Equal(t, int64(2), mulDiv(4, 3, 6))
 61	uassert.Equal(t, int64(0), mulDiv(1, 1, 3), "floors")
 62	uassert.Equal(t, int64(4_611_686_018_427_387_903), mulDiv(9_223_372_036_854_775_807, 1, 2),
 63		"operands near MaxInt64 stay exact through the 128-bit path")
 64
 65	mustPanic(t, "pair: division by a non-positive value", func() {
 66		mulDiv(1, 1, 0)
 67	})
 68	mustPanic(t, "pair: quotient overflows int64", func() {
 69		mulDiv(9_223_372_036_854_775_807, 9_223_372_036_854_775_807, 1)
 70	})
 71}
 72
 73func TestCmpProd(t *testing.T) {
 74	uassert.Equal(t, 0, cmpProd(6, 7, 42, 1))
 75	uassert.Equal(t, -1, cmpProd(6, 6, 42, 1))
 76	uassert.Equal(t, 1, cmpProd(7, 7, 42, 1))
 77	// Both products exceed int64 and differ only in the low word.
 78	uassert.Equal(t, -1, cmpProd(4_000_000_000, 4_000_000_000, 4_000_000_000, 4_000_000_001))
 79}
 80
 81// mustPanic is uassert.PanicsWithMessage without the realm argument: a pure
 82// package cannot produce a `cur` to hand it, since it can never declare a
 83// crossing function in the first place.
 84func mustPanic(t *testing.T, want string, f func()) {
 85	t.Helper()
 86	defer func() {
 87		r := recover()
 88		if r == nil {
 89			t.Errorf("expected panic %q, got none", want)
 90			return
 91		}
 92		got, ok := r.(string)
 93		if !ok {
 94			t.Errorf("expected a string panic %q, got %v", want, r)
 95			return
 96		}
 97		if got != want {
 98			t.Errorf("expected panic %q, got %q", want, got)
 99		}
100	}()
101	f()
102}