PathrockNetwork Gno Explorer
HomeBlocksTransactionsRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/r/gov/dao/treasury/test/v0

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
v0
Namespace
gov / dao / treasury / test
Files
4 (gnomod.toml)
Exported functions
n/a — Gnoland RPC unavailable (functions): vm/qfuncs gno.land/r/gov/dao/treasury/test/v0: unexpected response shape
Module
gno.land/r/gov/dao/treasury/test/v0
gno
0.9

Files (4)

  • gnomod.tomltoml
  • workaround.gnogno
  • treasury_test.gnogno
  • z_token_keys_copy_test.gnogno
treasury_test.gnogno
1package test23import (4	"chain"5	"chain/banker"6	"chain/runtime/unsafe"7	"sort"8	"strings"9	"testing"1011	"gno.land/p/nt/fqname/v0"12	"gno.land/p/nt/grc20/v0"13	"gno.land/p/nt/seqid/v0"14	"gno.land/p/nt/testutils/v0"15	trs_pkg "gno.land/p/nt/treasury/v0"16	"gno.land/p/nt/uassert/v0"1718	"gno.land/r/gov/dao"19	"gno.land/r/gov/dao/impl/v0"20	"gno.land/r/gov/dao/treasury/v0"21	"gno.land/r/nt/grc20reg/v0"22)2324// cur is a zero-value realm used as a placeholder when forwarding to25// uassert/urequire dispatch helpers that gained an `rlm realm` param.26// These tests pass `func()` callbacks (no crossing inside the callback),27// so rlm is ignored — a nil realm here is safe.28var cur realm29var nextTokenID seqid.ID30var (31	user1Addr       = testutils.TestAddress("g1user1")32	user2Addr       = testutils.TestAddress("g1user2")33	treasuryAddr    = chain.PackageAddress("gno.land/r/gov/dao/treasury/v0")34	allowedRealm    = testing.NewCodeRealm("gno.land/r/test/allowed")35	notAllowedRealm = testing.NewCodeRealm("gno.land/r/test/notallowed")36	mintAmount      = int64(1000)37)3839// Define a dummy trs_pkg.Payment type for testing purposes.40type dummyPayment struct {41	bankerID string42	str      string43}4445var _ trs_pkg.Payment = (*dummyPayment)(nil)4647func (dp *dummyPayment) BankerID() string { return dp.bankerID }48func (dp *dummyPayment) String() string   { return dp.str }4950func init(cur realm) {51	// Register allowed Realm path.52	dao.UpdateImpl(cross(cur), dao.NewUpdateRequest(impl.NewGovDAO(), []string{allowedRealm.PkgPath()}))53}5455func ugnotCoins(t *testing.T, amount int64) chain.Coins {56	t.Helper()5758	// Create a new coin with the ugnot denomination.59	return chain.NewCoins(chain.NewCoin("ugnot", amount))60}6162func ugnotBalance(t *testing.T, addr address) int64 {63	t.Helper()6465	// Get the balance of ugnot coins for the given address.66	banker_ := banker.NewReadonlyBanker()67	coins := banker_.GetCoins(addr)6869	return coins.AmountOf("ugnot")70}7172// Define a keyedToken type to hold the token and its key.73type keyedToken struct {74	key   string75	token *grc20.Token76}7778func registerGRC20Tokens(_ int, rlm realm, t *testing.T, tokenNames []string, toMint address) []keyedToken {79	t.Helper()8081	var (82		keyedTokens = make([]keyedToken, 0, len(tokenNames))83		keys        = make([]string, 0, len(tokenNames))84	)8586	for _, name := range tokenNames {87		// Create the token.88		symbol := strings.ToUpper(name)89		token, ledger := grc20.NewToken(name, symbol, 0, nextTokenID.Next(), rlm)9091		// Register the token.92		grc20reg.Register(cross(rlm), token, symbol)9394		// Mint tokens to the specified address.95		ledger.Mint(toMint, mintAmount)9697		// Add the token and key to the lists.98		key := fqname.Construct(unsafe.CurrentRealm().PkgPath(), symbol)99		keyedTokens = append(keyedTokens, keyedToken{key: key, token: token})100		keys = append(keys, key)101	}102103	// Set the token keys in the treasury.104	treasury.SetTokenKeys(cross(rlm), keys)105106	return keyedTokens107}108109func TestAllowedDAOs(cur realm, t *testing.T) {110	// Set the current Realm to the not allowed one.111	testing.SetRealm(notAllowedRealm)112113	// Define a dummy payment to test sending.114	dummyP := &dummyPayment{bankerID: "Dummy"}115116	// Try to send, it should abort because the Realm is not allowed.117	uassert.AbortsWithMessage(118		t, cur,119		"this Realm is not allowed to send payment: "+notAllowedRealm.PkgPath(),120		func() { treasury.Send(cross(cur), dummyP) },121	)122123	// Set the current Realm to the allowed one.124	testing.SetRealm(allowedRealm)125126	// Try to send, it should not abort because the Realm is allowed,127	// but because the dummy banker ID is not registered.128	uassert.AbortsWithMessage(129		t, cur,130		"banker not found: "+dummyP.BankerID(),131		func() { treasury.Send(cross(cur), dummyP) },132	)133}134135func TestRegisteredBankers(t *testing.T) {136	// Set the current Realm to the allowed one.137	testing.SetRealm(allowedRealm)138139	// Define the expected banker IDs.140	expectedBankerIDs := []string{141		trs_pkg.CoinsBanker{}.ID(),142		trs_pkg.GRC20Banker{}.ID(),143	}144145	// Get the registered bankers from the treasury and compare their lengths.146	registered := treasury.ListBankerIDs()147	uassert.Equal(t, len(registered), len(expectedBankerIDs))148149	// The treasury-returned slice is foreign-readonly; copy locally150	// before sorting in place.151	registeredBankerIDs := append([]string(nil), registered...)152153	// Sort both slices then compare them.154	sort.StringSlice(expectedBankerIDs).Sort()155	sort.StringSlice(registeredBankerIDs).Sort()156157	for i := range expectedBankerIDs {158		uassert.Equal(t, expectedBankerIDs[i], registeredBankerIDs[i])159	}160161	// Test HasBanker method.162	for _, bankerID := range expectedBankerIDs {163		uassert.True(t, treasury.HasBanker(bankerID))164	}165	uassert.False(t, treasury.HasBanker("UnknownBankerID"))166167	// Test Address method.168	for _, bankerID := range expectedBankerIDs {169		// The two bankers used for now should have the treasury Realm address.170		uassert.Equal(t, treasury.Address(bankerID), treasuryAddr.String())171	}172}173174func TestSendGRC20Payment(cur realm, t *testing.T) {175	// Set the current Realm to the allowed one.176	testing.SetRealm(allowedRealm)177178	// Try to send a GRC20 payment with a not registered token, it should abort.179	uassert.AbortsWithMessage(180		t, cur,181		"failed to send payment: GRC20 token not found: UNKNOW",182		func() {183			treasury.Send(cross(cur), trs_pkg.NewGRC20Payment("UNKNOW", 100, user1Addr))184		},185	)186187	// Create 3 GRC20 tokens and register them.188	keyedTokens := registerGRC20Tokens(189		0, cur,190		t,191		[]string{"TestToken0", "TestToken1", "TestToken2"},192		treasuryAddr,193	)194195	const txAmount = 42196197	// For each token-user pair.198	for i, userAddr := range []address{user1Addr, user2Addr} {199		for _, keyed := range keyedTokens {200			// Check that the treasury has the expected balance before sending.201			uassert.Equal(t, keyed.token.BalanceOf(treasuryAddr), mintAmount-int64(txAmount*i))202203			// Check that the user has no balance before sending.204			uassert.Equal(t, keyed.token.BalanceOf(userAddr), int64(0))205206			// Try to send a GRC20 payment with a registered token, it should not abort.207			uassert.NotAborts(t, cur, func() {208				treasury.Send(209					cross(cur),210					trs_pkg.NewGRC20Payment(211						keyed.key,212						txAmount,213						userAddr,214					),215				)216			})217218			// Check that the user has the expected balance after sending.219			uassert.Equal(t, keyed.token.BalanceOf(userAddr), int64(txAmount))220221			// Check that the treasury has the expected balance after sending.222			uassert.Equal(t, keyed.token.BalanceOf(treasuryAddr), mintAmount-int64(txAmount*(i+1)))223		}224	}225226	// Get the GRC20Banker ID.227	grc20BankerID := trs_pkg.GRC20Banker{}.ID()228229	// Test Balances method for the GRC20Banker.230	balances := treasury.Balances(grc20BankerID)231	uassert.Equal(t, len(balances), len(keyedTokens))232233	compared := 0234	for _, balance := range balances {235		for _, keyed := range keyedTokens {236			if balance.Denom == keyed.key {237				uassert.Equal(t, balance.Amount, keyed.token.BalanceOf(treasuryAddr))238				compared++239			}240		}241	}242	uassert.Equal(t, compared, len(keyedTokens))243244	// Check the history of the GRC20Banker.245	history := treasury.History(grc20BankerID, 1, 10)246	uassert.Equal(t, len(history), 6)247248	// Try to send a dummy payment with the GRC20 banker ID, it should abort.249	uassert.AbortsWithMessage(250		t, cur,251		"failed to send payment: invalid payment type",252		func() {253			treasury.Send(cross(cur), &dummyPayment{bankerID: grc20BankerID})254		},255	)256257	// Try to send a GRC20 payment without enough balance, it should abort.258	uassert.AbortsWithMessage(259		t, cur,260		"failed to send payment: insufficient balance",261		func() {262			treasury.Send(263				cross(cur),264				trs_pkg.NewGRC20Payment(265					keyedTokens[0].key,266					mintAmount*42, // Try to send more than the treasury has.267					user1Addr,268				),269			)270		},271	)272273	// Check the history of the GRC20Banker.274	history = treasury.History(grc20BankerID, 1, 10)275	uassert.Equal(t, len(history), 6)276}277278func TestSendCoinPayment(cur realm, t *testing.T) {279	// Set the current Realm to the allowed one.280	testing.SetRealm(allowedRealm)281282	// Issue initial ugnot coins to the treasury address.283	testing.IssueCoins(treasuryAddr, ugnotCoins(t, mintAmount))284285	// Get the CoinsBanker ID.286	bankerID := trs_pkg.CoinsBanker{}.ID()287288	// Define helper function to check balances and history.289	var (290		expectedTreasuryBalance = mintAmount291		expectedUser1Balance    = int64(0)292		expectedUser2Balance    = int64(0)293		expectedHistoryLen      = 0294		checkHistoryAndBalances = func() {295			t.Helper()296297			uassert.Equal(t, ugnotBalance(t, treasuryAddr), expectedTreasuryBalance)298			uassert.Equal(t, ugnotBalance(t, user1Addr), expectedUser1Balance)299			uassert.Equal(t, ugnotBalance(t, user2Addr), expectedUser2Balance)300301			// Check treasury.Balances returned value.302			balances := treasury.Balances(bankerID)303			uassert.Equal(t, len(balances), 1)304			uassert.Equal(t, balances[0].Denom, "ugnot")305			uassert.Equal(t, balances[0].Amount, expectedTreasuryBalance)306307			// Check treasury.History returned value.308			history := treasury.History(bankerID, 1, expectedHistoryLen+1)309			uassert.Equal(t, len(history), expectedHistoryLen)310		}311	)312313	// Check initial balances and history.314	checkHistoryAndBalances()315316	const txAmount = int64(42)317318	// Treasury send coins.319	for i := int64(0); i < 3; i++ {320		// Send ugnot coins to user1 and user2.321		uassert.NotAborts(t, cur, func() {322			treasury.Send(323				cross(cur),324				trs_pkg.NewCoinsPayment(ugnotCoins(t, txAmount), user1Addr),325			)326			treasury.Send(327				cross(cur),328				trs_pkg.NewCoinsPayment(ugnotCoins(t, txAmount), user2Addr),329			)330		})331332		// Update expected balances and history length.333		expectedTreasuryBalance = mintAmount - txAmount*2*(i+1)334		expectedUser1Balance = txAmount * (i + 1)335		expectedUser2Balance = expectedUser1Balance336		expectedHistoryLen = int(2 * (i + 1))337338		// Check balances and history after sending.339		checkHistoryAndBalances()340	}341}342343// The allowlist is the only thing standing between an arbitrary realm and the344// treasury: treasury.Send and treasury.SetTokenKeys gate on345// dao.InAllowedDAOs, and that helper returns true for EVERY caller while the346// list is empty (the genesis bootstrap window). UpdateImpl used to store an347// empty list, so a single implementation-only upgrade — dao.UpdateImpl with348// NewUpdateRequest(d, nil), which copies nil into a non-nil empty slice —349// silently reopened that window and handed the treasury to the whole chain.350//351// TestAllowedDAOs above proves the gate rejects an outsider in the normal352// state. This proves the gate cannot be switched off, which is the property353// that actually protects the funds.354func TestTreasuryLockdownCannotBeReopened(cur realm, t *testing.T) {355	savedDAOs := dao.AllowedDAOs()356357	// An allowlisted realm attempts the reopen. NewUpdateRequest(d, nil) is the358	// production spelling — it copies nil into a non-nil empty slice, which is359	// exactly what the old `!= nil` test accepted. (A bare UpdateRequest struct360	// literal cannot be built here: allocating another realm's struct type from361	// this realm is rejected by the VM. That path is covered by362	// r/gov/dao/allowlist_test.gno, which lives in the same package.)363	testing.SetRealm(allowedRealm)364	dao.UpdateImpl(cross(cur), dao.NewUpdateRequest(nil, nil))365366	// The outsider must still be refused at the treasury. Reaching367	// "banker not found" would mean authorization had been cleared — that368	// error comes from AFTER the InAllowedDAOs check.369	testing.SetRealm(notAllowedRealm)370	dummyP := &dummyPayment{bankerID: "Dummy"}371372	uassert.AbortsWithMessage(373		t, cur,374		"this Realm is not allowed to send payment: "+notAllowedRealm.PkgPath(),375		func() { treasury.Send(cross(cur), dummyP) },376	)377378	// SetTokenKeys reuses Send's message verbatim ("...to send payment...")379	// rather than naming its own operation — a copy-paste slip in380	// treasury.gno:65, asserted here as-is. Worth correcting separately; it is381	// operator-visible text, not a security property, so it is not changed as382	// part of this fix.383	uassert.AbortsWithMessage(384		t, cur,385		"this Realm is not allowed to send payment: "+notAllowedRealm.PkgPath(),386		func() { treasury.SetTokenKeys(cross(cur), []string{"evil/key"}) },387	)388389	// Restore explicitly rather than via defer: the assertions above leave the390	// current realm set to notAllowedRealm, and a deferred UpdateImpl would run391	// under it and be refused. uassert.AbortsWithMessage recovers the abort, so392	// control always reaches here.393	testing.SetRealm(allowedRealm)394	dao.UpdateImpl(cross(cur), dao.NewUpdateRequest(nil, savedDAOs))395	uassert.True(t, dao.InAllowedDAOs(allowedRealm.PkgPath()), "allowlist restored")396}397398func TestRenderEscapesUnknownBankerID(t *testing.T) {399	// The {banker} route reflects an unknown banker id into the page; a crafted400	// id must be escaped so it cannot inject markdown/HTML.401	out := treasury.Render("evil[x](y)")402	uassert.True(t, strings.Contains(out, `\[x\]`), "reflected banker id must be escaped")403	uassert.False(t, strings.Contains(out, "[x](y)"), "must not render a live link")404}405

Functions

Gnoland RPC unavailable (functions): vm/qfuncs gno.land/r/gov/dao/treasury/test/v0: unexpected response shape

Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.

Rendered

RenderedRawgnoweb ↗
This realm does not provide renderable output (vm/qrender → NoRenderDeclError). View on gnoweb ↗

vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.