PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

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

Package
Open in gnoweb ↗

Overview

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

Files (4)

  • gnomod.tomltoml
  • token.gnogno
  • types.gnogno
  • token_test.gnogno
token_test.gnogno
1package royalty23import (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 newRoy(maxBps int64, rlm realm) (*grc721.PrivateLedger, *Royalty, *Ledger) {22	_, tokenLedger := newToken("Foo", "FOO", 0, rlm)23	roy, royLedger := NewRoyalty(tokenLedger, maxBps)24	return tokenLedger, roy, royLedger25}2627func TestNewRoyalty(cur realm, t *testing.T) {28	tests := []struct {29		name      string30		nilLedger bool31		maxBps    int6432		wantPanic string // "" = success33	}{34		{"nil token ledger panics", true, 500, "royalty: nil core ledger"},35		{"maxBps above FeeDenominator panics", false, 10001, ErrMaxBpsRange.Error()},36		{"negative maxBps panics", false, -1, ErrMaxBpsRange.Error()},37		{"maxBps at FeeDenominator boundary constructs", false, 10000, ""},38		{"maxBps zero boundary constructs", false, 0, ""},39		{"typical maxBps constructs", false, 1000, ""},40	}4142	for _, tt := range tests {43		t.Run(tt.name, func(t *testing.T) {44			if tt.nilLedger {45				uassert.PanicsWithMessage(t, cur, tt.wantPanic, func() {46					NewRoyalty(nil, tt.maxBps)47				})48				return49			}5051			_, tokenLedger := newToken("Foo", "FOO", 0, cur)52			if tt.wantPanic != "" {53				uassert.PanicsWithMessage(t, cur, tt.wantPanic, func() {54					NewRoyalty(tokenLedger, tt.maxBps)55				})56				return57			}5859			roy, royLedger := NewRoyalty(tokenLedger, tt.maxBps)60			uassert.True(t, roy != nil)61			uassert.True(t, royLedger != nil)62			uassert.Equal(t, Kind, roy.ExtensionKind())63			uassert.Equal(t, "royalty", roy.ExtensionKind())64			uassert.Equal(t, tt.maxBps, roy.MaxBps())65			uassert.Equal(t, tokenLedger.ReadToken().ID(), roy.TokenID())66		})67	}68}6970func TestRoyaltyInfo(cur realm, t *testing.T) {71	artist := testutils.TestAddress("artist")72	special := testutils.TestAddress("special")7374	tests := []struct {75		name         string76		setDefault   bool77		defaultBps   int6478		setToken     bool79		tokenBps     int6480		salePrice    int6481		wantReceiver address82		wantAmount   int6483	}{84		{85			name:         "no royalty configured returns zero receiver and amount",86			salePrice:    10000,87			wantReceiver: zeroAddress,88			wantAmount:   0,89		},90		{91			name:         "default 5% yields 500 on salePrice 10000",92			setDefault:   true,93			defaultBps:   500,94			salePrice:    10000,95			wantReceiver: artist,96			wantAmount:   500,97		},98		{99			name:         "per-token override takes precedence over default",100			setDefault:   true,101			defaultBps:   500,102			setToken:     true,103			tokenBps:     250,104			salePrice:    10000,105			wantReceiver: special,106			wantAmount:   250,107		},108		{109			name:         "fractional bps 250 truncates on small sale price",110			setToken:     true,111			tokenBps:     250,112			salePrice:    100, // 100*250/10000 = 2113			wantReceiver: special,114			wantAmount:   2,115		},116		{117			name:         "zero sale price yields zero amount",118			setDefault:   true,119			defaultBps:   500,120			salePrice:    0,121			wantReceiver: artist,122			wantAmount:   0,123		},124		{125			name:         "default receiver applies when token has no override",126			setDefault:   true,127			defaultBps:   1000,128			salePrice:    5000,129			wantReceiver: artist,130			wantAmount:   500,131		},132	}133134	for _, tt := range tests {135		t.Run(tt.name, func(t *testing.T) {136			tokenLedger, roy, royLedger := newRoy(1000, cur)137			urequire.NoError(t, tokenLedger.Mint(testutils.TestAddress("alice"), "1"))138139			if tt.setDefault {140				urequire.NoError(t, royLedger.SetDefaultRoyalty(artist, tt.defaultBps))141			}142			if tt.setToken {143				urequire.NoError(t, royLedger.SetTokenRoyalty("1", special, tt.tokenBps))144			}145146			recv, amt, err := roy.RoyaltyInfo("1", tt.salePrice)147			urequire.NoError(t, err)148			uassert.Equal(t, tt.wantReceiver, recv)149			uassert.Equal(t, tt.wantAmount, amt)150		})151	}152}153154func TestDefaultRoyalty(cur realm, t *testing.T) {155	artist := testutils.TestAddress("artist")156157	t.Run("unset default reports not present", func(t *testing.T) {158		_, roy, _ := newRoy(1000, cur)159		info, ok := roy.DefaultRoyalty()160		uassert.False(t, ok)161		uassert.Equal(t, zeroAddress, info.Receiver)162		uassert.Equal(t, int64(0), info.Bps)163	})164165	t.Run("set default is reported and readable", func(t *testing.T) {166		_, roy, royLedger := newRoy(1000, cur)167		urequire.NoError(t, royLedger.SetDefaultRoyalty(artist, 500))168		info, ok := roy.DefaultRoyalty()169		uassert.True(t, ok)170		uassert.Equal(t, artist, info.Receiver)171		uassert.Equal(t, int64(500), info.Bps)172	})173174	t.Run("delete default clears it", func(t *testing.T) {175		_, roy, royLedger := newRoy(1000, cur)176		urequire.NoError(t, royLedger.SetDefaultRoyalty(artist, 500))177		royLedger.DeleteDefaultRoyalty()178		info, ok := roy.DefaultRoyalty()179		uassert.False(t, ok)180		uassert.Equal(t, zeroAddress, info.Receiver)181182		recv, amt, err := roy.RoyaltyInfo("1", 10000)183		urequire.NoError(t, err)184		uassert.Equal(t, zeroAddress, recv)185		uassert.Equal(t, int64(0), amt)186	})187}188189func TestTokenRoyalty(cur realm, t *testing.T) {190	artist := testutils.TestAddress("artist")191	special := testutils.TestAddress("special")192193	t.Run("unset per-token override reports not present", func(t *testing.T) {194		_, roy, _ := newRoy(1000, cur)195		info, ok := roy.TokenRoyalty("1")196		uassert.False(t, ok)197		uassert.Equal(t, zeroAddress, info.Receiver)198	})199200	t.Run("set per-token override is reported and readable", func(t *testing.T) {201		tokenLedger, roy, royLedger := newRoy(1000, cur)202		urequire.NoError(t, tokenLedger.Mint(testutils.TestAddress("alice"), "1"))203		urequire.NoError(t, royLedger.SetTokenRoyalty("1", special, 250))204		info, ok := roy.TokenRoyalty("1")205		uassert.True(t, ok)206		uassert.Equal(t, special, info.Receiver)207		uassert.Equal(t, int64(250), info.Bps)208	})209210	t.Run("delete per-token override falls back to default", func(t *testing.T) {211		tokenLedger, roy, royLedger := newRoy(1000, cur)212		urequire.NoError(t, tokenLedger.Mint(testutils.TestAddress("alice"), "1"))213		urequire.NoError(t, royLedger.SetDefaultRoyalty(artist, 500))214		urequire.NoError(t, royLedger.SetTokenRoyalty("1", special, 250))215216		royLedger.DeleteTokenRoyalty("1")217		_, ok := roy.TokenRoyalty("1")218		uassert.False(t, ok)219220		recv, amt, err := roy.RoyaltyInfo("1", 10000)221		urequire.NoError(t, err)222		uassert.Equal(t, artist, recv)223		uassert.Equal(t, int64(500), amt)224	})225}226227func TestSetDefaultRoyaltyValidation(cur realm, t *testing.T) {228	artist := testutils.TestAddress("artist")229230	tests := []struct {231		name     string232		receiver address233		bps      int64234		wantErr  error235	}{236		{"bps above maxBps returns ErrInvalidBps", artist, 1001, ErrInvalidBps},237		{"negative bps returns ErrInvalidBps", artist, -1, ErrInvalidBps},238		{"invalid (zero) receiver returns ErrInvalidReceiver", zeroAddress, 100, ErrInvalidReceiver},239		{"bps at maxBps boundary succeeds", artist, 1000, nil},240		{"zero bps succeeds", artist, 0, nil},241	}242243	for _, tt := range tests {244		t.Run(tt.name, func(t *testing.T) {245			_, roy, royLedger := newRoy(1000, cur)246			err := royLedger.SetDefaultRoyalty(tt.receiver, tt.bps)247			if tt.wantErr != nil {248				uassert.ErrorIs(t, err, tt.wantErr)249				_, ok := roy.DefaultRoyalty()250				uassert.False(t, ok)251				return252			}253			urequire.NoError(t, err)254			info, ok := roy.DefaultRoyalty()255			uassert.True(t, ok)256			uassert.Equal(t, tt.bps, info.Bps)257		})258	}259}260261func TestSetTokenRoyaltyValidation(cur realm, t *testing.T) {262	artist := testutils.TestAddress("artist")263264	tests := []struct {265		name     string266		mint     bool267		tid      grc721.TokenID268		receiver address269		bps      int64270		wantErr  error271	}{272		{"unminted token returns ErrTokenNotMinted", false, "404", artist, 100, ErrTokenNotMinted},273		{"minted token with bps above maxBps returns ErrInvalidBps", true, "1", artist, 1001, ErrInvalidBps},274		{"minted token with negative bps returns ErrInvalidBps", true, "1", artist, -1, ErrInvalidBps},275		{"minted token with invalid receiver returns ErrInvalidReceiver", true, "1", zeroAddress, 100, ErrInvalidReceiver},276		{"minted token at maxBps boundary succeeds", true, "1", artist, 1000, nil},277		{"minted token with valid override succeeds", true, "1", artist, 250, nil},278	}279280	for _, tt := range tests {281		t.Run(tt.name, func(t *testing.T) {282			tokenLedger, roy, royLedger := newRoy(1000, cur)283			if tt.mint {284				urequire.NoError(t, tokenLedger.Mint(testutils.TestAddress("alice"), tt.tid))285			}286287			err := royLedger.SetTokenRoyalty(tt.tid, tt.receiver, tt.bps)288			if tt.wantErr != nil {289				uassert.ErrorIs(t, err, tt.wantErr)290				_, ok := roy.TokenRoyalty(tt.tid)291				uassert.False(t, ok)292				return293			}294			urequire.NoError(t, err)295			info, ok := roy.TokenRoyalty(tt.tid)296			uassert.True(t, ok)297			uassert.Equal(t, tt.bps, info.Bps)298		})299	}300}301302func TestOnBurnClearsTokenRoyaltyKeepsDefault(cur realm, t *testing.T) {303	alice := testutils.TestAddress("alice")304	artist := testutils.TestAddress("artist")305	special := testutils.TestAddress("special")306	tokenLedger, roy, royLedger := newRoy(1000, cur)307308	urequire.NoError(t, tokenLedger.Mint(alice, "1"))309	urequire.NoError(t, royLedger.SetDefaultRoyalty(artist, 500))310	urequire.NoError(t, royLedger.SetTokenRoyalty("1", special, 250))311312	_, ok := roy.TokenRoyalty("1")313	uassert.True(t, ok)314315	urequire.NoError(t, tokenLedger.Burn("1"))316	_, ok = roy.TokenRoyalty("1")317	uassert.False(t, ok)318319	info, ok := roy.DefaultRoyalty()320	uassert.True(t, ok)321	uassert.Equal(t, artist, info.Receiver)322323	urequire.NoError(t, tokenLedger.Mint(alice, "1"))324	_, ok = roy.TokenRoyalty("1")325	uassert.False(t, ok)326	recv, amt, err := roy.RoyaltyInfo("1", 10000)327	urequire.NoError(t, err)328	uassert.Equal(t, artist, recv)329	uassert.Equal(t, int64(500), amt)330}331332func TestHooks(cur realm, t *testing.T) {333	alice := testutils.TestAddress("alice")334	bob := testutils.TestAddress("bob")335	artist := testutils.TestAddress("artist")336	special := testutils.TestAddress("special")337	tokenLedger, roy, royLedger := newRoy(1000, cur)338339	urequire.NoError(t, tokenLedger.Mint(alice, "1"))340	urequire.NoError(t, royLedger.SetDefaultRoyalty(artist, 500))341	urequire.NoError(t, royLedger.SetTokenRoyalty("1", special, 250))342343	royLedger.OnMint(alice, "1")344	royLedger.OnTransfer(alice, bob, "1")345	info, ok := roy.TokenRoyalty("1")346	uassert.True(t, ok)347	uassert.Equal(t, special, info.Receiver)348	_, ok = roy.DefaultRoyalty()349	uassert.True(t, ok)350351	royLedger.OnBurn("1")352	_, ok = roy.TokenRoyalty("1")353	uassert.False(t, ok)354	_, ok = roy.DefaultRoyalty()355	uassert.True(t, ok)356}357

Functions

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

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