diff --git a/examples/gno.land/p/demo/grc/grc721/basic_nft.gno b/examples/gno.land/p/demo/grc/grc721/basic_nft.gno index b707527c6a4..bec7338db42 100644 --- a/examples/gno.land/p/demo/grc/grc721/basic_nft.gno +++ b/examples/gno.land/p/demo/grc/grc721/basic_nft.gno @@ -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 { diff --git a/examples/gno.land/p/demo/grc/grc721/basic_nft_test.gno b/examples/gno.land/p/demo/grc/grc721/basic_nft_test.gno index 7ad378af2f7..925f1fca44b 100644 --- a/examples/gno.land/p/demo/grc/grc721/basic_nft_test.gno +++ b/examples/gno.land/p/demo/grc/grc721/basic_nft_test.gno @@ -4,6 +4,7 @@ import ( "std" "testing" + "gno.land/p/demo/testutils" "gno.land/r/demo/users" ) @@ -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) + } +} diff --git a/examples/gno.land/p/demo/grc/grc721/gno.mod b/examples/gno.land/p/demo/grc/grc721/gno.mod index ea8c9c9e52e..952e0cb8ee4 100644 --- a/examples/gno.land/p/demo/grc/grc721/gno.mod +++ b/examples/gno.land/p/demo/grc/grc721/gno.mod @@ -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 ) diff --git a/examples/gno.land/p/demo/grc/grc721/igrc721.gno b/examples/gno.land/p/demo/grc/grc721/igrc721.gno index d60308e11a1..387547a7e26 100644 --- a/examples/gno.land/p/demo/grc/grc721/igrc721.gno +++ b/examples/gno.land/p/demo/grc/grc721/igrc721.gno @@ -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 @@ -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