Search Apps Documentation Source Content File Folder Download Copy Actions Download

treasury_test.gno

9.01 Kb ยท 332 lines
  1package test
  2
  3import (
  4	"chain"
  5	"chain/banker"
  6	"chain/runtime"
  7	"sort"
  8	"strings"
  9	"testing"
 10
 11	"gno.land/p/demo/tokens/grc20"
 12	"gno.land/p/nt/fqname"
 13	"gno.land/p/nt/testutils"
 14	trs_pkg "gno.land/p/nt/treasury"
 15	"gno.land/p/nt/uassert"
 16
 17	"gno.land/r/demo/defi/grc20reg"
 18	"gno.land/r/gov/dao"
 19	"gno.land/r/gov/dao/v3/impl"
 20	"gno.land/r/gov/dao/v3/treasury"
 21)
 22
 23var (
 24	user1Addr       = testutils.TestAddress("g1user1")
 25	user2Addr       = testutils.TestAddress("g1user2")
 26	treasuryAddr    = chain.PackageAddress("gno.land/r/gov/dao/v3/treasury")
 27	allowedRealm    = testing.NewCodeRealm("gno.land/r/test/allowed")
 28	notAllowedRealm = testing.NewCodeRealm("gno.land/r/test/notallowed")
 29	mintAmount      = int64(1000)
 30)
 31
 32// Define a dummy trs_pkg.Payment type for testing purposes.
 33type dummyPayment struct {
 34	bankerID string
 35	str      string
 36}
 37
 38var _ trs_pkg.Payment = (*dummyPayment)(nil)
 39
 40func (dp *dummyPayment) BankerID() string { return dp.bankerID }
 41func (dp *dummyPayment) String() string   { return dp.str }
 42
 43func init() {
 44	// Register allowed Realm path.
 45	dao.UpdateImpl(cross, dao.UpdateRequest{
 46		DAO:         impl.NewGovDAO(),
 47		AllowedDAOs: []string{allowedRealm.PkgPath()},
 48	})
 49}
 50
 51func ugnotCoins(t *testing.T, amount int64) chain.Coins {
 52	t.Helper()
 53
 54	// Create a new coin with the ugnot denomination.
 55	return chain.NewCoins(chain.NewCoin("ugnot", amount))
 56}
 57
 58func ugnotBalance(t *testing.T, addr address) int64 {
 59	t.Helper()
 60
 61	// Get the balance of ugnot coins for the given address.
 62	banker_ := banker.NewBanker(banker.BankerTypeReadonly)
 63	coins := banker_.GetCoins(addr)
 64
 65	return coins.AmountOf("ugnot")
 66}
 67
 68// Define a keyedToken type to hold the token and its key.
 69type keyedToken struct {
 70	key   string
 71	token *grc20.Token
 72}
 73
 74func registerGRC20Tokens(t *testing.T, tokenNames []string, toMint address) []keyedToken {
 75	t.Helper()
 76
 77	var (
 78		keyedTokens = make([]keyedToken, 0, len(tokenNames))
 79		keys        = make([]string, 0, len(tokenNames))
 80	)
 81
 82	for _, name := range tokenNames {
 83		// Create the token.
 84		symbol := strings.ToUpper(name)
 85		token, ledger := grc20.NewToken(name, symbol, 0)
 86
 87		// Register the token.
 88		grc20reg.Register(cross, token, symbol)
 89
 90		// Mint tokens to the specified address.
 91		ledger.Mint(toMint, mintAmount)
 92
 93		// Add the token and key to the lists.
 94		key := fqname.Construct(runtime.CurrentRealm().PkgPath(), symbol)
 95		keyedTokens = append(keyedTokens, keyedToken{key: key, token: token})
 96		keys = append(keys, key)
 97	}
 98
 99	// Set the token keys in the treasury.
100	treasury.SetTokenKeys(cross, keys)
101
102	return keyedTokens
103}
104
105func TestAllowedDAOs(t *testing.T) {
106	// Set the current Realm to the not allowed one.
107	testing.SetRealm(notAllowedRealm)
108
109	// Define a dummy payment to test sending.
110	dummyP := &dummyPayment{bankerID: "Dummy"}
111
112	// Try to send, it should abort because the Realm is not allowed.
113	uassert.AbortsWithMessage(
114		t,
115		"this Realm is not allowed to send payment: "+notAllowedRealm.PkgPath(),
116		func() { treasury.Send(cross, dummyP) },
117	)
118
119	// Set the current Realm to the allowed one.
120	testing.SetRealm(allowedRealm)
121
122	// Try to send, it should not abort because the Realm is allowed,
123	// but because the dummy banker ID is not registered.
124	uassert.AbortsWithMessage(
125		t,
126		"banker not found: "+dummyP.BankerID(),
127		func() { treasury.Send(cross, dummyP) },
128	)
129}
130
131func TestRegisteredBankers(t *testing.T) {
132	// Set the current Realm to the allowed one.
133	testing.SetRealm(allowedRealm)
134
135	// Define the expected banker IDs.
136	expectedBankerIDs := []string{
137		trs_pkg.CoinsBanker{}.ID(),
138		trs_pkg.GRC20Banker{}.ID(),
139	}
140
141	// Get the registered bankers from the treasury and compare their lengths.
142	registeredBankerIDs := treasury.ListBankerIDs()
143	uassert.Equal(t, len(registeredBankerIDs), len(expectedBankerIDs))
144
145	// Sort both slices then compare them.
146	sort.StringSlice(expectedBankerIDs).Sort()
147	sort.StringSlice(registeredBankerIDs).Sort()
148
149	for i := range expectedBankerIDs {
150		uassert.Equal(t, expectedBankerIDs[i], registeredBankerIDs[i])
151	}
152
153	// Test HasBanker method.
154	for _, bankerID := range expectedBankerIDs {
155		uassert.True(t, treasury.HasBanker(bankerID))
156	}
157	uassert.False(t, treasury.HasBanker("UnknownBankerID"))
158
159	// Test Address method.
160	for _, bankerID := range expectedBankerIDs {
161		// The two bankers used for now should have the treasury Realm address.
162		uassert.Equal(t, treasury.Address(bankerID), treasuryAddr.String())
163	}
164}
165
166func TestSendGRC20Payment(t *testing.T) {
167	// Set the current Realm to the allowed one.
168	testing.SetRealm(allowedRealm)
169
170	// Try to send a GRC20 payment with a not registered token, it should abort.
171	uassert.AbortsWithMessage(
172		t,
173		"failed to send payment: GRC20 token not found: UNKNOW",
174		func() {
175			treasury.Send(cross, trs_pkg.NewGRC20Payment("UNKNOW", 100, user1Addr))
176		},
177	)
178
179	// Create 3 GRC20 tokens and register them.
180	keyedTokens := registerGRC20Tokens(
181		t,
182		[]string{"TestToken0", "TestToken1", "TestToken2"},
183		treasuryAddr,
184	)
185
186	const txAmount = 42
187
188	// For each token-user pair.
189	for i, userAddr := range []address{user1Addr, user2Addr} {
190		for _, keyed := range keyedTokens {
191			// Check that the treasury has the expected balance before sending.
192			uassert.Equal(t, keyed.token.BalanceOf(treasuryAddr), mintAmount-int64(txAmount*i))
193
194			// Check that the user has no balance before sending.
195			uassert.Equal(t, keyed.token.BalanceOf(userAddr), int64(0))
196
197			// Try to send a GRC20 payment with a registered token, it should not abort.
198			uassert.NotAborts(t, func() {
199				treasury.Send(
200					cross,
201					trs_pkg.NewGRC20Payment(
202						keyed.key,
203						txAmount,
204						userAddr,
205					),
206				)
207			})
208
209			// Check that the user has the expected balance after sending.
210			uassert.Equal(t, keyed.token.BalanceOf(userAddr), int64(txAmount))
211
212			// Check that the treasury has the expected balance after sending.
213			uassert.Equal(t, keyed.token.BalanceOf(treasuryAddr), mintAmount-int64(txAmount*(i+1)))
214		}
215	}
216
217	// Get the GRC20Banker ID.
218	grc20BankerID := trs_pkg.GRC20Banker{}.ID()
219
220	// Test Balances method for the GRC20Banker.
221	balances := treasury.Balances(grc20BankerID)
222	uassert.Equal(t, len(balances), len(keyedTokens))
223
224	compared := 0
225	for _, balance := range balances {
226		for _, keyed := range keyedTokens {
227			if balance.Denom == keyed.key {
228				uassert.Equal(t, balance.Amount, keyed.token.BalanceOf(treasuryAddr))
229				compared++
230			}
231		}
232	}
233	uassert.Equal(t, compared, len(keyedTokens))
234
235	// Check the history of the GRC20Banker.
236	history := treasury.History(grc20BankerID, 1, 10)
237	uassert.Equal(t, len(history), 6)
238
239	// Try to send a dummy payment with the GRC20 banker ID, it should abort.
240	uassert.AbortsWithMessage(
241		t,
242		"failed to send payment: invalid payment type",
243		func() {
244			treasury.Send(cross, &dummyPayment{bankerID: grc20BankerID})
245		},
246	)
247
248	// Try to send a GRC20 payment without enough balance, it should abort.
249	uassert.AbortsWithMessage(
250		t,
251		"failed to send payment: insufficient balance",
252		func() {
253			treasury.Send(
254				cross,
255				trs_pkg.NewGRC20Payment(
256					keyedTokens[0].key,
257					mintAmount*42, // Try to send more than the treasury has.
258					user1Addr,
259				),
260			)
261		},
262	)
263
264	// Check the history of the GRC20Banker.
265	history = treasury.History(grc20BankerID, 1, 10)
266	uassert.Equal(t, len(history), 6)
267}
268
269func TestSendCoinPayment(t *testing.T) {
270	// Set the current Realm to the allowed one.
271	testing.SetRealm(allowedRealm)
272
273	// Issue initial ugnot coins to the treasury address.
274	testing.IssueCoins(treasuryAddr, ugnotCoins(t, mintAmount))
275
276	// Get the CoinsBanker ID.
277	bankerID := trs_pkg.CoinsBanker{}.ID()
278
279	// Define helper function to check balances and history.
280	var (
281		expectedTreasuryBalance = mintAmount
282		expectedUser1Balance    = int64(0)
283		expectedUser2Balance    = int64(0)
284		expectedHistoryLen      = 0
285		checkHistoryAndBalances = func() {
286			t.Helper()
287
288			uassert.Equal(t, ugnotBalance(t, treasuryAddr), expectedTreasuryBalance)
289			uassert.Equal(t, ugnotBalance(t, user1Addr), expectedUser1Balance)
290			uassert.Equal(t, ugnotBalance(t, user2Addr), expectedUser2Balance)
291
292			// Check treasury.Balances returned value.
293			balances := treasury.Balances(bankerID)
294			uassert.Equal(t, len(balances), 1)
295			uassert.Equal(t, balances[0].Denom, "ugnot")
296			uassert.Equal(t, balances[0].Amount, expectedTreasuryBalance)
297
298			// Check treasury.History returned value.
299			history := treasury.History(bankerID, 1, expectedHistoryLen+1)
300			uassert.Equal(t, len(history), expectedHistoryLen)
301		}
302	)
303
304	// Check initial balances and history.
305	checkHistoryAndBalances()
306
307	const txAmount = int64(42)
308
309	// Treasury send coins.
310	for i := int64(0); i < 3; i++ {
311		// Send ugnot coins to user1 and user2.
312		uassert.NotAborts(t, func() {
313			treasury.Send(
314				cross,
315				trs_pkg.NewCoinsPayment(ugnotCoins(t, txAmount), user1Addr),
316			)
317			treasury.Send(
318				cross,
319				trs_pkg.NewCoinsPayment(ugnotCoins(t, txAmount), user2Addr),
320			)
321		})
322
323		// Update expected balances and history length.
324		expectedTreasuryBalance = mintAmount - txAmount*2*(i+1)
325		expectedUser1Balance = txAmount * (i + 1)
326		expectedUser2Balance = expectedUser1Balance
327		expectedHistoryLen = int(2 * (i + 1))
328
329		// Check balances and history after sending.
330		checkHistoryAndBalances()
331	}
332}