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/p/nt/grc721/enumerable/v0

Package
Open in gnoweb ↗

Overview

Kind
Pure package
Name
v0
Namespace
nt / grc721 / enumerable
Files
4 (gnomod.toml)
Exported functions
n/a — not supported for pure packages by the node (vm/qfuncs)
Module
gno.land/p/nt/grc721/enumerable/v0
gno
0.9

Files (4)

  • gnomod.tomltoml
  • token.gnogno
  • types.gnogno
  • token_test.gnogno
token_test.gnogno
1package enumerable23import (4	"testing"56	"gno.land/p/nt/grc721/v0"7	"gno.land/p/nt/seqid/v0"8	"gno.land/p/nt/testutils/v0"9	"gno.land/p/nt/uassert/v0"10	"gno.land/p/nt/urequire/v0"11)1213// Same-realm cross: NewToken requires rlm.IsCurrent().14func newCore(name, symbol string, id seqid.ID, rlm realm) (tok *grc721.Token, led *grc721.PrivateLedger) {15	func(cur realm) {16		tok, led = grc721.NewToken(name, symbol, id, cur)17	}(cross(rlm))1819	return20}2122func newEnum(cur realm) (enum *Enumerable, core *grc721.PrivateLedger, led *Ledger) {23	_, coreLedger := newCore("Foo", "FOO", 0, cur)24	enum, led = NewEnumerable(coreLedger)25	return enum, coreLedger, led26}2728func globalOrder(t *testing.T, enum *Enumerable) []string {29	t.Helper()30	out := []string{}31	for i := int64(0); i < enum.TotalSupply(); i++ {32		tid, err := enum.TokenByIndex(i)33		urequire.NoError(t, err)34		out = append(out, tid.String())35	}36	return out37}3839func ownerOrder(t *testing.T, enum *Enumerable, owner address) []string {40	t.Helper()41	out := []string{}42	for i := int64(0); ; i++ {43		tid, err := enum.TokenOfOwnerByIndex(owner, i)44		if err != nil {45			break46		}47		out = append(out, tid.String())48	}49	return out50}5152func eqStrings(t *testing.T, name string, got, want []string) {53	t.Helper()54	if len(got) != len(want) {55		t.Errorf("%s: length got %d want %d (%v vs %v)", name, len(got), len(want), got, want)56		return57	}58	for i := range want {59		uassert.Equal(t, want[i], got[i])60	}61}6263func TestNewEnumerable(cur realm, t *testing.T) {64	uassert.PanicsWithMessage(t, cur, "enumerable: nil core ledger", func() {65		NewEnumerable(nil)66	})6768	tok, coreLedger := newCore("Foo", "FOO", 0, cur)69	enum, led := NewEnumerable(coreLedger)7071	urequire.True(t, enum != nil, "enum built")72	urequire.True(t, led != nil, "ledger built")7374	tests := []struct {75		name string76		got  string77		want string78	}{79		{"ExtensionKind is enumerable", enum.ExtensionKind(), Kind},80		{"ExtensionKind constant value", Kind, "enumerable"},81		{"TokenID matches core token id", enum.TokenID(), tok.ID()},82	}83	for _, tt := range tests {84		t.Run(tt.name, func(t *testing.T) {85			uassert.Equal(t, tt.want, tt.got)86		})87	}8889	uassert.Equal(t, int64(0), enum.TotalSupply())90}9192func TestReadIndices(cur realm, t *testing.T) {93	alice := testutils.TestAddress("alice")94	bob := testutils.TestAddress("bob")95	carol := testutils.TestAddress("carol")9697	enum, coreLedger, _ := newEnum(cur)98	urequire.NoError(t, coreLedger.Mint(alice, "1"))99	urequire.NoError(t, coreLedger.Mint(alice, "2"))100	urequire.NoError(t, coreLedger.Mint(bob, "3"))101102	uassert.Equal(t, int64(3), enum.TotalSupply())103104	tests := []struct {105		name string106		run  func(t *testing.T)107	}{108		{"TokenByIndex 0 returns first minted", func(t *testing.T) {109			tid, err := enum.TokenByIndex(0)110			urequire.NoError(t, err)111			uassert.Equal(t, "1", tid.String())112		}},113		{"TokenByIndex last returns last minted", func(t *testing.T) {114			tid, err := enum.TokenByIndex(2)115			urequire.NoError(t, err)116			uassert.Equal(t, "3", tid.String())117		}},118		{"TokenByIndex negative returns ErrIndexOutOfRange", func(t *testing.T) {119			_, err := enum.TokenByIndex(-1)120			uassert.ErrorIs(t, err, ErrIndexOutOfRange)121		}},122		{"TokenByIndex out of range returns ErrIndexOutOfRange", func(t *testing.T) {123			_, err := enum.TokenByIndex(3)124			uassert.ErrorIs(t, err, ErrIndexOutOfRange)125		}},126		{"TokenOfOwnerByIndex returns owner's token", func(t *testing.T) {127			tid, err := enum.TokenOfOwnerByIndex(alice, 1)128			urequire.NoError(t, err)129			uassert.Equal(t, "2", tid.String())130		}},131		{"TokenOfOwnerByIndex unknown owner returns ErrIndexOutOfRange", func(t *testing.T) {132			_, err := enum.TokenOfOwnerByIndex(carol, 0)133			uassert.ErrorIs(t, err, ErrIndexOutOfRange)134		}},135		{"TokenOfOwnerByIndex negative index returns ErrIndexOutOfRange", func(t *testing.T) {136			_, err := enum.TokenOfOwnerByIndex(alice, -1)137			uassert.ErrorIs(t, err, ErrIndexOutOfRange)138		}},139		{"TokenOfOwnerByIndex past end returns ErrIndexOutOfRange", func(t *testing.T) {140			_, err := enum.TokenOfOwnerByIndex(alice, 2)141			uassert.ErrorIs(t, err, ErrIndexOutOfRange)142		}},143	}144	for _, tt := range tests {145		t.Run(tt.name, tt.run)146	}147148	eqStrings(t, "global order", globalOrder(t, enum), []string{"1", "2", "3"})149	eqStrings(t, "alice order", ownerOrder(t, enum, alice), []string{"1", "2"})150	eqStrings(t, "bob order", ownerOrder(t, enum, bob), []string{"3"})151}152153func TestBurnGlobalSwapAndPop(cur realm, t *testing.T) {154	alice := testutils.TestAddress("alice")155156	tests := []struct {157		name      string158		burn      grc721.TokenID159		wantOrder []string160	}{161		{"burn last token truncates global list (idx == last)", "3", []string{"1", "2"}},162		{"burn middle token swap-and-pops the global list (idx != last)", "2", []string{"1", "3"}},163		{"burn first token swap-and-pops the global list (idx != last)", "1", []string{"3", "2"}},164	}165	for _, tt := range tests {166		t.Run(tt.name, func(t *testing.T) {167			enum, coreLedger, _ := newEnum(cur)168			urequire.NoError(t, coreLedger.Mint(alice, "1"))169			urequire.NoError(t, coreLedger.Mint(alice, "2"))170			urequire.NoError(t, coreLedger.Mint(alice, "3"))171172			urequire.NoError(t, coreLedger.Burn(tt.burn))173174			uassert.Equal(t, int64(2), enum.TotalSupply())175			eqStrings(t, tt.name, globalOrder(t, enum), tt.wantOrder)176		})177	}178}179180func TestBurnOwnerSwapAndPop(cur realm, t *testing.T) {181	alice := testutils.TestAddress("alice")182183	tests := []struct {184		name       string185		mint       []grc721.TokenID186		burn       grc721.TokenID187		wantOwner  []string188		ownerEmpty bool189	}{190		{191			name:      "burn first of owner swap-and-pops per-owner list (idx != last)",192			mint:      []grc721.TokenID{"1", "2", "3"},193			burn:      "1",194			wantOwner: []string{"3", "2"},195		},196		{197			name:      "burn middle of owner swap-and-pops per-owner list (idx != last)",198			mint:      []grc721.TokenID{"1", "2", "3"},199			burn:      "2",200			wantOwner: []string{"1", "3"},201		},202		{203			name:      "burn last of owner truncates per-owner list (idx == last)",204			mint:      []grc721.TokenID{"1", "2", "3"},205			burn:      "3",206			wantOwner: []string{"1", "2"},207		},208		{209			name:       "burn only token empties per-owner list (owned.Remove)",210			mint:       []grc721.TokenID{"1"},211			burn:       "1",212			ownerEmpty: true,213		},214	}215	for _, tt := range tests {216		t.Run(tt.name, func(t *testing.T) {217			enum, coreLedger, _ := newEnum(cur)218			for _, tid := range tt.mint {219				urequire.NoError(t, coreLedger.Mint(alice, tid))220			}221222			urequire.NoError(t, coreLedger.Burn(tt.burn))223224			if tt.ownerEmpty {225				_, err := enum.TokenOfOwnerByIndex(alice, 0)226				uassert.ErrorIs(t, err, ErrIndexOutOfRange)227				return228			}229			eqStrings(t, tt.name, ownerOrder(t, enum, alice), tt.wantOwner)230		})231	}232}233234func TestTransfer(cur realm, t *testing.T) {235	alice := testutils.TestAddress("alice")236	bob := testutils.TestAddress("bob")237238	enum, coreLedger, _ := newEnum(cur)239	urequire.NoError(t, coreLedger.Mint(alice, "1"))240	urequire.NoError(t, coreLedger.Mint(alice, "2"))241	urequire.NoError(t, coreLedger.Mint(bob, "3"))242243	tests := []struct {244		name string245		run  func(t *testing.T)246	}{247		{"transfer 1 alice->bob swap-and-pops alice list", func(t *testing.T) {248			urequire.NoError(t, coreLedger.TransferFrom(alice, alice, bob, "1"))249			eqStrings(t, "alice after transfer", ownerOrder(t, enum, alice), []string{"2"})250			eqStrings(t, "bob after transfer", ownerOrder(t, enum, bob), []string{"3", "1"})251		}},252		{"transfer 2 alice->bob empties alice list", func(t *testing.T) {253			urequire.NoError(t, coreLedger.TransferFrom(alice, alice, bob, "2"))254			_, err := enum.TokenOfOwnerByIndex(alice, 0)255			uassert.ErrorIs(t, err, ErrIndexOutOfRange)256			eqStrings(t, "bob owns all", ownerOrder(t, enum, bob), []string{"3", "1", "2"})257		}},258		{"global supply unchanged by transfers", func(t *testing.T) {259			uassert.Equal(t, int64(3), enum.TotalSupply())260		}},261	}262	for _, tt := range tests {263		t.Run(tt.name, tt.run)264	}265}266267func TestDefensiveNoOps(cur realm, t *testing.T) {268	alice := testutils.TestAddress("alice")269	bob := testutils.TestAddress("bob")270271	enum, coreLedger, led := newEnum(cur)272	urequire.NoError(t, coreLedger.Mint(alice, "1"))273274	tests := []struct {275		name string276		run  func(t *testing.T)277	}{278		{"removeFromAll unknown id is a no-op", func(t *testing.T) {279			led.removeFromAll("999")280			uassert.Equal(t, int64(1), enum.TotalSupply())281		}},282		{"removeFromOwner unknown owner is a no-op", func(t *testing.T) {283			led.removeFromOwner(bob, "1")284			eqStrings(t, "alice untouched", ownerOrder(t, enum, alice), []string{"1"})285		}},286		{"removeFromOwner known owner unknown id is a no-op", func(t *testing.T) {287			led.removeFromOwner(alice, "999")288			eqStrings(t, "alice untouched", ownerOrder(t, enum, alice), []string{"1"})289		}},290		{"OnBurn of never-minted id is a no-op", func(t *testing.T) {291			led.OnBurn("neverminted")292			uassert.Equal(t, int64(1), enum.TotalSupply())293			eqStrings(t, "alice untouched", ownerOrder(t, enum, alice), []string{"1"})294		}},295	}296	for _, tt := range tests {297		t.Run(tt.name, tt.run)298	}299}300

Functions

not supported for pure packages by the node (vm/qfuncs)

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