Skip to content

Commit

Permalink
feat(p/grc721): add SetTokenURI to IGRC721 (#1309)
Browse files Browse the repository at this point in the history
The current grc721 implementation lacks the ability to set the tokenURI
metadata for an NFT. There are placheolders inside the struct, but the
actual setter was not written. This PR add the following functionality
in the `grc` folder inside the `p` folder:

- new type grc721 interface for `TokenURI` on top of the existing
`TokenID`
- new error type in `errors.gno`
- SetTokenURI() implementation in `basic_nft.gno`
- new test added to `basic_nft_test.gno`

AFAIK there are no breaking changes in this PR, tests are passing
locally.

Let me know any issues.

---------

Co-authored-by: Morgan <[email protected]>
  • Loading branch information
irreverentsimplicity and thehowl authored Dec 21, 2023
1 parent d10aa9b commit f2034b1
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 1 deletion.
19 changes: 19 additions & 0 deletions examples/gno.land/p/demo/grc/grc721/basic_nft.gno
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,25 @@ func (s *basicNFT) TokenURI(tid TokenID) (string, error) {
return uri.(string), nil
}

func (s *basicNFT) SetTokenURI(tid TokenID, tURI TokenURI) (bool, error) {
// check for invalid TokenID
if !s.exists(tid) {
return false, ErrInvalidTokenId
}

// check for the right owner
owner, err := s.OwnerOf(tid)
if err != nil {
return false, err
}
caller := std.PrevRealm().Addr()
if caller != owner {
return false, ErrCallerIsNotOwner
}
s.tokenURIs.Set(string(tid), string(tURI))
return true, nil
}

// IsApprovedForAll returns true if operator is approved for all by the owner.
// Otherwise, returns false
func (s *basicNFT) IsApprovedForAll(owner, operator std.Address) bool {
Expand Down
45 changes: 45 additions & 0 deletions examples/gno.land/p/demo/grc/grc721/basic_nft_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"std"
"testing"

"gno.land/p/demo/testutils"
"gno.land/r/demo/users"
)

Expand Down Expand Up @@ -367,3 +368,47 @@ func TestBurn(t *testing.T) {
t.Errorf("should result in error")
}
}

func TestSetTokenURI(t *testing.T) {
dummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol)
if dummy == nil {
t.Errorf("should not be nil")
}

addr1 := users.AddressOrName("g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm")
addr2 := users.AddressOrName("g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj")
tokenURI := "http://example.com/token"

std.TestSetOrigCaller(std.Address(addr1)) // addr1

dummy.mint(addr1.Resolve(), TokenID("1"))
_, derr := dummy.SetTokenURI(TokenID("1"), TokenURI(tokenURI))

if derr != nil {
t.Errorf("Should not result in error ", derr.Error())
}

// Test case: Invalid token ID
_, err := dummy.SetTokenURI(TokenID("3"), TokenURI(tokenURI))
if err != ErrInvalidTokenId {
t.Errorf("Expected error %v, got %v", ErrInvalidTokenId, err)
}

std.TestSetOrigCaller(std.Address(addr2)) // addr2

_, cerr := dummy.SetTokenURI(TokenID("1"), TokenURI(tokenURI)) // addr2 trying to set URI for token 1
if cerr != ErrCallerIsNotOwner {
t.Errorf("Expected error %v, got %v", ErrCallerIsNotOwner, err)
}

// Test case: Retrieving TokenURI
std.TestSetOrigCaller(std.Address(addr1)) // addr1

dummyTokenURI, err := dummy.TokenURI(TokenID("1"))
if err != nil {
t.Errorf("TokenURI error: %v, ", err.Error())
}
if dummyTokenURI != tokenURI {
t.Errorf("Expected URI %v, got %v", tokenURI, dummyTokenURI)
}
}
1 change: 1 addition & 0 deletions examples/gno.land/p/demo/grc/grc721/gno.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module gno.land/p/demo/grc/grc721

require (
gno.land/p/demo/avl v0.0.0-latest
gno.land/p/demo/testutils v0.0.0-latest
gno.land/p/demo/ufmt v0.0.0-latest
gno.land/r/demo/users v0.0.0-latest
)
6 changes: 5 additions & 1 deletion examples/gno.land/p/demo/grc/grc721/igrc721.gno
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import "std"
type IGRC721 interface {
BalanceOf(owner std.Address) (uint64, error)
OwnerOf(tid TokenID) (std.Address, error)
SetTokenURI(tid TokenID, tURI TokenURI) (bool, error)
SafeTransferFrom(from, to std.Address, tid TokenID) error
TransferFrom(from, to std.Address, tid TokenID) error
Approve(approved std.Address, tid TokenID) error
Expand All @@ -13,7 +14,10 @@ type IGRC721 interface {
IsApprovedForAll(owner, operator std.Address) bool
}

type TokenID string
type (
TokenID string
TokenURI string
)

type TransferEvent struct {
From std.Address
Expand Down

0 comments on commit f2034b1

Please sign in to comment.