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.

operator_faucet_test.gno

16.32 Kb · 525 lines
  1package admin
  2
  3import (
  4	"testing"
  5
  6	"chain"
  7	"chain/banker"
  8	"chain/runtime"
  9
 10	"gno.land/p/nt/uassert/v0"
 11)
 12
 13const (
 14	testFaucetFundAmount int64 = 1000000
 15)
 16
 17type faucetTestHelper struct{}
 18
 19var faucetTest = &faucetTestHelper{}
 20
 21func TestSetFaucetTargetAmount(cur realm, t *testing.T) {
 22	tests := []struct {
 23		name         string
 24		originCaller address
 25		realm        runtime.Realm
 26		amount       int64
 27		shouldPanic  bool
 28		panicMsg     string
 29	}{
 30		{"admin sets target amount", testAdmin, testing.NewUserRealm(testAdmin), 1000000, false, ""},
 31		{"unauthorized", unauthorizedUser, testing.NewUserRealm(unauthorizedUser), 1000000, true, "admin access required"},
 32		{"zero amount", testAdmin, testing.NewUserRealm(testAdmin), 0, true, "faucet target amount must be positive"},
 33		{"negative amount", testAdmin, testing.NewUserRealm(testAdmin), -1, true, "faucet target amount must be positive"},
 34	}
 35
 36	for _, tt := range tests {
 37		setupCommonAdminTests(cur, t)
 38		testing.SetOriginCaller(tt.originCaller)
 39		testing.SetRealm(tt.realm)
 40
 41		if tt.shouldPanic {
 42			uassert.AbortsWithMessage(t, cur, tt.panicMsg, func() {
 43				SetFaucetTargetAmount(cross(cur), tt.amount)
 44			})
 45			uassert.Equal(t, int64(0), GetFaucetTargetAmount())
 46			continue
 47		}
 48
 49		SetFaucetTargetAmount(cross(cur), tt.amount)
 50		uassert.Equal(t, tt.amount, GetFaucetTargetAmount())
 51	}
 52}
 53
 54func TestRequestFaucetTransfersDeficit(cur realm, t *testing.T) {
 55	setupCommonAdminTests(cur, t)
 56
 57	adminRealm := adminRealmAddr()
 58	adminBefore := adminUgnotBalance(adminRealm)
 59	operatorBefore := adminUgnotBalance(testOperator)
 60	expectedTransfer := int64(300000)
 61	faucetTest.FundAdmin(testFaucetFundAmount)
 62
 63	testing.SetOriginCaller(testAdmin)
 64	testing.SetRealm(testing.NewUserRealm(testAdmin))
 65	SetOperator(cross(cur), testOperator)
 66	SetFaucetTargetAmount(cross(cur), operatorBefore+expectedTransfer)
 67
 68	testing.SetOriginCaller(testOperator)
 69	testing.SetRealm(testing.NewUserRealm(testOperator))
 70	transferred := RequestFaucet(cross(cur))
 71
 72	uassert.Equal(t, expectedTransfer, transferred)
 73	uassert.Equal(t, operatorBefore+expectedTransfer, adminUgnotBalance(testOperator))
 74	uassert.Equal(t, adminBefore+testFaucetFundAmount-expectedTransfer, adminUgnotBalance(adminRealm))
 75}
 76
 77func TestRequestFaucetForTransfersDeficit(cur realm, t *testing.T) {
 78	setupCommonAdminTests(cur, t)
 79
 80	adminRealm := adminRealmAddr()
 81	adminBefore := adminUgnotBalance(adminRealm)
 82	operatorBefore := adminUgnotBalance(testOperator)
 83	expectedTransfer := int64(300000)
 84	faucetTest.FundAdmin(testFaucetFundAmount)
 85
 86	testing.SetOriginCaller(testAdmin)
 87	testing.SetRealm(testing.NewUserRealm(testAdmin))
 88	SetOperator(cross(cur), testOperator)
 89	SetFaucetTargetAmount(cross(cur), operatorBefore+expectedTransfer)
 90	transferred := RequestFaucetFor(cross(cur), testOperator)
 91
 92	uassert.Equal(t, expectedTransfer, transferred)
 93	uassert.Equal(t, operatorBefore+expectedTransfer, adminUgnotBalance(testOperator))
 94	uassert.Equal(t, adminBefore+testFaucetFundAmount-expectedTransfer, adminUgnotBalance(adminRealm))
 95}
 96
 97func TestRequestFaucetsTransfersDeficits(cur realm, t *testing.T) {
 98	setupCommonAdminTests(cur, t)
 99
100	testing.SetOriginCaller(testAdmin)
101	testing.SetRealm(testing.NewUserRealm(testAdmin))
102	UnsetOperator(cross(cur), testAdmin)
103	SetOperator(cross(cur), testOperator)
104	SetOperator(cross(cur), testUser)
105	SetOperator(cross(cur), testUser2)
106
107	adminRealm := adminRealmAddr()
108	adminBefore := adminUgnotBalance(adminRealm)
109	operatorBefore := adminUgnotBalance(testOperator)
110	userBefore := adminUgnotBalance(testUser)
111	user2Before := adminUgnotBalance(testUser2)
112	targetAmount := maxInt64(operatorBefore, maxInt64(userBefore, user2Before)) + 300000
113	if user2Before < targetAmount {
114		faucetTest.Issue(testUser2, targetAmount-user2Before)
115		user2Before = targetAmount
116	}
117	operatorDeficit := targetAmount - operatorBefore
118	userDeficit := targetAmount - userBefore
119	expectedTransfer := operatorDeficit + userDeficit
120	faucetTest.FundAdmin(expectedTransfer)
121
122	testing.SetOriginCaller(testAdmin)
123	testing.SetRealm(testing.NewUserRealm(testAdmin))
124	SetFaucetTargetAmount(cross(cur), targetAmount)
125	transferred := RequestFaucets(cross(cur))
126
127	uassert.Equal(t, expectedTransfer, transferred)
128	uassert.Equal(t, targetAmount, adminUgnotBalance(testOperator))
129	uassert.Equal(t, targetAmount, adminUgnotBalance(testUser))
130	uassert.Equal(t, user2Before, adminUgnotBalance(testUser2))
131	uassert.Equal(t, adminBefore, adminUgnotBalance(adminRealm))
132}
133
134func TestRequestFaucetsReturnsZeroWithoutDeficits(cur realm, t *testing.T) {
135	setupCommonAdminTests(cur, t)
136
137	testing.SetOriginCaller(testAdmin)
138	testing.SetRealm(testing.NewUserRealm(testAdmin))
139	UnsetOperator(cross(cur), testAdmin)
140	SetOperator(cross(cur), testOperator)
141	SetOperator(cross(cur), testUser)
142
143	operatorBefore := adminUgnotBalance(testOperator)
144	userBefore := adminUgnotBalance(testUser)
145	targetAmount := maxInt64(operatorBefore, userBefore)
146	if operatorBefore < targetAmount {
147		faucetTest.Issue(testOperator, targetAmount-operatorBefore)
148	}
149	if userBefore < targetAmount {
150		faucetTest.Issue(testUser, targetAmount-userBefore)
151	}
152	if targetAmount <= 0 {
153		targetAmount = 1
154		faucetTest.Issue(testOperator, targetAmount)
155		faucetTest.Issue(testUser, targetAmount)
156	}
157
158	adminBefore := adminUgnotBalance(adminRealmAddr())
159	testing.SetOriginCaller(testAdmin)
160	testing.SetRealm(testing.NewUserRealm(testAdmin))
161	SetFaucetTargetAmount(cross(cur), targetAmount)
162	transferred := RequestFaucets(cross(cur))
163
164	uassert.Equal(t, int64(0), transferred)
165	uassert.Equal(t, adminBefore, adminUgnotBalance(adminRealmAddr()))
166}
167
168func TestRequestFaucetRejectsInvalidRequest(cur realm, t *testing.T) {
169	tests := []struct {
170		name     string
171		setup    func(cur realm) (address, runtime.Realm)
172		panicMsg string
173	}{
174		{
175			name: "non-operator direct caller",
176			setup: func(cur realm) (address, runtime.Realm) {
177				testing.SetOriginCaller(testAdmin)
178				testing.SetRealm(testing.NewUserRealm(testAdmin))
179				SetFaucetTargetAmount(cross(cur), 1000000)
180				faucetTest.FundAdmin(testFaucetFundAmount)
181				return unauthorizedUser, testing.NewUserRealm(unauthorizedUser)
182			},
183			panicMsg: "operator access required",
184		},
185		{
186			name: "operator balance already reaches target",
187			setup: func(cur realm) (address, runtime.Realm) {
188				operatorBalance := adminUgnotBalance(testOperator)
189				if operatorBalance == 0 {
190					faucetTest.Issue(testOperator, 1)
191					operatorBalance = 1
192				}
193				testing.SetOriginCaller(testAdmin)
194				testing.SetRealm(testing.NewUserRealm(testAdmin))
195				SetOperator(cross(cur), testOperator)
196				SetFaucetTargetAmount(cross(cur), operatorBalance)
197				faucetTest.FundAdmin(testFaucetFundAmount)
198				return testOperator, testing.NewUserRealm(testOperator)
199			},
200			panicMsg: "operator balance is not below faucet target",
201		},
202		{
203			name: "admin realm balance is insufficient",
204			setup: func(cur realm) (address, runtime.Realm) {
205				adminBalance := adminUgnotBalance(adminRealmAddr())
206				testing.SetOriginCaller(testAdmin)
207				testing.SetRealm(testing.NewUserRealm(testAdmin))
208				SetOperator(cross(cur), testOperator)
209				SetFaucetTargetAmount(cross(cur), adminUgnotBalance(testOperator)+adminBalance+1)
210				return testOperator, testing.NewUserRealm(testOperator)
211			},
212			panicMsg: "admin realm balance is insufficient",
213		},
214		{
215			name: "operator origin through code realm",
216			setup: func(cur realm) (address, runtime.Realm) {
217				testing.SetOriginCaller(testAdmin)
218				testing.SetRealm(testing.NewUserRealm(testAdmin))
219				SetOperator(cross(cur), testOperator)
220				SetFaucetTargetAmount(cross(cur), adminUgnotBalance(testOperator)+1)
221				faucetTest.FundAdmin(testFaucetFundAmount)
222				return testOperator, testing.NewCodeRealm("gno.land/r/akkadia/beta/operator_faucet_relay")
223			},
224			panicMsg: "contract calls not allowed: function must be called directly by user",
225		},
226		{
227			name: "target amount is not configured",
228			setup: func(cur realm) (address, runtime.Realm) {
229				testing.SetOriginCaller(testAdmin)
230				testing.SetRealm(testing.NewUserRealm(testAdmin))
231				SetOperator(cross(cur), testOperator)
232				faucetTest.FundAdmin(testFaucetFundAmount)
233				return testOperator, testing.NewUserRealm(testOperator)
234			},
235			panicMsg: "faucet target amount must be positive",
236		},
237	}
238
239	for _, tt := range tests {
240		setupCommonAdminTests(cur, t)
241		caller, rlm := tt.setup(cur)
242		testing.SetOriginCaller(caller)
243		testing.SetRealm(rlm)
244
245		uassert.AbortsWithMessage(t, cur, tt.panicMsg, func() {
246			RequestFaucet(cross(cur))
247		})
248	}
249}
250
251func TestRequestFaucetForRejectsInvalidRequest(cur realm, t *testing.T) {
252	tests := []struct {
253		name     string
254		setup    func(cur realm) (address, runtime.Realm, address)
255		panicMsg string
256	}{
257		{
258			name: "non-admin caller",
259			setup: func(cur realm) (address, runtime.Realm, address) {
260				testing.SetOriginCaller(testAdmin)
261				testing.SetRealm(testing.NewUserRealm(testAdmin))
262				SetOperator(cross(cur), testOperator)
263				SetFaucetTargetAmount(cross(cur), 1000000)
264				faucetTest.FundAdmin(testFaucetFundAmount)
265				return unauthorizedUser, testing.NewUserRealm(unauthorizedUser), testOperator
266			},
267			panicMsg: "admin access required",
268		},
269		{
270			name: "non-operator target",
271			setup: func(cur realm) (address, runtime.Realm, address) {
272				testing.SetOriginCaller(testAdmin)
273				testing.SetRealm(testing.NewUserRealm(testAdmin))
274				SetFaucetTargetAmount(cross(cur), 1000000)
275				faucetTest.FundAdmin(testFaucetFundAmount)
276				return testAdmin, testing.NewUserRealm(testAdmin), unauthorizedUser
277			},
278			panicMsg: "operator access required",
279		},
280		{
281			name: "operator balance already reaches target",
282			setup: func(cur realm) (address, runtime.Realm, address) {
283				operatorBalance := adminUgnotBalance(testOperator)
284				if operatorBalance == 0 {
285					faucetTest.Issue(testOperator, 1)
286					operatorBalance = 1
287				}
288				testing.SetOriginCaller(testAdmin)
289				testing.SetRealm(testing.NewUserRealm(testAdmin))
290				SetOperator(cross(cur), testOperator)
291				SetFaucetTargetAmount(cross(cur), operatorBalance)
292				faucetTest.FundAdmin(testFaucetFundAmount)
293				return testAdmin, testing.NewUserRealm(testAdmin), testOperator
294			},
295			panicMsg: "operator balance is not below faucet target",
296		},
297		{
298			name: "admin realm balance is insufficient",
299			setup: func(cur realm) (address, runtime.Realm, address) {
300				adminBalance := adminUgnotBalance(adminRealmAddr())
301				testing.SetOriginCaller(testAdmin)
302				testing.SetRealm(testing.NewUserRealm(testAdmin))
303				SetOperator(cross(cur), testOperator)
304				SetFaucetTargetAmount(cross(cur), adminUgnotBalance(testOperator)+adminBalance+1)
305				return testAdmin, testing.NewUserRealm(testAdmin), testOperator
306			},
307			panicMsg: "admin realm balance is insufficient",
308		},
309		{
310			name: "target amount is not configured",
311			setup: func(cur realm) (address, runtime.Realm, address) {
312				testing.SetOriginCaller(testAdmin)
313				testing.SetRealm(testing.NewUserRealm(testAdmin))
314				SetOperator(cross(cur), testOperator)
315				faucetTest.FundAdmin(testFaucetFundAmount)
316				return testAdmin, testing.NewUserRealm(testAdmin), testOperator
317			},
318			panicMsg: "faucet target amount must be positive",
319		},
320	}
321
322	for _, tt := range tests {
323		setupCommonAdminTests(cur, t)
324		caller, rlm, operator := tt.setup(cur)
325		testing.SetOriginCaller(caller)
326		testing.SetRealm(rlm)
327
328		uassert.AbortsWithMessage(t, cur, tt.panicMsg, func() {
329			RequestFaucetFor(cross(cur), operator)
330		})
331	}
332}
333
334func TestRequestFaucetsRejectsInvalidRequest(cur realm, t *testing.T) {
335	tests := []struct {
336		name     string
337		setup    func(cur realm) (address, runtime.Realm)
338		panicMsg string
339	}{
340		{
341			name: "non-admin caller",
342			setup: func(cur realm) (address, runtime.Realm) {
343				testing.SetOriginCaller(testAdmin)
344				testing.SetRealm(testing.NewUserRealm(testAdmin))
345				SetOperator(cross(cur), testOperator)
346				SetFaucetTargetAmount(cross(cur), adminUgnotBalance(testOperator)+1)
347				faucetTest.FundAdmin(testFaucetFundAmount)
348				return unauthorizedUser, testing.NewUserRealm(unauthorizedUser)
349			},
350			panicMsg: "admin access required",
351		},
352		{
353			name: "admin realm balance is insufficient",
354			setup: func(cur realm) (address, runtime.Realm) {
355				adminBalance := adminUgnotBalance(adminRealmAddr())
356				testing.SetOriginCaller(testAdmin)
357				testing.SetRealm(testing.NewUserRealm(testAdmin))
358				SetOperator(cross(cur), testOperator)
359				SetFaucetTargetAmount(cross(cur), adminUgnotBalance(testOperator)+adminBalance+1)
360				return testAdmin, testing.NewUserRealm(testAdmin)
361			},
362			panicMsg: "admin realm balance is insufficient",
363		},
364		{
365			name: "target amount is not configured",
366			setup: func(cur realm) (address, runtime.Realm) {
367				testing.SetOriginCaller(testAdmin)
368				testing.SetRealm(testing.NewUserRealm(testAdmin))
369				SetOperator(cross(cur), testOperator)
370				faucetTest.FundAdmin(testFaucetFundAmount)
371				return testAdmin, testing.NewUserRealm(testAdmin)
372			},
373			panicMsg: "faucet target amount must be positive",
374		},
375	}
376
377	for _, tt := range tests {
378		setupCommonAdminTests(cur, t)
379		caller, rlm := tt.setup(cur)
380		testing.SetOriginCaller(caller)
381		testing.SetRealm(rlm)
382
383		uassert.AbortsWithMessage(t, cur, tt.panicMsg, func() {
384			RequestFaucets(cross(cur))
385		})
386	}
387}
388
389func TestWithdrawTransfersAdminRealmBalance(cur realm, t *testing.T) {
390	setupCommonAdminTests(cur, t)
391
392	adminRealm := adminRealmAddr()
393	adminBefore := adminUgnotBalance(adminRealm)
394	recipientBefore := adminUgnotBalance(testUser)
395	withdrawAmount := int64(200000)
396	faucetTest.FundAdmin(withdrawAmount)
397
398	testing.SetOriginCaller(testAdmin)
399	testing.SetRealm(testing.NewUserRealm(testAdmin))
400	transferred := Withdraw(cross(cur), testUser, withdrawAmount)
401
402	uassert.Equal(t, withdrawAmount, transferred)
403	uassert.Equal(t, recipientBefore+withdrawAmount, adminUgnotBalance(testUser))
404	uassert.Equal(t, adminBefore, adminUgnotBalance(adminRealm))
405}
406
407type withdrawRejectContext struct {
408	caller          address
409	recipient       address
410	amount          int64
411	adminBefore     int64
412	recipientBefore int64
413	checkRecipient  bool
414}
415
416func TestWithdrawRejectsInvalidRequest(cur realm, t *testing.T) {
417	tests := []struct {
418		name     string
419		setup    func() withdrawRejectContext
420		panicMsg string
421	}{
422		{
423			name: "non-admin caller",
424			setup: func() withdrawRejectContext {
425				faucetTest.FundAdmin(1)
426				return withdrawRejectContext{
427					caller:    unauthorizedUser,
428					recipient: testUser,
429					amount:    1,
430				}
431			},
432			panicMsg: "admin access required",
433		},
434		{
435			name: "invalid recipient",
436			setup: func() withdrawRejectContext {
437				faucetTest.FundAdmin(1)
438				return withdrawRejectContext{
439					caller:    testAdmin,
440					recipient: address(""),
441					amount:    1,
442				}
443			},
444			panicMsg: "invalid address",
445		},
446		{
447			name: "zero amount",
448			setup: func() withdrawRejectContext {
449				faucetTest.FundAdmin(1)
450				return withdrawRejectContext{
451					caller:    testAdmin,
452					recipient: testUser,
453					amount:    0,
454				}
455			},
456			panicMsg: "amount must be greater than 0",
457		},
458		{
459			name: "negative amount",
460			setup: func() withdrawRejectContext {
461				faucetTest.FundAdmin(1)
462				return withdrawRejectContext{
463					caller:    testAdmin,
464					recipient: testUser,
465					amount:    -1,
466				}
467			},
468			panicMsg: "amount must be greater than 0",
469		},
470		{
471			name: "admin realm balance is insufficient",
472			setup: func() withdrawRejectContext {
473				return withdrawRejectContext{
474					caller:    testAdmin,
475					recipient: testUser,
476					amount:    adminUgnotBalance(adminRealmAddr()) + 1,
477				}
478			},
479			panicMsg: "admin realm balance is insufficient",
480		},
481	}
482
483	for _, tt := range tests {
484		setupCommonAdminTests(cur, t)
485		ctx := tt.setup()
486		ctx.adminBefore = adminUgnotBalance(adminRealmAddr())
487		ctx.checkRecipient = ctx.recipient.IsValid()
488		if ctx.checkRecipient {
489			ctx.recipientBefore = adminUgnotBalance(ctx.recipient)
490		}
491		testing.SetOriginCaller(ctx.caller)
492		testing.SetRealm(testing.NewUserRealm(ctx.caller))
493
494		uassert.AbortsWithMessage(t, cur, tt.panicMsg, func() {
495			Withdraw(cross(cur), ctx.recipient, ctx.amount)
496		})
497		uassert.Equal(t, ctx.adminBefore, adminUgnotBalance(adminRealmAddr()))
498		if ctx.checkRecipient {
499			uassert.Equal(t, ctx.recipientBefore, adminUgnotBalance(ctx.recipient))
500		}
501	}
502}
503
504func adminUgnotBalance(addr address) int64 {
505	bnk := banker.NewReadonlyBanker()
506	return bnk.GetCoins(addr).AmountOf("ugnot")
507}
508
509func (h *faucetTestHelper) FundAdmin(amount int64) {
510	h.Issue(adminRealmAddr(), amount)
511}
512
513func (h *faucetTestHelper) Issue(addr address, amount int64) {
514	if amount <= 0 {
515		return
516	}
517	testing.IssueCoins(addr, chain.Coins{{"ugnot", amount}})
518}
519
520func maxInt64(a int64, b int64) int64 {
521	if a > b {
522		return a
523	}
524	return b
525}