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

aaabbb_test.gno

4.79 Kb · 127 lines
  1package aaabbb
  2
  3import (
  4	"chain"
  5	"strings"
  6	"testing"
  7
  8	"gno.land/p/nt/testutils/v0"
  9	"gno.land/p/nt/uassert/v0"
 10	"gno.land/r/moul/x/pairreg/v0"
 11	"gno.land/r/moul/x/pairs/aaa/v0"
 12	"gno.land/r/moul/x/pairs/bbb/v0"
 13)
 14
 15const selfPath = "gno.land/r/moul/x/pairs/aaabbb/v0"
 16
 17func self() address { return chain.PackageAddress(selfPath) }
 18
 19// fund gives addr both tokens and approves this instance to spend them.
 20func fund(cur realm, addr address) {
 21	testing.SetRealm(testing.NewUserRealm(addr))
 22	aaa.Faucet(cross(cur))
 23	bbb.Faucet(cross(cur))
 24	aaa.Approve(cross(cur), self(), 1_000_000_000)
 25	bbb.Approve(cross(cur), self(), 1_000_000_000)
 26}
 27
 28// Deploying the instance is the whole "factory call": its init builds the pair
 29// and announces it, so one addpkg both creates and lists it.
 30func TestSelfRegisteredAtInit(t *testing.T) {
 31	e := pairreg.Get(selfPath)
 32	uassert.True(t, e != nil, "instance must register itself in init")
 33	uassert.Equal(t, selfPath, e.Path)
 34	uassert.Equal(t, 1, pairreg.Size())
 35
 36	keyA, keyB := Keys()
 37	uassert.Equal(t, "gno.land/r/moul/x/pairs/aaa/v0.AAA", keyA)
 38	uassert.Equal(t, "gno.land/r/moul/x/pairs/bbb/v0.BBB", keyB)
 39}
 40
 41// The registry holds a live pointer into this realm's storage, so its own
 42// Render shows state it never copied. This is the property that lets one
 43// qrender describe a thousand instances.
 44func TestRegistrySeesLiveState(cur realm, t *testing.T) {
 45	alice := testutils.TestAddress("alice-live")
 46	fund(cur, alice)
 47	AddLiquidity(cross(cur), 1_000_000, 4_000_000)
 48
 49	e := pairreg.Get(selfPath)
 50	resA, resB := e.Pair.Reserves()
 51	uassert.Equal(t, int64(1_000_000), resA)
 52	uassert.Equal(t, int64(4_000_000), resB)
 53
 54	index := pairreg.Render("")
 55	uassert.True(t, strings.Contains(index, "AAA/BBB"), "index lists the couple")
 56	uassert.True(t, strings.Contains(index, "1000000 AAA"), "index shows live reserves")
 57	uassert.True(t, strings.Contains(index, "moul/x/pairs/aaabbb/v0"), "index links the instance")
 58
 59	// Wind the pair back down so the next test starts from an empty pool.
 60	RemoveLiquidity(cross(cur), TotalShares())
 61}
 62
 63// Full lifecycle through the instance's own entry points, which is also the
 64// proof that funds move to and from THIS realm's address while every line of
 65// logic runs in the shared pure package.
 66func TestLifecycle(cur realm, t *testing.T) {
 67	alice := testutils.TestAddress("alice-cycle")
 68	fund(cur, alice)
 69
 70	minted := AddLiquidity(cross(cur), 1_000_000, 4_000_000)
 71	uassert.Equal(t, int64(1_000_000), minted, "first deposit mints amountA, no sqrt")
 72	uassert.Equal(t, int64(1_000_000), aaa.BalanceOf(self()), "tokens land on the instance")
 73	uassert.Equal(t, int64(4_000_000), bbb.BalanceOf(self()))
 74	uassert.Equal(t, minted, SharesOf(alice))
 75
 76	quoted := Quote("gno.land/r/moul/x/pairs/aaa/v0.AAA", 100_000)
 77	out := Swap(cross(cur), "gno.land/r/moul/x/pairs/aaa/v0.AAA", 100_000, 362_000)
 78	uassert.Equal(t, int64(362_644), out, "same pricing as the single-realm design")
 79	uassert.Equal(t, quoted, out, "Quote matches what Swap pays")
 80
 81	resA, resB := Reserves()
 82	uassert.Equal(t, int64(1_100_000), resA)
 83	uassert.Equal(t, int64(3_637_356), resB)
 84
 85	gotA, gotB := RemoveLiquidity(cross(cur), 500_000)
 86	uassert.Equal(t, int64(550_000), gotA)
 87	uassert.Equal(t, int64(1_818_678), gotB)
 88
 89	// The last provider out is paid the whole reserve, so nothing is stranded.
 90	gotA, gotB = RemoveLiquidity(cross(cur), TotalShares())
 91	uassert.Equal(t, int64(550_000), gotA)
 92	uassert.Equal(t, int64(1_818_678), gotB)
 93	uassert.Equal(t, int64(0), TotalShares())
 94	uassert.Equal(t, int64(0), aaa.BalanceOf(self()), "instance holds nothing afterwards")
 95	uassert.Equal(t, int64(0), bbb.BalanceOf(self()))
 96}
 97
 98// A token this pair does not hold must be refused by name, not silently
 99// treated as one of the two sides. Asserted through Quote, which reaches the
100// same guard without a crossing call: a panic raised inside a crossing call
101// unwinds the whole transaction and is not recoverable by the caller.
102func TestRejectsForeignToken(cur realm, t *testing.T) {
103	alice := testutils.TestAddress("alice-foreign")
104	fund(cur, alice)
105	AddLiquidity(cross(cur), 1_000_000, 4_000_000)
106
107	uassert.PanicsWithMessage(t, cur, "pair: gno.land/r/nope/v0.NOPE is not in this pair", func() {
108		Quote("gno.land/r/nope/v0.NOPE", 1_000)
109	})
110	RemoveLiquidity(cross(cur), TotalShares())
111}
112
113func TestRender(cur realm, t *testing.T) {
114	alice := testutils.TestAddress("alice-render")
115	fund(cur, alice)
116
117	empty := Render("")
118	uassert.True(t, strings.Contains(empty, "# AAA / BBB"))
119	uassert.True(t, strings.Contains(empty, "Empty"), "empty pair says so")
120
121	AddLiquidity(cross(cur), 1_000_000, 4_000_000)
122	full := Render("")
123	uassert.True(t, strings.Contains(full, "| AAA | 1000000 |"), "reserves row")
124	uassert.True(t, strings.Contains(full, "LP shares: **1000000**"))
125	uassert.True(t, strings.Contains(full, "1 AAA buys ~4 BBB"), "spot line")
126	RemoveLiquidity(cross(cur), TotalShares())
127}