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/metadata/v0

Package
Open in gnoweb ↗

Overview

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

Files (4)

  • gnomod.tomltoml
  • token.gnogno
  • types.gnogno
  • token_test.gnogno
token_test.gnogno
1package metadata23import (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// newToken builds via same-realm cross (NewToken requires rlm.IsCurrent()).14func newToken(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))18	return19}2021func newMeta(id seqid.ID, rlm realm) (*grc721.PrivateLedger, *Metadata, *Ledger) {22	_, tokenLedger := newToken("Foo", "FOO", id, rlm)23	meta, metaLedger := NewMetadata(tokenLedger)24	return tokenLedger, meta, metaLedger25}2627func TestNewMetadata(cur realm, t *testing.T) {28	tests := []struct {29		name      string30		nilLedger bool31	}{32		{"nil token ledger panics", true},33		{"valid token ledger constructs read view and ledger", false},34	}3536	for _, tt := range tests {37		t.Run(tt.name, func(t *testing.T) {38			if tt.nilLedger {39				uassert.PanicsWithMessage(t, cur, "metadata: nil core ledger", func() {40					NewMetadata(nil)41				})42				return43			}4445			_, tokenLedger := newToken("Foo", "FOO", 0, cur)46			meta, metaLedger := NewMetadata(tokenLedger)4748			uassert.True(t, meta != nil)49			uassert.True(t, metaLedger != nil)50			uassert.Equal(t, Kind, meta.ExtensionKind())51			uassert.Equal(t, "metadata", meta.ExtensionKind())52			uassert.Equal(t, tokenLedger.ReadToken().ID(), meta.TokenID())53		})54	}55}5657func TestSetTokenURI(cur realm, t *testing.T) {58	tests := []struct {59		name    string60		mint    bool61		tid     grc721.TokenID62		uri     string63		wantErr error64	}{65		{"unminted token returns ErrTokenNotMinted", false, "404", "ipfs://x", ErrTokenNotMinted},66		{"minted token sets uri", true, "1", "ipfs://uri-1", nil},67		{"empty uri on minted token is allowed", true, "1", "", nil},68	}6970	for _, tt := range tests {71		t.Run(tt.name, func(t *testing.T) {72			tokenLedger, meta, metaLedger := newMeta(0, cur)73			if tt.mint {74				urequire.NoError(t, tokenLedger.Mint(testutils.TestAddress("alice"), tt.tid))75			}7677			err := metaLedger.SetTokenURI(tt.tid, tt.uri)78			if tt.wantErr != nil {79				uassert.ErrorIs(t, err, tt.wantErr)80				uassert.False(t, meta.HasTokenURI(tt.tid))81				return82			}8384			urequire.NoError(t, err)85			uassert.True(t, meta.HasTokenURI(tt.tid))86			got, err := meta.TokenURI(tt.tid)87			urequire.NoError(t, err)88			uassert.Equal(t, tt.uri, got)89		})90	}91}9293func TestSetTokenURIOverwrite(cur realm, t *testing.T) {94	tokenLedger, meta, metaLedger := newMeta(0, cur)95	urequire.NoError(t, tokenLedger.Mint(testutils.TestAddress("alice"), "1"))9697	urequire.NoError(t, metaLedger.SetTokenURI("1", "ipfs://first"))98	urequire.NoError(t, metaLedger.SetTokenURI("1", "ipfs://second"))99100	got, err := meta.TokenURI("1")101	urequire.NoError(t, err)102	uassert.Equal(t, "ipfs://second", got)103}104105func TestSetTokenMetadata(cur realm, t *testing.T) {106	full := Data{107		Image:           "ipfs://img-1",108		ImageData:       "<svg/>",109		ExternalURL:     "https://example.com/1",110		Description:     "first",111		Name:            "Token One",112		Attributes:      []Trait{{DisplayType: "string", TraitType: "rarity", Value: "rare"}},113		BackgroundColor: "ffffff",114		AnimationURL:    "ipfs://anim-1",115		YoutubeURL:      "https://youtu.be/abc",116	}117118	tests := []struct {119		name    string120		mint    bool121		tid     grc721.TokenID122		data    Data123		wantErr error124	}{125		{"unminted token returns ErrTokenNotMinted", false, "404", Data{Name: "x"}, ErrTokenNotMinted},126		{"minted token stores full metadata", true, "1", full, nil},127		{"minted token stores zero-value metadata", true, "2", Data{}, nil},128	}129130	for _, tt := range tests {131		t.Run(tt.name, func(t *testing.T) {132			tokenLedger, meta, metaLedger := newMeta(0, cur)133			if tt.mint {134				urequire.NoError(t, tokenLedger.Mint(testutils.TestAddress("alice"), tt.tid))135			}136137			err := metaLedger.SetTokenMetadata(tt.tid, tt.data)138			if tt.wantErr != nil {139				uassert.ErrorIs(t, err, tt.wantErr)140				uassert.False(t, meta.HasMetadata(tt.tid))141				return142			}143144			urequire.NoError(t, err)145			uassert.True(t, meta.HasMetadata(tt.tid))146			got, err := meta.TokenMetadata(tt.tid)147			urequire.NoError(t, err)148			uassert.Equal(t, tt.data.Name, got.Name)149			uassert.Equal(t, tt.data.Description, got.Description)150			uassert.Equal(t, tt.data.Image, got.Image)151			uassert.Equal(t, len(tt.data.Attributes), len(got.Attributes))152			if len(tt.data.Attributes) > 0 {153				uassert.Equal(t, tt.data.Attributes[0].Value, got.Attributes[0].Value)154				uassert.Equal(t, tt.data.Attributes[0].TraitType, got.Attributes[0].TraitType)155			}156		})157	}158}159160func TestSetTokenMetadataOverwrite(cur realm, t *testing.T) {161	tokenLedger, meta, metaLedger := newMeta(0, cur)162	urequire.NoError(t, tokenLedger.Mint(testutils.TestAddress("alice"), "1"))163164	urequire.NoError(t, metaLedger.SetTokenMetadata("1", Data{Name: "first"}))165	urequire.NoError(t, metaLedger.SetTokenMetadata("1", Data{Name: "second"}))166167	got, err := meta.TokenMetadata("1")168	urequire.NoError(t, err)169	uassert.Equal(t, "second", got.Name)170}171172func TestReadsOnUnsetToken(cur realm, t *testing.T) {173	tests := []struct {174		name string175		fn   func(t *testing.T, meta *Metadata)176	}{177		{"TokenURI on unset token returns ErrNoTokenURI", func(t *testing.T, meta *Metadata) {178			uri, err := meta.TokenURI("1")179			uassert.Equal(t, "", uri)180			uassert.ErrorIs(t, err, ErrNoTokenURI)181		}},182		{"TokenMetadata on unset token returns ErrNoMetadata", func(t *testing.T, meta *Metadata) {183			data, err := meta.TokenMetadata("1")184			uassert.Equal(t, "", data.Name)185			uassert.ErrorIs(t, err, ErrNoMetadata)186		}},187		{"HasTokenURI is false on unset token", func(t *testing.T, meta *Metadata) {188			uassert.False(t, meta.HasTokenURI("1"))189		}},190		{"HasMetadata is false on unset token", func(t *testing.T, meta *Metadata) {191			uassert.False(t, meta.HasMetadata("1"))192		}},193	}194195	for _, tt := range tests {196		t.Run(tt.name, func(t *testing.T) {197			_, meta, _ := newMeta(0, cur)198			tt.fn(t, meta)199		})200	}201}202203func TestOnBurnClearsMetadata(cur realm, t *testing.T) {204	alice := testutils.TestAddress("alice")205	tokenLedger, meta, metaLedger := newMeta(0, cur)206207	urequire.NoError(t, tokenLedger.Mint(alice, "1"))208	urequire.NoError(t, metaLedger.SetTokenURI("1", "ipfs://uri-1"))209	urequire.NoError(t, metaLedger.SetTokenMetadata("1", Data{Name: "one"}))210	uassert.True(t, meta.HasTokenURI("1"))211	uassert.True(t, meta.HasMetadata("1"))212213	urequire.NoError(t, tokenLedger.Burn("1"))214	uassert.False(t, meta.HasTokenURI("1"))215	uassert.False(t, meta.HasMetadata("1"))216217	_, err := meta.TokenURI("1")218	uassert.ErrorIs(t, err, ErrNoTokenURI)219	_, err = meta.TokenMetadata("1")220	uassert.ErrorIs(t, err, ErrNoMetadata)221222	urequire.NoError(t, tokenLedger.Mint(alice, "1"))223	uassert.False(t, meta.HasTokenURI("1"))224	uassert.False(t, meta.HasMetadata("1"))225}226227func TestHooks(cur realm, t *testing.T) {228	alice := testutils.TestAddress("alice")229	bob := testutils.TestAddress("bob")230	tokenLedger, meta, metaLedger := newMeta(0, cur)231232	urequire.NoError(t, tokenLedger.Mint(alice, "1"))233	urequire.NoError(t, metaLedger.SetTokenURI("1", "ipfs://uri-1"))234	urequire.NoError(t, metaLedger.SetTokenMetadata("1", Data{Name: "one"}))235236	metaLedger.OnMint(alice, "1")237	metaLedger.OnTransfer(alice, bob, "1")238	uassert.True(t, meta.HasTokenURI("1"))239	uassert.True(t, meta.HasMetadata("1"))240	uri, err := meta.TokenURI("1")241	urequire.NoError(t, err)242	uassert.Equal(t, "ipfs://uri-1", uri)243244	metaLedger.OnBurn("1")245	uassert.False(t, meta.HasTokenURI("1"))246	uassert.False(t, meta.HasMetadata("1"))247}248

Functions

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

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