From 6b9ceb7bc03361059c45f28f3d10277548e57c2d Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Fri, 10 Jan 2025 16:34:01 +0100 Subject: [PATCH 01/78] initial commit --- .../r/matijamarjanovic/tokenhub/gno.mod | 1 + .../r/matijamarjanovic/tokenhub/test.gno | 54 ++++++++++++ .../r/matijamarjanovic/tokenhub/tokenhub.gno | 83 +++++++++++++++++++ 3 files changed, 138 insertions(+) create mode 100644 examples/gno.land/r/matijamarjanovic/tokenhub/gno.mod create mode 100644 examples/gno.land/r/matijamarjanovic/tokenhub/test.gno create mode 100644 examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/gno.mod b/examples/gno.land/r/matijamarjanovic/tokenhub/gno.mod new file mode 100644 index 00000000000..19deb1b830d --- /dev/null +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/gno.mod @@ -0,0 +1 @@ +module gno.land/r/matijamarjanovic/tokenhub diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/test.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/test.gno new file mode 100644 index 00000000000..6ab2812e520 --- /dev/null +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/test.gno @@ -0,0 +1,54 @@ +package tokenhub + +import ( + "std" + + "gno.land/p/demo/grc/grc20" +) + +var ( + tokenA *grc20.Token + tokenB *grc20.Token + tokenC *grc20.Token + tokenD *grc20.Token + tokenE *grc20.Token +) + +func init() { + tokenA = grc20.NewToken("Token A", "TKNA", 6) + tokenB = grc20.NewToken("Token B", "TKNB", 6) + tokenC = grc20.NewToken("Token C", "TKNC", 6) + tokenD = grc20.NewToken("Token D", "TKND", 6) + tokenE = grc20.NewToken("Token E", "TKNE", 6) + + Send5OfEach("g1ej0qca5ptsw9kfr64ey8jvfy9eacga6mpj2z0y") + RegisterToken(tokenA) + RegisterToken(tokenB) + RegisterToken(tokenC) + RegisterToken(tokenD) + RegisterToken(tokenE) +} + +func Send5OfEach(to std.Address) error { + if !to.IsValid() { + return grc20.ErrInvalidAddress + } + + if err := adminA.Mint(to, 5); err != nil { + return err + } + if err := adminB.Mint(to, 5); err != nil { + return err + } + if err := adminC.Mint(to, 5); err != nil { + return err + } + if err := adminD.Mint(to, 5); err != nil { + return err + } + if err := adminE.Mint(to, 5); err != nil { + return err + } + + return nil +} diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno new file mode 100644 index 00000000000..c5264378600 --- /dev/null +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -0,0 +1,83 @@ +package tokenhub + +import ( + "std" + "errors" + "strings" + "gno.land/p/demo/avl" + "gno.land/p/demo/grc/grc20" + "gno.land/p/moul/md" + "gno.land/p/demo/ufmt" +) + +var ( + registeredTokens = avl.NewTree() // symbol -> *grc20.Token + + ErrTokenAlreadyRegistered = errors.New("token already registered") + ErrTokenNotFound = errors.New("token not found") +) + +func RegisterToken(token *grc20.Token) error { + symbol := token.GetSymbol() + + if registeredTokens.Has(symbol) { + return ErrTokenAlreadyRegistered + } + + registeredTokens.Set(symbol, token) + return nil +} + +func getBalances(addr std.Address) string { + balances := avl.NewTree() + var sb strings.Builder + + registeredTokens.Iterate("", "", func(key string, value interface{}) bool { + token := value.(*grc20.Token) + balance := token.BalanceOf(addr) + balances.Set(key, balance) + return false + }) + + sb.WriteString("Balances: ") + balances.Iterate("", "", func(key string, value interface{}) bool { + balance := value.(uint64) + sb.WriteString(ufmt.Sprintf("%s: %d ", key, balance)) + return false + }) + + return sb.String() +} + +func GetMyBalances() string { + return getBalances(std.GetOrigCaller()) +} + +func Render(path string) string { + var sb strings.Builder + + sb.WriteString(md.H1("TokenHub")) + sb.WriteString(md.Paragraph("A central registry for GRC20 tokens on gno.land")) + + sb.WriteString(md.H2("Registered Tokens")) + + var tokenItems []string + registeredTokens.Iterate("", "", func(key string, value interface{}) bool { + token := value.(*grc20.Token) + tokenItems = append(tokenItems, ufmt.Sprintf("%s (%s) - %d decimals", + token.GetName(), + token.GetSymbol(), + token.GetDecimals())) + return false + }) + + if len(tokenItems) > 0 { + sb.WriteString(md.BulletList(tokenItems)) + } else { + sb.WriteString(md.Italic("No tokens registered yet")) + sb.WriteString("\n") + } + + return sb.String() +} + From 7ec4dc740c282a5398b6af26207e15815be61311 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Fri, 10 Jan 2025 20:05:33 +0100 Subject: [PATCH 02/78] -add getter to basic nft and igrc721 interface -change tokenhub to be a hub for nfts rather than a hub for fungible tokens --- .../gno.land/p/demo/grc/grc721/basic_nft.gno | 7 ++ .../gno.land/p/demo/grc/grc721/igrc721.gno | 3 + .../r/matijamarjanovic/tokenhub/test.gno | 72 +++++++++---------- .../r/matijamarjanovic/tokenhub/tokenhub.gno | 69 ++++++++++-------- 4 files changed, 86 insertions(+), 65 deletions(-) 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 0505aaa1c26..d2e7ca506fa 100644 --- a/examples/gno.land/p/demo/grc/grc721/basic_nft.gno +++ b/examples/gno.land/p/demo/grc/grc721/basic_nft.gno @@ -395,3 +395,10 @@ func (s *basicNFT) RenderHome() (str string) { return } + +// Then add the Getter method to your NFT types +func (n *basicNFT) Getter() NFTGetter { + return func() IGRC721 { + return n + } +} diff --git a/examples/gno.land/p/demo/grc/grc721/igrc721.gno b/examples/gno.land/p/demo/grc/grc721/igrc721.gno index 6c26c953d51..508da6744d9 100644 --- a/examples/gno.land/p/demo/grc/grc721/igrc721.gno +++ b/examples/gno.land/p/demo/grc/grc721/igrc721.gno @@ -26,3 +26,6 @@ const ( ApprovalEvent = "Approval" ApprovalForAllEvent = "ApprovalForAll" ) + +type NFTGetter func() IGRC721 + diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/test.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/test.gno index 6ab2812e520..231ddd28847 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/test.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/test.gno @@ -3,51 +3,51 @@ package tokenhub import ( "std" - "gno.land/p/demo/grc/grc20" + "gno.land/p/demo/grc/grc721" + "gno.land/p/demo/ufmt" ) - var ( - tokenA *grc20.Token - tokenB *grc20.Token - tokenC *grc20.Token - tokenD *grc20.Token - tokenE *grc20.Token + // Initialize basic NFTs using the public constructor + nftA = grc721.NewBasicNFT("Collection A", "NFTA") + nftB = grc721.NewBasicNFT("Collection B", "NFTB") + nftC = grc721.NewBasicNFT("Collection C", "NFTC") ) - func init() { - tokenA = grc20.NewToken("Token A", "TKNA", 6) - tokenB = grc20.NewToken("Token B", "TKNB", 6) - tokenC = grc20.NewToken("Token C", "TKNC", 6) - tokenD = grc20.NewToken("Token D", "TKND", 6) - tokenE = grc20.NewToken("Token E", "TKNE", 6) - - Send5OfEach("g1ej0qca5ptsw9kfr64ey8jvfy9eacga6mpj2z0y") - RegisterToken(tokenA) - RegisterToken(tokenB) - RegisterToken(tokenC) - RegisterToken(tokenD) - RegisterToken(tokenE) + + // Test address to receive NFTs + testAddr := std.Address("g1ej0qca5ptsw9kfr64ey8jvfy9eacga6mpj2z0y") + + // Mint test NFTs + MintTestNFTs(testAddr) + + // Register all NFT collections using the Getter method + RegisterNFT(func() grc721.IGRC721 { return nftA }) + RegisterNFT(func() grc721.IGRC721 { return nftB }) + RegisterNFT(func() grc721.IGRC721 { return nftC }) } -func Send5OfEach(to std.Address) error { +func MintTestNFTs(to std.Address) error { if !to.IsValid() { - return grc20.ErrInvalidAddress + return grc721.ErrInvalidAddress } - if err := adminA.Mint(to, 5); err != nil { - return err - } - if err := adminB.Mint(to, 5); err != nil { - return err - } - if err := adminC.Mint(to, 5); err != nil { - return err - } - if err := adminD.Mint(to, 5); err != nil { - return err - } - if err := adminE.Mint(to, 5); err != nil { - return err + // Mint NFTs for each collection + for i := 1; i <= 2; i++ { + // Create TokenID for each NFT + tokenIDA := grc721.TokenID(ufmt.Sprintf("NFTA_%d", i)) + if err := nftA.Mint(to, tokenIDA); err != nil { + return err + } + + tokenIDB := grc721.TokenID(ufmt.Sprintf("NFTB_%d", i)) + if err := nftB.Mint(to, tokenIDB); err != nil { + return err + } + + tokenIDC := grc721.TokenID(ufmt.Sprintf("NFTC_%d", i)) + if err := nftC.Mint(to, tokenIDC); err != nil { + return err + } } return nil diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index c5264378600..a7664a2cfab 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -3,28 +3,34 @@ package tokenhub import ( "std" "errors" - "strings" + "strings" "gno.land/p/demo/avl" - "gno.land/p/demo/grc/grc20" - "gno.land/p/moul/md" - "gno.land/p/demo/ufmt" + "gno.land/p/demo/grc/grc721" + "gno.land/p/moul/md" + "gno.land/p/demo/ufmt" ) var ( - registeredTokens = avl.NewTree() // symbol -> *grc20.Token + registeredNFTs = avl.NewTree() // symbol -> NFTGetter - ErrTokenAlreadyRegistered = errors.New("token already registered") - ErrTokenNotFound = errors.New("token not found") + ErrNFTAlreadyRegistered = errors.New("NFT already registered") + ErrNFTNotFound = errors.New("NFT not found") ) -func RegisterToken(token *grc20.Token) error { - symbol := token.GetSymbol() +func RegisterNFT(nftGetter grc721.NFTGetter) error { + nft := nftGetter() + metadata, ok := nft.(grc721.IGRC721CollectionMetadata) + if !ok { + return errors.New("NFT must implement IGRC721CollectionMetadata") + } + + symbol := metadata.Symbol() - if registeredTokens.Has(symbol) { - return ErrTokenAlreadyRegistered + if registeredNFTs.Has(symbol) { + return ErrNFTAlreadyRegistered } - registeredTokens.Set(symbol, token) + registeredNFTs.Set(symbol, nftGetter) return nil } @@ -32,14 +38,15 @@ func getBalances(addr std.Address) string { balances := avl.NewTree() var sb strings.Builder - registeredTokens.Iterate("", "", func(key string, value interface{}) bool { - token := value.(*grc20.Token) - balance := token.BalanceOf(addr) + registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { + nftGetter := value.(grc721.NFTGetter) + nft := nftGetter() + balance, _ := nft.BalanceOf(addr) balances.Set(key, balance) return false }) - sb.WriteString("Balances: ") + sb.WriteString("NFT Balances: ") balances.Iterate("", "", func(key string, value interface{}) bool { balance := value.(uint64) sb.WriteString(ufmt.Sprintf("%s: %d ", key, balance)) @@ -56,25 +63,29 @@ func GetMyBalances() string { func Render(path string) string { var sb strings.Builder - sb.WriteString(md.H1("TokenHub")) - sb.WriteString(md.Paragraph("A central registry for GRC20 tokens on gno.land")) + sb.WriteString(md.H1("NFT Hub")) + sb.WriteString(md.Paragraph("A central registry for GRC721 NFTs on gno.land")) - sb.WriteString(md.H2("Registered Tokens")) + sb.WriteString(md.H2("Registered NFTs")) - var tokenItems []string - registeredTokens.Iterate("", "", func(key string, value interface{}) bool { - token := value.(*grc20.Token) - tokenItems = append(tokenItems, ufmt.Sprintf("%s (%s) - %d decimals", - token.GetName(), - token.GetSymbol(), - token.GetDecimals())) + var nftItems []string + registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { + nftGetter := value.(grc721.NFTGetter) + nft := nftGetter() + metadata, ok := nft.(grc721.IGRC721CollectionMetadata) + if !ok { + return false + } + nftItems = append(nftItems, ufmt.Sprintf("%s (%s)", + metadata.Name(), + metadata.Symbol())) return false }) - if len(tokenItems) > 0 { - sb.WriteString(md.BulletList(tokenItems)) + if len(nftItems) > 0 { + sb.WriteString(md.BulletList(nftItems)) } else { - sb.WriteString(md.Italic("No tokens registered yet")) + sb.WriteString(md.Italic("No NFTs registered yet")) sb.WriteString("\n") } From 7970e4202e5e710a604cf4832d4a27288fe6dce8 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Fri, 10 Jan 2025 20:27:07 +0100 Subject: [PATCH 03/78] fix fmt --- .../gno.land/p/demo/grc/grc721/basic_nft.gno | 6 +- .../gno.land/p/demo/grc/grc721/igrc721.gno | 1 - .../r/matijamarjanovic/tokenhub/test.gno | 8 +- .../r/matijamarjanovic/tokenhub/tokenhub.gno | 150 +++++++++--------- 4 files changed, 83 insertions(+), 82 deletions(-) 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 d2e7ca506fa..bd754ef62fe 100644 --- a/examples/gno.land/p/demo/grc/grc721/basic_nft.gno +++ b/examples/gno.land/p/demo/grc/grc721/basic_nft.gno @@ -398,7 +398,7 @@ func (s *basicNFT) RenderHome() (str string) { // Then add the Getter method to your NFT types func (n *basicNFT) Getter() NFTGetter { - return func() IGRC721 { - return n - } + return func() IGRC721 { + return n + } } diff --git a/examples/gno.land/p/demo/grc/grc721/igrc721.gno b/examples/gno.land/p/demo/grc/grc721/igrc721.gno index 508da6744d9..054dc322f31 100644 --- a/examples/gno.land/p/demo/grc/grc721/igrc721.gno +++ b/examples/gno.land/p/demo/grc/grc721/igrc721.gno @@ -28,4 +28,3 @@ const ( ) type NFTGetter func() IGRC721 - diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/test.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/test.gno index 231ddd28847..72cc085c201 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/test.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/test.gno @@ -6,20 +6,22 @@ import ( "gno.land/p/demo/grc/grc721" "gno.land/p/demo/ufmt" ) + var ( // Initialize basic NFTs using the public constructor nftA = grc721.NewBasicNFT("Collection A", "NFTA") nftB = grc721.NewBasicNFT("Collection B", "NFTB") - nftC = grc721.NewBasicNFT("Collection C", "NFTC") + nftC = grc721.NewBasicNFT("Collection C", "NFTC") ) + func init() { // Test address to receive NFTs testAddr := std.Address("g1ej0qca5ptsw9kfr64ey8jvfy9eacga6mpj2z0y") - + // Mint test NFTs MintTestNFTs(testAddr) - + // Register all NFT collections using the Getter method RegisterNFT(func() grc721.IGRC721 { return nftA }) RegisterNFT(func() grc721.IGRC721 { return nftB }) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index a7664a2cfab..f14466cd2bb 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -1,94 +1,94 @@ package tokenhub import ( - "std" - "errors" - "strings" - "gno.land/p/demo/avl" - "gno.land/p/demo/grc/grc721" - "gno.land/p/moul/md" - "gno.land/p/demo/ufmt" + "errors" + "std" + "strings" + + "gno.land/p/demo/avl" + "gno.land/p/demo/grc/grc721" + "gno.land/p/demo/ufmt" + "gno.land/p/moul/md" ) var ( - registeredNFTs = avl.NewTree() // symbol -> NFTGetter - - ErrNFTAlreadyRegistered = errors.New("NFT already registered") - ErrNFTNotFound = errors.New("NFT not found") + registeredNFTs = avl.NewTree() // symbol -> NFTGetter + + ErrNFTAlreadyRegistered = errors.New("NFT already registered") + ErrNFTNotFound = errors.New("NFT not found") ) func RegisterNFT(nftGetter grc721.NFTGetter) error { - nft := nftGetter() - metadata, ok := nft.(grc721.IGRC721CollectionMetadata) - if !ok { - return errors.New("NFT must implement IGRC721CollectionMetadata") - } - - symbol := metadata.Symbol() - - if registeredNFTs.Has(symbol) { - return ErrNFTAlreadyRegistered - } - - registeredNFTs.Set(symbol, nftGetter) - return nil + nft := nftGetter() + metadata, ok := nft.(grc721.IGRC721CollectionMetadata) + if !ok { + return errors.New("NFT must implement IGRC721CollectionMetadata") + } + + symbol := metadata.Symbol() + + if registeredNFTs.Has(symbol) { + return ErrNFTAlreadyRegistered + } + + registeredNFTs.Set(symbol, nftGetter) + return nil } func getBalances(addr std.Address) string { - balances := avl.NewTree() - var sb strings.Builder - - registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { - nftGetter := value.(grc721.NFTGetter) - nft := nftGetter() - balance, _ := nft.BalanceOf(addr) - balances.Set(key, balance) - return false - }) - - sb.WriteString("NFT Balances: ") - balances.Iterate("", "", func(key string, value interface{}) bool { - balance := value.(uint64) - sb.WriteString(ufmt.Sprintf("%s: %d ", key, balance)) - return false - }) - - return sb.String() + balances := avl.NewTree() + var sb strings.Builder + + registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { + nftGetter := value.(grc721.NFTGetter) + nft := nftGetter() + balance, _ := nft.BalanceOf(addr) + balances.Set(key, balance) + return false + }) + + sb.WriteString("NFT Balances: ") + balances.Iterate("", "", func(key string, value interface{}) bool { + balance := value.(uint64) + sb.WriteString(ufmt.Sprintf("%s: %d ", key, balance)) + return false + }) + + return sb.String() } func GetMyBalances() string { - return getBalances(std.GetOrigCaller()) + return getBalances(std.GetOrigCaller()) } func Render(path string) string { - var sb strings.Builder - - sb.WriteString(md.H1("NFT Hub")) - sb.WriteString(md.Paragraph("A central registry for GRC721 NFTs on gno.land")) - - sb.WriteString(md.H2("Registered NFTs")) - - var nftItems []string - registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { - nftGetter := value.(grc721.NFTGetter) - nft := nftGetter() - metadata, ok := nft.(grc721.IGRC721CollectionMetadata) - if !ok { - return false - } - nftItems = append(nftItems, ufmt.Sprintf("%s (%s)", - metadata.Name(), - metadata.Symbol())) - return false - }) - - if len(nftItems) > 0 { - sb.WriteString(md.BulletList(nftItems)) - } else { - sb.WriteString(md.Italic("No NFTs registered yet")) - sb.WriteString("\n") - } - - return sb.String() -} + var sb strings.Builder + + sb.WriteString(md.H1("NFT Hub")) + sb.WriteString(md.Paragraph("A central registry for GRC721 NFTs on gno.land")) + + sb.WriteString(md.H2("Registered NFTs")) + var nftItems []string + registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { + nftGetter := value.(grc721.NFTGetter) + nft := nftGetter() + metadata, ok := nft.(grc721.IGRC721CollectionMetadata) + if !ok { + return false + } + nftItems = append(nftItems, ufmt.Sprintf("%s (%s)", + metadata.Name(), + metadata.Symbol())) + return false + }) + + if len(nftItems) > 0 { + sb.WriteString(md.BulletList(nftItems)) + } else { + sb.WriteString(md.Italic("No NFTs registered yet")) + sb.WriteString("\n") + } + + return sb.String() +} From 9c397a56e09e84aabe62f4df4538051a6a80c769 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Sat, 11 Jan 2025 19:35:42 +0100 Subject: [PATCH 04/78] - add iterate function to grc20reg to be able to get all registered tokens - match registering grc721 to grc20 registration which now go by pck path (and custom slug) instead of symbol - combine the two in the token hub --- .../gno.land/r/demo/grc20reg/grc20reg.gno | 7 ++ .../r/matijamarjanovic/tokenhub/test.gno | 41 ++++++++-- .../r/matijamarjanovic/tokenhub/tokenhub.gno | 76 ++++++++++++++++--- 3 files changed, 107 insertions(+), 17 deletions(-) diff --git a/examples/gno.land/r/demo/grc20reg/grc20reg.gno b/examples/gno.land/r/demo/grc20reg/grc20reg.gno index ff46ec94860..5490a1ce1d7 100644 --- a/examples/gno.land/r/demo/grc20reg/grc20reg.gno +++ b/examples/gno.land/r/demo/grc20reg/grc20reg.gno @@ -73,4 +73,11 @@ func Render(path string) string { } } +func Iterate(callback func(key string, token grc20.TokenGetter) bool) { + registry.Iterate("", "", func(key string, tokenI interface{}) bool { + tokenGetter := tokenI.(grc20.TokenGetter) + return callback(key, tokenGetter) + }) +} + const registerEvent = "register" diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/test.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/test.gno index 72cc085c201..13bdd888464 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/test.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/test.gno @@ -3,6 +3,7 @@ package tokenhub import ( "std" + "gno.land/p/demo/grc/grc20" "gno.land/p/demo/grc/grc721" "gno.land/p/demo/ufmt" ) @@ -12,20 +13,31 @@ var ( nftA = grc721.NewBasicNFT("Collection A", "NFTA") nftB = grc721.NewBasicNFT("Collection B", "NFTB") nftC = grc721.NewBasicNFT("Collection C", "NFTC") + + // Initialize GRC20 tokens + tokenA, tokenAAdmin = grc20.NewToken("Token A", "TOKA", 6) + tokenB, tokenBAdmin = grc20.NewToken("Token B", "TOKB", 6) + tokenC, tokenCAdmin = grc20.NewToken("Token C", "TOKC", 6) ) func init() { - - // Test address to receive NFTs + // Test address to receive tokens testAddr := std.Address("g1ej0qca5ptsw9kfr64ey8jvfy9eacga6mpj2z0y") // Mint test NFTs MintTestNFTs(testAddr) + // Mint test tokens + MintTestTokens(testAddr) // Register all NFT collections using the Getter method - RegisterNFT(func() grc721.IGRC721 { return nftA }) - RegisterNFT(func() grc721.IGRC721 { return nftB }) - RegisterNFT(func() grc721.IGRC721 { return nftC }) + RegisterNFT(func() grc721.IGRC721 { return nftA }, "nft-a") + RegisterNFT(func() grc721.IGRC721 { return nftB }, "nft-b") + RegisterNFT(func() grc721.IGRC721 { return nftC }, "nft-c") + + // Register all tokens using their Getter methods + RegisterToken(tokenA.Getter(), "token-a") + RegisterToken(tokenB.Getter(), "token-b") + RegisterToken(tokenC.Getter(), "token-c") } func MintTestNFTs(to std.Address) error { @@ -54,3 +66,22 @@ func MintTestNFTs(to std.Address) error { return nil } + +func MintTestTokens(to std.Address) error { + if !to.IsValid() { + return grc20.ErrInvalidAddress + } + + // Mint some test tokens to the address + if err := tokenAAdmin.Mint(to, 1000_000000); err != nil { // 1000 TOKA + return err + } + if err := tokenBAdmin.Mint(to, 10000_000000); err != nil { // 10000 TOKB + return err + } + if err := tokenCAdmin.Mint(to, 100_000000); err != nil { // 100 TOKC + return err + } + + return nil +} diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index f14466cd2bb..e3576b35848 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -9,29 +9,46 @@ import ( "gno.land/p/demo/grc/grc721" "gno.land/p/demo/ufmt" "gno.land/p/moul/md" + "gno.land/p/demo/grc/grc20" + "gno.land/r/demo/grc20reg" + "gno.land/p/demo/fqname" ) var ( - registeredNFTs = avl.NewTree() // symbol -> NFTGetter + registeredNFTs = avl.NewTree() // rlmPath[.slug] -> NFTGetter ErrNFTAlreadyRegistered = errors.New("NFT already registered") ErrNFTNotFound = errors.New("NFT not found") ) -func RegisterNFT(nftGetter grc721.NFTGetter) error { +func RegisterToken(tokenGetter grc20.TokenGetter, slugs ...string) { + slug := "" + if len(slugs) > 0 { + slug = slugs[0] + } + grc20reg.Register(tokenGetter, slug) +} + + +func RegisterNFT(nftGetter grc721.NFTGetter, slugs ...string) error { nft := nftGetter() metadata, ok := nft.(grc721.IGRC721CollectionMetadata) if !ok { return errors.New("NFT must implement IGRC721CollectionMetadata") } - symbol := metadata.Symbol() + rlmPath := std.PrevRealm().PkgPath() + slug := "" + if len(slugs) > 0 { + slug = slugs[0] + } + key := fqname.Construct(rlmPath, slug) - if registeredNFTs.Has(symbol) { + if registeredNFTs.Has(key) { return ErrNFTAlreadyRegistered } - registeredNFTs.Set(symbol, nftGetter) + registeredNFTs.Set(key, nftGetter) return nil } @@ -39,18 +56,34 @@ func getBalances(addr std.Address) string { balances := avl.NewTree() var sb strings.Builder + // NFT balances registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { nftGetter := value.(grc721.NFTGetter) nft := nftGetter() balance, _ := nft.BalanceOf(addr) - balances.Set(key, balance) + balances.Set("nft:"+key, balance) return false }) - sb.WriteString("NFT Balances: ") + // GRC20 balances + grc20reg.Iterate(func(key string, tokenGetter grc20.TokenGetter) bool { + token := tokenGetter() + balance := token.BalanceOf(addr) + balances.Set("token:"+key, balance) + return false + }) + + sb.WriteString("Balances:") balances.Iterate("", "", func(key string, value interface{}) bool { - balance := value.(uint64) - sb.WriteString(ufmt.Sprintf("%s: %d ", key, balance)) + if strings.HasPrefix(key, "nft:") { + balance := value.(uint64) + symbol := strings.TrimPrefix(key, "nft:") + sb.WriteString(ufmt.Sprintf("NFT %s: %d ", symbol, balance)) + } else { + balance := value.(uint64) + symbol := strings.TrimPrefix(key, "token:") + sb.WriteString(ufmt.Sprintf("Token %s: %d ", symbol, balance)) + } return false }) @@ -64,11 +97,11 @@ func GetMyBalances() string { func Render(path string) string { var sb strings.Builder - sb.WriteString(md.H1("NFT Hub")) - sb.WriteString(md.Paragraph("A central registry for GRC721 NFTs on gno.land")) + sb.WriteString(md.H1("Token Hub")) + sb.WriteString(md.Paragraph("A central registry for GRC721 NFTs and GRC20 tokens on gno.land")) + // NFT section sb.WriteString(md.H2("Registered NFTs")) - var nftItems []string registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { nftGetter := value.(grc721.NFTGetter) @@ -90,5 +123,24 @@ func Render(path string) string { sb.WriteString("\n") } + // GRC20 token section + sb.WriteString(md.H2("Registered Tokens")) + var tokenItems []string + grc20reg.Iterate(func(key string, tokenGetter grc20.TokenGetter) bool { + token := tokenGetter() + tokenItems = append(tokenItems, ufmt.Sprintf("%s (%s)", + token.GetName(), + token.GetSymbol())) + return false + }) + + if len(tokenItems) > 0 { + sb.WriteString(md.BulletList(tokenItems)) + } else { + sb.WriteString(md.Italic("No tokens registered yet")) + sb.WriteString("\n") + } + return sb.String() } + From a02b9551c3cfa6e88716ac9633c71f26938b0b1e Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Sat, 11 Jan 2025 19:38:40 +0100 Subject: [PATCH 05/78] remove unused var --- examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index e3576b35848..413125b873d 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -32,7 +32,7 @@ func RegisterToken(tokenGetter grc20.TokenGetter, slugs ...string) { func RegisterNFT(nftGetter grc721.NFTGetter, slugs ...string) error { nft := nftGetter() - metadata, ok := nft.(grc721.IGRC721CollectionMetadata) + _, ok := nft.(grc721.IGRC721CollectionMetadata) if !ok { return errors.New("NFT must implement IGRC721CollectionMetadata") } @@ -74,7 +74,7 @@ func getBalances(addr std.Address) string { }) sb.WriteString("Balances:") - balances.Iterate("", "", func(key string, value interface{}) bool { + balances.Iterate("", "", func(key string, value interface{}) bool { //should we display all even if the balance is 0? if strings.HasPrefix(key, "nft:") { balance := value.(uint64) symbol := strings.TrimPrefix(key, "nft:") From 093134151859543d8216c57e7447967559fda84e Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Sat, 11 Jan 2025 20:24:23 +0100 Subject: [PATCH 06/78] add getter to grc1155 --- .../gno.land/p/demo/grc/grc1155/basic_grc1155_token.gno | 6 ++++++ examples/gno.land/p/demo/grc/grc1155/igrc1155.gno | 2 ++ 2 files changed, 8 insertions(+) diff --git a/examples/gno.land/p/demo/grc/grc1155/basic_grc1155_token.gno b/examples/gno.land/p/demo/grc/grc1155/basic_grc1155_token.gno index f152ee90e79..6043132e1dc 100644 --- a/examples/gno.land/p/demo/grc/grc1155/basic_grc1155_token.gno +++ b/examples/gno.land/p/demo/grc/grc1155/basic_grc1155_token.gno @@ -345,3 +345,9 @@ func (s *basicGRC1155Token) RenderHome() (str string) { return } + +func (mt *basicGRC1155Token) Getter() MultiTokenGetter { + return func() IGRC1155 { + return mt + } +} diff --git a/examples/gno.land/p/demo/grc/grc1155/igrc1155.gno b/examples/gno.land/p/demo/grc/grc1155/igrc1155.gno index 5d524e36773..0e7b947cd29 100644 --- a/examples/gno.land/p/demo/grc/grc1155/igrc1155.gno +++ b/examples/gno.land/p/demo/grc/grc1155/igrc1155.gno @@ -38,3 +38,5 @@ type ApprovalForAllEvent struct { type UpdateURIEvent struct { URI string } + +type MultiTokenGetter func() IGRC1155 From 04ee40e65f97149215126555d56a5b490f4e686e Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Sat, 11 Jan 2025 22:02:55 +0100 Subject: [PATCH 07/78] -add support for grc1155 (not perfect) --- .../r/matijamarjanovic/tokenhub/test.gno | 54 ++++++++---- .../r/matijamarjanovic/tokenhub/tokenhub.gno | 86 +++++++++++++++++-- 2 files changed, 117 insertions(+), 23 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/test.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/test.gno index 13bdd888464..ddb2ad8a1bd 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/test.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/test.gno @@ -5,39 +5,43 @@ import ( "gno.land/p/demo/grc/grc20" "gno.land/p/demo/grc/grc721" + "gno.land/p/demo/grc/grc1155" "gno.land/p/demo/ufmt" ) var ( - // Initialize basic NFTs using the public constructor nftA = grc721.NewBasicNFT("Collection A", "NFTA") nftB = grc721.NewBasicNFT("Collection B", "NFTB") nftC = grc721.NewBasicNFT("Collection C", "NFTC") - // Initialize GRC20 tokens tokenA, tokenAAdmin = grc20.NewToken("Token A", "TOKA", 6) tokenB, tokenBAdmin = grc20.NewToken("Token B", "TOKB", 6) tokenC, tokenCAdmin = grc20.NewToken("Token C", "TOKC", 6) + + mtGameItems = grc1155.NewBasicGRC1155Token("https://game.example.com/items/{id}.json") + mtArtworks = grc1155.NewBasicGRC1155Token("https://art.example.com/pieces/{id}.json") + mtCollectibles = grc1155.NewBasicGRC1155Token("https://collect.example.com/cards/{id}.json") ) func init() { - // Test address to receive tokens testAddr := std.Address("g1ej0qca5ptsw9kfr64ey8jvfy9eacga6mpj2z0y") - // Mint test NFTs - MintTestNFTs(testAddr) - // Mint test tokens - MintTestTokens(testAddr) - - // Register all NFT collections using the Getter method RegisterNFT(func() grc721.IGRC721 { return nftA }, "nft-a") RegisterNFT(func() grc721.IGRC721 { return nftB }, "nft-b") RegisterNFT(func() grc721.IGRC721 { return nftC }, "nft-c") - // Register all tokens using their Getter methods RegisterToken(tokenA.Getter(), "token-a") RegisterToken(tokenB.Getter(), "token-b") RegisterToken(tokenC.Getter(), "token-c") + + RegisterGRC1155Token(mtGameItems.Getter(), "sword", "game-sword") + RegisterGRC1155Token(mtGameItems.Getter(), "potion", "game-potion") + RegisterGRC1155Token(mtArtworks.Getter(), "artwork_1", "art-piece-1") + RegisterGRC1155Token(mtCollectibles.Getter(), "rare_card", "card-rare") + + MintTestNFTs(testAddr) + MintTestTokens(testAddr) + MintTestMultiTokens(testAddr) } func MintTestNFTs(to std.Address) error { @@ -45,9 +49,7 @@ func MintTestNFTs(to std.Address) error { return grc721.ErrInvalidAddress } - // Mint NFTs for each collection for i := 1; i <= 2; i++ { - // Create TokenID for each NFT tokenIDA := grc721.TokenID(ufmt.Sprintf("NFTA_%d", i)) if err := nftA.Mint(to, tokenIDA); err != nil { return err @@ -72,14 +74,34 @@ func MintTestTokens(to std.Address) error { return grc20.ErrInvalidAddress } - // Mint some test tokens to the address - if err := tokenAAdmin.Mint(to, 1000_000000); err != nil { // 1000 TOKA + if err := tokenAAdmin.Mint(to, 1000_000000); err != nil { + return err + } + if err := tokenBAdmin.Mint(to, 10000_000000); err != nil { + return err + } + if err := tokenCAdmin.Mint(to, 100_000000); err != nil { + return err + } + + return nil +} + +func MintTestMultiTokens(to std.Address) error { + if !to.IsValid() { + return grc1155.ErrInvalidAddress + } + + if err := mtGameItems.SafeMint(to, "sword", 10); err != nil { + return err + } + if err := mtGameItems.SafeMint(to, "potion", 50); err != nil { return err } - if err := tokenBAdmin.Mint(to, 10000_000000); err != nil { // 10000 TOKB + if err := mtArtworks.SafeMint(to, "artwork_1", 1); err != nil { return err } - if err := tokenCAdmin.Mint(to, 100_000000); err != nil { // 100 TOKC + if err := mtCollectibles.SafeMint(to, "rare_card", 1); err != nil { return err } diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index 413125b873d..03c410f0526 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -7,29 +7,39 @@ import ( "gno.land/p/demo/avl" "gno.land/p/demo/grc/grc721" + "gno.land/p/demo/grc/grc1155" "gno.land/p/demo/ufmt" - "gno.land/p/moul/md" "gno.land/p/demo/grc/grc20" "gno.land/r/demo/grc20reg" "gno.land/p/demo/fqname" + "gno.land/p/moul/md" ) +// Add this struct to store GRC1155 token info +type GRC1155TokenInfo struct { + Collection grc1155.MultiTokenGetter + TokenID string +} + var ( registeredNFTs = avl.NewTree() // rlmPath[.slug] -> NFTGetter + registeredMTs = avl.NewTree() // rlmPath[.slug] -> GRC1155TokenInfo ErrNFTAlreadyRegistered = errors.New("NFT already registered") - ErrNFTNotFound = errors.New("NFT not found") + ErrNFTNotFound = errors.New("NFT not found") + ErrMTAlreadyRegistered = errors.New("Multi-token already registered") + ErrMTNotFound = errors.New("Multi-token not found") + ErrMTInfoNotFound = errors.New("Multi-token info not found") ) func RegisterToken(tokenGetter grc20.TokenGetter, slugs ...string) { slug := "" if len(slugs) > 0 { - slug = slugs[0] + slug = slugs[0] } grc20reg.Register(tokenGetter, slug) } - func RegisterNFT(nftGetter grc721.NFTGetter, slugs ...string) error { nft := nftGetter() _, ok := nft.(grc721.IGRC721CollectionMetadata) @@ -52,6 +62,25 @@ func RegisterNFT(nftGetter grc721.NFTGetter, slugs ...string) error { return nil } +func RegisterGRC1155Token(mtGetter grc1155.MultiTokenGetter, tokenID string, slugs ...string) error { + rlmPath := std.PrevRealm().PkgPath() + slug := "" + if len(slugs) > 0 { + slug = slugs[0] + } + key := fqname.Construct(rlmPath, slug) + + if registeredMTs.Has(key) { + return ErrMTAlreadyRegistered + } + + registeredMTs.Set(key, GRC1155TokenInfo{ + Collection: mtGetter, + TokenID: tokenID, + }) + return nil +} + func getBalances(addr std.Address) string { balances := avl.NewTree() var sb strings.Builder @@ -73,16 +102,40 @@ func getBalances(addr std.Address) string { return false }) + // GRC1155 token balances + registeredMTs.Iterate("", "", func(key string, value interface{}) bool { + info := value.(GRC1155TokenInfo) + mt := info.Collection() + balance, err := mt.BalanceOf(addr, grc1155.TokenID(info.TokenID)) + if err == nil { + balances.Set("mt:"+key, balance) + } + return false + }) + sb.WriteString("Balances:") balances.Iterate("", "", func(key string, value interface{}) bool { //should we display all even if the balance is 0? - if strings.HasPrefix(key, "nft:") { + switch { + case strings.HasPrefix(key, "nft:"): balance := value.(uint64) symbol := strings.TrimPrefix(key, "nft:") sb.WriteString(ufmt.Sprintf("NFT %s: %d ", symbol, balance)) - } else { + case strings.HasPrefix(key, "token:"): balance := value.(uint64) symbol := strings.TrimPrefix(key, "token:") sb.WriteString(ufmt.Sprintf("Token %s: %d ", symbol, balance)) + case strings.HasPrefix(key, "mt:"): + balance := value.(uint64) + symbol := strings.TrimPrefix(key, "mt:") + info, exists := registeredMTs.Get(strings.TrimPrefix(key, "mt:")) + if !exists { + return errors.New(ErrMTInfoNotFound) + } + collectionPath := strings.TrimPrefix(symbol, std.PrevRealm().PkgPath()+".") + sb.WriteString(ufmt.Sprintf("MultiToken %s.%s: %d ", + collectionPath, + info.(GRC1155TokenInfo).TokenID, + balance)) } return false }) @@ -98,7 +151,7 @@ func Render(path string) string { var sb strings.Builder sb.WriteString(md.H1("Token Hub")) - sb.WriteString(md.Paragraph("A central registry for GRC721 NFTs and GRC20 tokens on gno.land")) + sb.WriteString(md.Paragraph("A central registry for GRC721 NFTs, GRC20 tokens, and GRC1155 multi-tokens on gno.land")) // NFT section sb.WriteString(md.H2("Registered NFTs")) @@ -141,6 +194,25 @@ func Render(path string) string { sb.WriteString("\n") } + // GRC1155 section + sb.WriteString(md.H2("Registered Multi-Tokens")) + var mtItems []string + registeredMTs.Iterate("", "", func(key string, value interface{}) bool { + info := value.(GRC1155TokenInfo) + mtItems = append(mtItems, ufmt.Sprintf("TokenID: %s (Collection: %s)", + info.TokenID, + strings.TrimPrefix(key, std.PrevRealm().PkgPath()+"."), + )) + return false + }) + + if len(mtItems) > 0 { + sb.WriteString(md.BulletList(mtItems)) + } else { + sb.WriteString(md.Italic("No multi-tokens registered yet")) + sb.WriteString("\n") + } + return sb.String() } From af1631fbb06b6c18bac046f7efc862d21b5e72ac Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Sun, 12 Jan 2025 15:40:50 +0100 Subject: [PATCH 08/78] display different token types on different pages --- .../r/matijamarjanovic/tokenhub/tokenhub.gno | 135 +++++++++++------- 1 file changed, 81 insertions(+), 54 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index 03c410f0526..d73d0c68acf 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -15,7 +15,6 @@ import ( "gno.land/p/moul/md" ) -// Add this struct to store GRC1155 token info type GRC1155TokenInfo struct { Collection grc1155.MultiTokenGetter TokenID string @@ -129,7 +128,7 @@ func getBalances(addr std.Address) string { symbol := strings.TrimPrefix(key, "mt:") info, exists := registeredMTs.Get(strings.TrimPrefix(key, "mt:")) if !exists { - return errors.New(ErrMTInfoNotFound) + panic("MT info not found") } collectionPath := strings.TrimPrefix(symbol, std.PrevRealm().PkgPath()+".") sb.WriteString(ufmt.Sprintf("MultiToken %s.%s: %d ", @@ -150,67 +149,95 @@ func GetMyBalances() string { func Render(path string) string { var sb strings.Builder - sb.WriteString(md.H1("Token Hub")) - sb.WriteString(md.Paragraph("A central registry for GRC721 NFTs, GRC20 tokens, and GRC1155 multi-tokens on gno.land")) + switch path { + case "": + // home page + sb.WriteString(md.H1("Token Hub")) + sb.WriteString(md.Paragraph("A central registry for GRC721 NFTs, GRC20 tokens, and GRC1155 multi-tokens on gno.land")) + + links := []string{ + md.Link("GRC20 Tokens", "http://localhost:8888/r/matijamarjanovic/tokenhub:token"), + md.Link("GRC721 NFTs", "http://localhost:8888/r/matijamarjanovic/tokenhub:nft"), + md.Link("GRC1155 Multi-Tokens", "http://localhost:8888/r/matijamarjanovic/tokenhub:mt"), + } + sb.WriteString(md.BulletList(links)) + + case "token": + // GRC20 token section + sb.WriteString(md.H1("GRC20 Tokens")) + var tokenItems []string + grc20reg.Iterate(func(key string, tokenGetter grc20.TokenGetter) bool { + token := tokenGetter() + tokenItems = append(tokenItems, ufmt.Sprintf("%s (%s)", + md.Bold(token.GetName()), + md.InlineCode(token.GetSymbol()))) + return false + }) - // NFT section - sb.WriteString(md.H2("Registered NFTs")) - var nftItems []string - registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { - nftGetter := value.(grc721.NFTGetter) - nft := nftGetter() - metadata, ok := nft.(grc721.IGRC721CollectionMetadata) - if !ok { + if len(tokenItems) > 0 { + sb.WriteString(md.BulletList(tokenItems)) + } else { + sb.WriteString(md.Italic("No tokens registered yet")) + sb.WriteString("\n") + } + + case "nft": + // NFT section + sb.WriteString(md.H1("GRC721 NFTs")) + var nftItems []string + registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { + nftGetter := value.(grc721.NFTGetter) + nft := nftGetter() + metadata, ok := nft.(grc721.IGRC721CollectionMetadata) + if !ok { + return false + } + nftItems = append(nftItems, ufmt.Sprintf("%s (%s)", + md.Bold(metadata.Name()), + md.InlineCode(metadata.Symbol()))) return false + }) + + if len(nftItems) > 0 { + sb.WriteString(md.BulletList(nftItems)) + } else { + sb.WriteString(md.Italic("No NFTs registered yet")) + sb.WriteString("\n") } - nftItems = append(nftItems, ufmt.Sprintf("%s (%s)", - metadata.Name(), - metadata.Symbol())) - return false - }) - if len(nftItems) > 0 { - sb.WriteString(md.BulletList(nftItems)) - } else { - sb.WriteString(md.Italic("No NFTs registered yet")) - sb.WriteString("\n") - } + case "mt": + // GRC1155 section + sb.WriteString(md.H1("GRC1155 Multi-Tokens")) + var mtItems []string + registeredMTs.Iterate("", "", func(key string, value interface{}) bool { + info := value.(GRC1155TokenInfo) + mtItems = append(mtItems, ufmt.Sprintf("%s: %s", + md.Bold("TokenID"), + md.InlineCode(info.TokenID) + + " (Collection: " + + md.InlineCode(strings.TrimPrefix(key, std.PrevRealm().PkgPath()+".")) + + ")")) + return false + }) - // GRC20 token section - sb.WriteString(md.H2("Registered Tokens")) - var tokenItems []string - grc20reg.Iterate(func(key string, tokenGetter grc20.TokenGetter) bool { - token := tokenGetter() - tokenItems = append(tokenItems, ufmt.Sprintf("%s (%s)", - token.GetName(), - token.GetSymbol())) - return false - }) + if len(mtItems) > 0 { + sb.WriteString(md.BulletList(mtItems)) + } else { + sb.WriteString(md.Italic("No multi-tokens registered yet")) + sb.WriteString("\n") + } - if len(tokenItems) > 0 { - sb.WriteString(md.BulletList(tokenItems)) - } else { - sb.WriteString(md.Italic("No tokens registered yet")) - sb.WriteString("\n") + default: + sb.WriteString(md.H1("404 Not Found")) + sb.WriteString(md.Paragraph("The requested page does not exist.")) + sb.WriteString(md.Link("Back to home", "http://localhost:8888/r/matijamarjanovic/tokenhub")) } - // GRC1155 section - sb.WriteString(md.H2("Registered Multi-Tokens")) - var mtItems []string - registeredMTs.Iterate("", "", func(key string, value interface{}) bool { - info := value.(GRC1155TokenInfo) - mtItems = append(mtItems, ufmt.Sprintf("TokenID: %s (Collection: %s)", - info.TokenID, - strings.TrimPrefix(key, std.PrevRealm().PkgPath()+"."), - )) - return false - }) - - if len(mtItems) > 0 { - sb.WriteString(md.BulletList(mtItems)) - } else { - sb.WriteString(md.Italic("No multi-tokens registered yet")) + // Add navigation footer if not on home page + if path != "" { sb.WriteString("\n") + sb.WriteString(md.HorizontalRule()) + sb.WriteString(md.Link("Back to home", "http://localhost:8888/r/matijamarjanovic/tokenhub")) } return sb.String() From 42152f4acddfde594ed43c1299a947002157aa61 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Sun, 12 Jan 2025 16:00:12 +0100 Subject: [PATCH 09/78] add pagination for grc20 (this includes adding 1 more iteration function to grc20reg.gno but this might help with iteration in that realm as well) --- .../gno.land/r/demo/grc20reg/grc20reg.gno | 20 ++++++++ .../r/matijamarjanovic/tokenhub/tokenhub.gno | 51 +++++++++++++++---- 2 files changed, 61 insertions(+), 10 deletions(-) diff --git a/examples/gno.land/r/demo/grc20reg/grc20reg.gno b/examples/gno.land/r/demo/grc20reg/grc20reg.gno index 5490a1ce1d7..a01610a4b41 100644 --- a/examples/gno.land/r/demo/grc20reg/grc20reg.gno +++ b/examples/gno.land/r/demo/grc20reg/grc20reg.gno @@ -80,4 +80,24 @@ func Iterate(callback func(key string, token grc20.TokenGetter) bool) { }) } +// iterate through n items starting from the startKey (pagination) +func IterateN(startKey string, n int, callback func(key string, token grc20.TokenGetter) bool) string { + var lastKey string + count := 0 + + registry.Iterate(startKey, "", func(key string, tokenI interface{}) bool { + if count >= n { + return true + } + + tokenGetter := tokenI.(grc20.TokenGetter) + lastKey = key + count++ + + return callback(key, tokenGetter) + }) + + return lastKey +} + const registerEvent = "register" diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index d73d0c68acf..4ffb90f7324 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -4,6 +4,7 @@ import ( "errors" "std" "strings" + "strconv" "gno.land/p/demo/avl" "gno.land/p/demo/grc/grc721" @@ -29,6 +30,8 @@ var ( ErrMTAlreadyRegistered = errors.New("Multi-token already registered") ErrMTNotFound = errors.New("Multi-token not found") ErrMTInfoNotFound = errors.New("Multi-token info not found") + + pageSize = 10 // tokens per page ) func RegisterToken(tokenGetter grc20.TokenGetter, slugs ...string) { @@ -149,24 +152,40 @@ func GetMyBalances() string { func Render(path string) string { var sb strings.Builder - switch path { - case "": + switch { + case path == "": // home page sb.WriteString(md.H1("Token Hub")) sb.WriteString(md.Paragraph("A central registry for GRC721 NFTs, GRC20 tokens, and GRC1155 multi-tokens on gno.land")) links := []string{ - md.Link("GRC20 Tokens", "http://localhost:8888/r/matijamarjanovic/tokenhub:token"), + md.Link("GRC20 Tokens", "http://localhost:8888/r/matijamarjanovic/tokenhub:token_1"), md.Link("GRC721 NFTs", "http://localhost:8888/r/matijamarjanovic/tokenhub:nft"), md.Link("GRC1155 Multi-Tokens", "http://localhost:8888/r/matijamarjanovic/tokenhub:mt"), } sb.WriteString(md.BulletList(links)) - case "token": - // GRC20 token section + case strings.HasPrefix(path, "token"): // grc20 tokens sb.WriteString(md.H1("GRC20 Tokens")) var tokenItems []string - grc20reg.Iterate(func(key string, tokenGetter grc20.TokenGetter) bool { + + page := 1 + if strings.HasPrefix(path, "token_") { // get page number from path + pageStr := strings.TrimPrefix(path, "token_") + if p, err := strconv.Atoi(pageStr); err == nil { + page = p + } + } + + startKey := "" + if page > 1 { + lastKey := grc20reg.IterateN("", (page-1)*pageSize, func(key string, tokenGetter grc20.TokenGetter) bool { // start position + return false + }) + startKey = lastKey + } + + lastKey := grc20reg.IterateN(startKey, pageSize, func(key string, tokenGetter grc20.TokenGetter) bool { // get current page tokens token := tokenGetter() tokenItems = append(tokenItems, ufmt.Sprintf("%s (%s)", md.Bold(token.GetName()), @@ -176,13 +195,26 @@ func Render(path string) string { if len(tokenItems) > 0 { sb.WriteString(md.BulletList(tokenItems)) + + sb.WriteString("\n") + sb.WriteString(md.HorizontalRule()) + + if page > 1 { + prevPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:token_%d", page-1) + sb.WriteString(md.Link("Previous", prevPage)) + sb.WriteString(" | ") + } + + if len(tokenItems) == pageSize && lastKey != "" { + nextPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:token_%d", page+1) + sb.WriteString(md.Link("Next", nextPage)) + } } else { sb.WriteString(md.Italic("No tokens registered yet")) sb.WriteString("\n") } - case "nft": - // NFT section + case strings.HasPrefix(path, "nft"): // grc721 nfts sb.WriteString(md.H1("GRC721 NFTs")) var nftItems []string registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { @@ -205,8 +237,7 @@ func Render(path string) string { sb.WriteString("\n") } - case "mt": - // GRC1155 section + case strings.HasPrefix(path, "mt"): // grc1155 multi-tokens sb.WriteString(md.H1("GRC1155 Multi-Tokens")) var mtItems []string registeredMTs.Iterate("", "", func(key string, value interface{}) bool { From c07116f7aa60259d9df7173bcd356db2ca2ff55d Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Sun, 12 Jan 2025 16:08:38 +0100 Subject: [PATCH 10/78] add pagination to grc721 page as well --- .../r/matijamarjanovic/tokenhub/tokenhub.gno | 44 +++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index 4ffb90f7324..8a08741a742 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -31,7 +31,7 @@ var ( ErrMTNotFound = errors.New("Multi-token not found") ErrMTInfoNotFound = errors.New("Multi-token info not found") - pageSize = 10 // tokens per page + pageSize = 1 // tokens per page ) func RegisterToken(tokenGetter grc20.TokenGetter, slugs ...string) { @@ -160,8 +160,8 @@ func Render(path string) string { links := []string{ md.Link("GRC20 Tokens", "http://localhost:8888/r/matijamarjanovic/tokenhub:token_1"), - md.Link("GRC721 NFTs", "http://localhost:8888/r/matijamarjanovic/tokenhub:nft"), - md.Link("GRC1155 Multi-Tokens", "http://localhost:8888/r/matijamarjanovic/tokenhub:mt"), + md.Link("GRC721 NFTs", "http://localhost:8888/r/matijamarjanovic/tokenhub:nft_1"), + md.Link("GRC1155 Multi-Tokens", "http://localhost:8888/r/matijamarjanovic/tokenhub:mt_1"), } sb.WriteString(md.BulletList(links)) @@ -217,21 +217,59 @@ func Render(path string) string { case strings.HasPrefix(path, "nft"): // grc721 nfts sb.WriteString(md.H1("GRC721 NFTs")) var nftItems []string + + page := 1 + if strings.HasPrefix(path, "nft_") { + pageStr := strings.TrimPrefix(path, "nft_") + if p, err := strconv.Atoi(pageStr); err == nil { + page = p + } + } + + startIdx := (page - 1) * pageSize + count := 0 + currentIdx := 0 + registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { + if currentIdx < startIdx { + currentIdx++ + return false + } + if count >= pageSize { + return true + } + nftGetter := value.(grc721.NFTGetter) nft := nftGetter() metadata, ok := nft.(grc721.IGRC721CollectionMetadata) if !ok { return false } + nftItems = append(nftItems, ufmt.Sprintf("%s (%s)", md.Bold(metadata.Name()), md.InlineCode(metadata.Symbol()))) + + count++ + currentIdx++ return false }) if len(nftItems) > 0 { sb.WriteString(md.BulletList(nftItems)) + sb.WriteString("\n") + sb.WriteString(md.HorizontalRule()) + + if page > 1 { + prevPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:nft_%d", page-1) + sb.WriteString(md.Link("Previous", prevPage)) + sb.WriteString(" | ") + } + + if len(nftItems) == pageSize { + nextPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:nft_%d", page+1) + sb.WriteString(md.Link("Next", nextPage)) + } } else { sb.WriteString(md.Italic("No NFTs registered yet")) sb.WriteString("\n") From c6ea85834d2a0f84d52dd53a134d0cfea9e4f6b9 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Sun, 12 Jan 2025 16:12:25 +0100 Subject: [PATCH 11/78] add pagination for grc1155 --- .../r/matijamarjanovic/tokenhub/tokenhub.gno | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index 8a08741a742..94a95b939a8 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -278,7 +278,28 @@ func Render(path string) string { case strings.HasPrefix(path, "mt"): // grc1155 multi-tokens sb.WriteString(md.H1("GRC1155 Multi-Tokens")) var mtItems []string + + page := 1 + if strings.HasPrefix(path, "mt_") { + pageStr := strings.TrimPrefix(path, "mt_") + if p, err := strconv.Atoi(pageStr); err == nil { + page = p + } + } + + startIdx := (page - 1) * pageSize + count := 0 + currentIdx := 0 + registeredMTs.Iterate("", "", func(key string, value interface{}) bool { + if currentIdx < startIdx { + currentIdx++ + return false + } + if count >= pageSize { + return true + } + info := value.(GRC1155TokenInfo) mtItems = append(mtItems, ufmt.Sprintf("%s: %s", md.Bold("TokenID"), @@ -286,11 +307,27 @@ func Render(path string) string { " (Collection: " + md.InlineCode(strings.TrimPrefix(key, std.PrevRealm().PkgPath()+".")) + ")")) + + count++ + currentIdx++ return false }) if len(mtItems) > 0 { sb.WriteString(md.BulletList(mtItems)) + sb.WriteString("\n") + sb.WriteString(md.HorizontalRule()) + + if page > 1 { + prevPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:mt_%d", page-1) + sb.WriteString(md.Link("Previous", prevPage)) + sb.WriteString(" | ") + } + + if len(mtItems) == pageSize { + nextPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:mt_%d", page+1) + sb.WriteString(md.Link("Next", nextPage)) + } } else { sb.WriteString(md.Italic("No multi-tokens registered yet")) sb.WriteString("\n") @@ -302,7 +339,7 @@ func Render(path string) string { sb.WriteString(md.Link("Back to home", "http://localhost:8888/r/matijamarjanovic/tokenhub")) } - // Add navigation footer if not on home page + // navigation footer if not on home page if path != "" { sb.WriteString("\n") sb.WriteString(md.HorizontalRule()) From b5967e4a9c539de9794a0a099717e678f9ddf003 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Sun, 12 Jan 2025 16:16:44 +0100 Subject: [PATCH 12/78] add a getter function for tokens with non zero balances --- .../r/matijamarjanovic/tokenhub/tokenhub.gno | 192 ++++++++++++------ 1 file changed, 131 insertions(+), 61 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index 94a95b939a8..3a2d8dcd2fb 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -83,70 +83,12 @@ func RegisterGRC1155Token(mtGetter grc1155.MultiTokenGetter, tokenID string, slu return nil } -func getBalances(addr std.Address) string { - balances := avl.NewTree() - var sb strings.Builder - - // NFT balances - registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { - nftGetter := value.(grc721.NFTGetter) - nft := nftGetter() - balance, _ := nft.BalanceOf(addr) - balances.Set("nft:"+key, balance) - return false - }) - - // GRC20 balances - grc20reg.Iterate(func(key string, tokenGetter grc20.TokenGetter) bool { - token := tokenGetter() - balance := token.BalanceOf(addr) - balances.Set("token:"+key, balance) - return false - }) - - // GRC1155 token balances - registeredMTs.Iterate("", "", func(key string, value interface{}) bool { - info := value.(GRC1155TokenInfo) - mt := info.Collection() - balance, err := mt.BalanceOf(addr, grc1155.TokenID(info.TokenID)) - if err == nil { - balances.Set("mt:"+key, balance) - } - return false - }) - - sb.WriteString("Balances:") - balances.Iterate("", "", func(key string, value interface{}) bool { //should we display all even if the balance is 0? - switch { - case strings.HasPrefix(key, "nft:"): - balance := value.(uint64) - symbol := strings.TrimPrefix(key, "nft:") - sb.WriteString(ufmt.Sprintf("NFT %s: %d ", symbol, balance)) - case strings.HasPrefix(key, "token:"): - balance := value.(uint64) - symbol := strings.TrimPrefix(key, "token:") - sb.WriteString(ufmt.Sprintf("Token %s: %d ", symbol, balance)) - case strings.HasPrefix(key, "mt:"): - balance := value.(uint64) - symbol := strings.TrimPrefix(key, "mt:") - info, exists := registeredMTs.Get(strings.TrimPrefix(key, "mt:")) - if !exists { - panic("MT info not found") - } - collectionPath := strings.TrimPrefix(symbol, std.PrevRealm().PkgPath()+".") - sb.WriteString(ufmt.Sprintf("MultiToken %s.%s: %d ", - collectionPath, - info.(GRC1155TokenInfo).TokenID, - balance)) - } - return false - }) - - return sb.String() +func GetMyBalancesAll() string { + return getBalances(std.GetOrigCaller()) } func GetMyBalances() string { - return getBalances(std.GetOrigCaller()) + return getNonZeroBalances(std.GetOrigCaller()) } func Render(path string) string { @@ -349,3 +291,131 @@ func Render(path string) string { return sb.String() } + +func getBalances(addr std.Address) string { + balances := avl.NewTree() + var sb strings.Builder + + // NFT balances + registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { + nftGetter := value.(grc721.NFTGetter) + nft := nftGetter() + balance, _ := nft.BalanceOf(addr) + balances.Set("nft:"+key, balance) + return false + }) + + // GRC20 balances + grc20reg.Iterate(func(key string, tokenGetter grc20.TokenGetter) bool { + token := tokenGetter() + balance := token.BalanceOf(addr) + balances.Set("token:"+key, balance) + return false + }) + + // GRC1155 token balances + registeredMTs.Iterate("", "", func(key string, value interface{}) bool { + info := value.(GRC1155TokenInfo) + mt := info.Collection() + balance, err := mt.BalanceOf(addr, grc1155.TokenID(info.TokenID)) + if err == nil { + balances.Set("mt:"+key, balance) + } + return false + }) + + sb.WriteString("Balances:") + balances.Iterate("", "", func(key string, value interface{}) bool { + switch { + case strings.HasPrefix(key, "nft:"): + balance := value.(uint64) + symbol := strings.TrimPrefix(key, "nft:") + sb.WriteString(ufmt.Sprintf("NFT %s: %d ", symbol, balance)) + case strings.HasPrefix(key, "token:"): + balance := value.(uint64) + symbol := strings.TrimPrefix(key, "token:") + sb.WriteString(ufmt.Sprintf("Token %s: %d ", symbol, balance)) + case strings.HasPrefix(key, "mt:"): + balance := value.(uint64) + symbol := strings.TrimPrefix(key, "mt:") + info, exists := registeredMTs.Get(strings.TrimPrefix(key, "mt:")) + if !exists { + panic("MT info not found") + } + collectionPath := strings.TrimPrefix(symbol, std.PrevRealm().PkgPath()+".") + sb.WriteString(ufmt.Sprintf("MultiToken %s.%s: %d ", + collectionPath, + info.(GRC1155TokenInfo).TokenID, + balance)) + } + return false + }) + + return sb.String() +} + +func getNonZeroBalances(addr std.Address) string { + balances := avl.NewTree() + var sb strings.Builder + + // NFT balances + registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { + nftGetter := value.(grc721.NFTGetter) + nft := nftGetter() + balance, _ := nft.BalanceOf(addr) + if balance > 0 { + balances.Set("nft:"+key, balance) + } + return false + }) + + // GRC20 balances + grc20reg.Iterate(func(key string, tokenGetter grc20.TokenGetter) bool { + token := tokenGetter() + balance := token.BalanceOf(addr) + if balance > 0 { + balances.Set("token:"+key, balance) + } + return false + }) + + // GRC1155 token balances + registeredMTs.Iterate("", "", func(key string, value interface{}) bool { + info := value.(GRC1155TokenInfo) + mt := info.Collection() + balance, err := mt.BalanceOf(addr, grc1155.TokenID(info.TokenID)) + if err == nil && balance > 0 { + balances.Set("mt:"+key, balance) + } + return false + }) + + sb.WriteString("Non-Zero Balances:") + balances.Iterate("", "", func(key string, value interface{}) bool { + switch { + case strings.HasPrefix(key, "nft:"): + balance := value.(uint64) + symbol := strings.TrimPrefix(key, "nft:") + sb.WriteString(ufmt.Sprintf("NFT %s: %d ", symbol, balance)) + case strings.HasPrefix(key, "token:"): + balance := value.(uint64) + symbol := strings.TrimPrefix(key, "token:") + sb.WriteString(ufmt.Sprintf("Token %s: %d ", symbol, balance)) + case strings.HasPrefix(key, "mt:"): + balance := value.(uint64) + symbol := strings.TrimPrefix(key, "mt:") + info, exists := registeredMTs.Get(strings.TrimPrefix(key, "mt:")) + if !exists { + panic("MT info not found") + } + collectionPath := strings.TrimPrefix(symbol, std.PrevRealm().PkgPath()+".") + sb.WriteString(ufmt.Sprintf("MultiToken %s.%s: %d ", + collectionPath, + info.(GRC1155TokenInfo).TokenID, + balance)) + } + return false + }) + + return sb.String() +} From 8727cc68617fbed46eb22e4f36ed298e0a0660ac Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Sun, 12 Jan 2025 17:13:48 +0100 Subject: [PATCH 13/78] -update saving grc1155 so it makes most sense -update the getbalances functions so they're easier to parse --- .../gno.land/r/matijamarjanovic/test/gno.mod | 1 + .../{tokenhub => test}/test.gno | 23 +++++----- .../r/matijamarjanovic/tokenhub/tokenhub.gno | 46 ++++++++----------- 3 files changed, 31 insertions(+), 39 deletions(-) create mode 100644 examples/gno.land/r/matijamarjanovic/test/gno.mod rename examples/gno.land/r/matijamarjanovic/{tokenhub => test}/test.gno (76%) diff --git a/examples/gno.land/r/matijamarjanovic/test/gno.mod b/examples/gno.land/r/matijamarjanovic/test/gno.mod new file mode 100644 index 00000000000..aa2814cdb1e --- /dev/null +++ b/examples/gno.land/r/matijamarjanovic/test/gno.mod @@ -0,0 +1 @@ +module gno.land/r/matijamarjanovic/test diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/test.gno b/examples/gno.land/r/matijamarjanovic/test/test.gno similarity index 76% rename from examples/gno.land/r/matijamarjanovic/tokenhub/test.gno rename to examples/gno.land/r/matijamarjanovic/test/test.gno index ddb2ad8a1bd..5c2dc2f8c9c 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/test.gno +++ b/examples/gno.land/r/matijamarjanovic/test/test.gno @@ -1,4 +1,4 @@ -package tokenhub +package test import ( "std" @@ -7,6 +7,7 @@ import ( "gno.land/p/demo/grc/grc721" "gno.land/p/demo/grc/grc1155" "gno.land/p/demo/ufmt" + "gno.land/r/matijamarjanovic/tokenhub" ) var ( @@ -26,18 +27,18 @@ var ( func init() { testAddr := std.Address("g1ej0qca5ptsw9kfr64ey8jvfy9eacga6mpj2z0y") - RegisterNFT(func() grc721.IGRC721 { return nftA }, "nft-a") - RegisterNFT(func() grc721.IGRC721 { return nftB }, "nft-b") - RegisterNFT(func() grc721.IGRC721 { return nftC }, "nft-c") + tokenhub.RegisterNFT(func() grc721.IGRC721 { return nftA }, "nft-a") + tokenhub.RegisterNFT(func() grc721.IGRC721 { return nftB }, "nft-b") + tokenhub.RegisterNFT(func() grc721.IGRC721 { return nftC }, "nft-c") - RegisterToken(tokenA.Getter(), "token-a") - RegisterToken(tokenB.Getter(), "token-b") - RegisterToken(tokenC.Getter(), "token-c") + tokenhub.RegisterToken(tokenA.Getter(), "token-a") + tokenhub.RegisterToken(tokenB.Getter(), "token-b") + tokenhub.RegisterToken(tokenC.Getter(), "token-c") - RegisterGRC1155Token(mtGameItems.Getter(), "sword", "game-sword") - RegisterGRC1155Token(mtGameItems.Getter(), "potion", "game-potion") - RegisterGRC1155Token(mtArtworks.Getter(), "artwork_1", "art-piece-1") - RegisterGRC1155Token(mtCollectibles.Getter(), "rare_card", "card-rare") + tokenhub.RegisterGRC1155Token(mtGameItems.Getter(), "sword") + tokenhub.RegisterGRC1155Token(mtGameItems.Getter(), "potion") + tokenhub.RegisterGRC1155Token(mtArtworks.Getter(), "artwork_1") + tokenhub.RegisterGRC1155Token(mtCollectibles.Getter(), "rare_card") MintTestNFTs(testAddr) MintTestTokens(testAddr) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index 3a2d8dcd2fb..ab82d934d1e 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -32,6 +32,7 @@ var ( ErrMTInfoNotFound = errors.New("Multi-token info not found") pageSize = 1 // tokens per page + test = "" ) func RegisterToken(tokenGetter grc20.TokenGetter, slugs ...string) { @@ -64,13 +65,12 @@ func RegisterNFT(nftGetter grc721.NFTGetter, slugs ...string) error { return nil } -func RegisterGRC1155Token(mtGetter grc1155.MultiTokenGetter, tokenID string, slugs ...string) error { +// for grc1155 is a little bit different, since the one collection can have multiple tokens, this is why don't use slugs but tokenIDs, +// and the collection is saved as a part of the :value in the node +func RegisterGRC1155Token(mtGetter grc1155.MultiTokenGetter, tokenID string) error { rlmPath := std.PrevRealm().PkgPath() - slug := "" - if len(slugs) > 0 { - slug = slugs[0] - } - key := fqname.Construct(rlmPath, slug) + + key := fqname.Construct(rlmPath, tokenID) if registeredMTs.Has(key) { return ErrMTAlreadyRegistered @@ -93,6 +93,7 @@ func GetMyBalances() string { func Render(path string) string { var sb strings.Builder + sb.WriteString(test) switch { case path == "": @@ -324,28 +325,23 @@ func getBalances(addr std.Address) string { return false }) - sb.WriteString("Balances:") + sb.WriteString("Balances: ") balances.Iterate("", "", func(key string, value interface{}) bool { switch { case strings.HasPrefix(key, "nft:"): balance := value.(uint64) symbol := strings.TrimPrefix(key, "nft:") - sb.WriteString(ufmt.Sprintf("NFT %s: %d ", symbol, balance)) + sb.WriteString(ufmt.Sprintf("NFT:%s:%d,", symbol, balance)) + sb.WriteString(ufmt.Sprintf("Token:%s:%d,", symbol, balance)) case strings.HasPrefix(key, "token:"): balance := value.(uint64) symbol := strings.TrimPrefix(key, "token:") - sb.WriteString(ufmt.Sprintf("Token %s: %d ", symbol, balance)) + sb.WriteString(ufmt.Sprintf("Token:%s:%d,", symbol, balance)) case strings.HasPrefix(key, "mt:"): balance := value.(uint64) symbol := strings.TrimPrefix(key, "mt:") - info, exists := registeredMTs.Get(strings.TrimPrefix(key, "mt:")) - if !exists { - panic("MT info not found") - } - collectionPath := strings.TrimPrefix(symbol, std.PrevRealm().PkgPath()+".") - sb.WriteString(ufmt.Sprintf("MultiToken %s.%s: %d ", - collectionPath, - info.(GRC1155TokenInfo).TokenID, + sb.WriteString(ufmt.Sprintf("MultiToken:%s:%d,", + symbol, balance)) } return false @@ -390,28 +386,22 @@ func getNonZeroBalances(addr std.Address) string { return false }) - sb.WriteString("Non-Zero Balances:") + sb.WriteString("Non-Zero Balances: ") balances.Iterate("", "", func(key string, value interface{}) bool { switch { case strings.HasPrefix(key, "nft:"): balance := value.(uint64) symbol := strings.TrimPrefix(key, "nft:") - sb.WriteString(ufmt.Sprintf("NFT %s: %d ", symbol, balance)) + sb.WriteString(ufmt.Sprintf("NFT:%s:%d,", symbol, balance)) case strings.HasPrefix(key, "token:"): balance := value.(uint64) symbol := strings.TrimPrefix(key, "token:") - sb.WriteString(ufmt.Sprintf("Token %s: %d ", symbol, balance)) + sb.WriteString(ufmt.Sprintf("Token:%s:%d,", symbol, balance)) case strings.HasPrefix(key, "mt:"): balance := value.(uint64) symbol := strings.TrimPrefix(key, "mt:") - info, exists := registeredMTs.Get(strings.TrimPrefix(key, "mt:")) - if !exists { - panic("MT info not found") - } - collectionPath := strings.TrimPrefix(symbol, std.PrevRealm().PkgPath()+".") - sb.WriteString(ufmt.Sprintf("MultiToken %s.%s: %d ", - collectionPath, - info.(GRC1155TokenInfo).TokenID, + sb.WriteString(ufmt.Sprintf("MultiToken:%s:%d,", + symbol, balance)) } return false From 0186c2d280ab1272e90586129f856d7fee5b7dc1 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Sun, 12 Jan 2025 17:27:19 +0100 Subject: [PATCH 14/78] add get and must get for all token types --- .../gno.land/r/matijamarjanovic/test/test.gno | 1 + .../r/matijamarjanovic/tokenhub/tokenhub.gno | 44 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/examples/gno.land/r/matijamarjanovic/test/test.gno b/examples/gno.land/r/matijamarjanovic/test/test.gno index 5c2dc2f8c9c..1084dd48d57 100644 --- a/examples/gno.land/r/matijamarjanovic/test/test.gno +++ b/examples/gno.land/r/matijamarjanovic/test/test.gno @@ -43,6 +43,7 @@ func init() { MintTestNFTs(testAddr) MintTestTokens(testAddr) MintTestMultiTokens(testAddr) + } func MintTestNFTs(to std.Address) error { diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index ab82d934d1e..98e7aec523b 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -91,6 +91,47 @@ func GetMyBalances() string { return getNonZeroBalances(std.GetOrigCaller()) } +func GetGRC20(key string) grc20.TokenGetter { + return grc20reg.Get(key) +} + +func MustGetGRC20(key string) grc20.TokenGetter { + return grc20reg.MustGet(key) +} + +func GetGRC721(key string) grc721.NFTGetter { + nftGetter, ok := registeredNFTs.Get(key) + if !ok { + return nil + } + return nftGetter.(grc721.NFTGetter) +} + +func MustGetGRC721(key string) grc721.NFTGetter { + nftGetter := GetGRC721(key) + if nftGetter == nil { + panic("unknown NFT: " + key) + } + return nftGetter +} + +func GetGRC1155(key string) (GRC1155TokenInfo, bool) { + info, ok := registeredMTs.Get(key) + if !ok { + return GRC1155TokenInfo{}, false + } + return info.(GRC1155TokenInfo), true +} + +func MustGetGRC1155(key string) GRC1155TokenInfo { + info, ok := GetGRC1155(key) + if !ok { + panic("unknown multi-token: " + key) + } + return info +} + + func Render(path string) string { var sb strings.Builder sb.WriteString(test) @@ -293,6 +334,8 @@ func Render(path string) string { } + + func getBalances(addr std.Address) string { balances := avl.NewTree() var sb strings.Builder @@ -409,3 +452,4 @@ func getNonZeroBalances(addr std.Address) string { return sb.String() } + From 3181a38642b711ec8b4111d5ac21529775eb59b8 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Sun, 12 Jan 2025 18:18:19 +0100 Subject: [PATCH 15/78] fix fmt --- .../r/matijamarjanovic/tokenhub/tokenhub.gno | 85 +++++++++---------- 1 file changed, 40 insertions(+), 45 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index 98e7aec523b..f97ab5bb934 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -3,17 +3,17 @@ package tokenhub import ( "errors" "std" - "strings" "strconv" + "strings" "gno.land/p/demo/avl" - "gno.land/p/demo/grc/grc721" + "gno.land/p/demo/fqname" "gno.land/p/demo/grc/grc1155" - "gno.land/p/demo/ufmt" "gno.land/p/demo/grc/grc20" - "gno.land/r/demo/grc20reg" - "gno.land/p/demo/fqname" + "gno.land/p/demo/grc/grc721" + "gno.land/p/demo/ufmt" "gno.land/p/moul/md" + "gno.land/r/demo/grc20reg" ) type GRC1155TokenInfo struct { @@ -23,22 +23,22 @@ type GRC1155TokenInfo struct { var ( registeredNFTs = avl.NewTree() // rlmPath[.slug] -> NFTGetter - registeredMTs = avl.NewTree() // rlmPath[.slug] -> GRC1155TokenInfo + registeredMTs = avl.NewTree() // rlmPath[.slug] -> GRC1155TokenInfo ErrNFTAlreadyRegistered = errors.New("NFT already registered") - ErrNFTNotFound = errors.New("NFT not found") - ErrMTAlreadyRegistered = errors.New("Multi-token already registered") - ErrMTNotFound = errors.New("Multi-token not found") - ErrMTInfoNotFound = errors.New("Multi-token info not found") + ErrNFTNotFound = errors.New("NFT not found") + ErrMTAlreadyRegistered = errors.New("Multi-token already registered") + ErrMTNotFound = errors.New("Multi-token not found") + ErrMTInfoNotFound = errors.New("Multi-token info not found") pageSize = 1 // tokens per page - test = "" + test = "" ) func RegisterToken(tokenGetter grc20.TokenGetter, slugs ...string) { slug := "" if len(slugs) > 0 { - slug = slugs[0] + slug = slugs[0] } grc20reg.Register(tokenGetter, slug) } @@ -131,7 +131,6 @@ func MustGetGRC1155(key string) GRC1155TokenInfo { return info } - func Render(path string) string { var sb strings.Builder sb.WriteString(test) @@ -141,7 +140,7 @@ func Render(path string) string { // home page sb.WriteString(md.H1("Token Hub")) sb.WriteString(md.Paragraph("A central registry for GRC721 NFTs, GRC20 tokens, and GRC1155 multi-tokens on gno.land")) - + links := []string{ md.Link("GRC20 Tokens", "http://localhost:8888/r/matijamarjanovic/tokenhub:token_1"), md.Link("GRC721 NFTs", "http://localhost:8888/r/matijamarjanovic/tokenhub:nft_1"), @@ -152,7 +151,7 @@ func Render(path string) string { case strings.HasPrefix(path, "token"): // grc20 tokens sb.WriteString(md.H1("GRC20 Tokens")) var tokenItems []string - + page := 1 if strings.HasPrefix(path, "token_") { // get page number from path pageStr := strings.TrimPrefix(path, "token_") @@ -160,7 +159,7 @@ func Render(path string) string { page = p } } - + startKey := "" if page > 1 { lastKey := grc20reg.IterateN("", (page-1)*pageSize, func(key string, tokenGetter grc20.TokenGetter) bool { // start position @@ -168,7 +167,7 @@ func Render(path string) string { }) startKey = lastKey } - + lastKey := grc20reg.IterateN(startKey, pageSize, func(key string, tokenGetter grc20.TokenGetter) bool { // get current page tokens token := tokenGetter() tokenItems = append(tokenItems, ufmt.Sprintf("%s (%s)", @@ -179,16 +178,16 @@ func Render(path string) string { if len(tokenItems) > 0 { sb.WriteString(md.BulletList(tokenItems)) - + sb.WriteString("\n") sb.WriteString(md.HorizontalRule()) - + if page > 1 { prevPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:token_%d", page-1) sb.WriteString(md.Link("Previous", prevPage)) sb.WriteString(" | ") } - + if len(tokenItems) == pageSize && lastKey != "" { nextPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:token_%d", page+1) sb.WriteString(md.Link("Next", nextPage)) @@ -201,7 +200,7 @@ func Render(path string) string { case strings.HasPrefix(path, "nft"): // grc721 nfts sb.WriteString(md.H1("GRC721 NFTs")) var nftItems []string - + page := 1 if strings.HasPrefix(path, "nft_") { pageStr := strings.TrimPrefix(path, "nft_") @@ -213,7 +212,7 @@ func Render(path string) string { startIdx := (page - 1) * pageSize count := 0 currentIdx := 0 - + registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { if currentIdx < startIdx { currentIdx++ @@ -222,18 +221,18 @@ func Render(path string) string { if count >= pageSize { return true } - + nftGetter := value.(grc721.NFTGetter) nft := nftGetter() metadata, ok := nft.(grc721.IGRC721CollectionMetadata) if !ok { return false } - + nftItems = append(nftItems, ufmt.Sprintf("%s (%s)", md.Bold(metadata.Name()), md.InlineCode(metadata.Symbol()))) - + count++ currentIdx++ return false @@ -243,13 +242,13 @@ func Render(path string) string { sb.WriteString(md.BulletList(nftItems)) sb.WriteString("\n") sb.WriteString(md.HorizontalRule()) - + if page > 1 { prevPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:nft_%d", page-1) sb.WriteString(md.Link("Previous", prevPage)) sb.WriteString(" | ") } - + if len(nftItems) == pageSize { nextPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:nft_%d", page+1) sb.WriteString(md.Link("Next", nextPage)) @@ -262,7 +261,7 @@ func Render(path string) string { case strings.HasPrefix(path, "mt"): // grc1155 multi-tokens sb.WriteString(md.H1("GRC1155 Multi-Tokens")) var mtItems []string - + page := 1 if strings.HasPrefix(path, "mt_") { pageStr := strings.TrimPrefix(path, "mt_") @@ -274,7 +273,7 @@ func Render(path string) string { startIdx := (page - 1) * pageSize count := 0 currentIdx := 0 - + registeredMTs.Iterate("", "", func(key string, value interface{}) bool { if currentIdx < startIdx { currentIdx++ @@ -283,15 +282,15 @@ func Render(path string) string { if count >= pageSize { return true } - + info := value.(GRC1155TokenInfo) mtItems = append(mtItems, ufmt.Sprintf("%s: %s", md.Bold("TokenID"), - md.InlineCode(info.TokenID) + - " (Collection: " + - md.InlineCode(strings.TrimPrefix(key, std.PrevRealm().PkgPath()+".")) + - ")")) - + md.InlineCode(info.TokenID)+ + " (Collection: "+ + md.InlineCode(strings.TrimPrefix(key, std.PrevRealm().PkgPath()+"."))+ + ")")) + count++ currentIdx++ return false @@ -301,13 +300,13 @@ func Render(path string) string { sb.WriteString(md.BulletList(mtItems)) sb.WriteString("\n") sb.WriteString(md.HorizontalRule()) - + if page > 1 { prevPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:mt_%d", page-1) sb.WriteString(md.Link("Previous", prevPage)) sb.WriteString(" | ") } - + if len(mtItems) == pageSize { nextPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:mt_%d", page+1) sb.WriteString(md.Link("Next", nextPage)) @@ -333,9 +332,6 @@ func Render(path string) string { return sb.String() } - - - func getBalances(addr std.Address) string { balances := avl.NewTree() var sb strings.Builder @@ -383,8 +379,8 @@ func getBalances(addr std.Address) string { case strings.HasPrefix(key, "mt:"): balance := value.(uint64) symbol := strings.TrimPrefix(key, "mt:") - sb.WriteString(ufmt.Sprintf("MultiToken:%s:%d,", - symbol, + sb.WriteString(ufmt.Sprintf("MultiToken:%s:%d,", + symbol, balance)) } return false @@ -443,8 +439,8 @@ func getNonZeroBalances(addr std.Address) string { case strings.HasPrefix(key, "mt:"): balance := value.(uint64) symbol := strings.TrimPrefix(key, "mt:") - sb.WriteString(ufmt.Sprintf("MultiToken:%s:%d,", - symbol, + sb.WriteString(ufmt.Sprintf("MultiToken:%s:%d,", + symbol, balance)) } return false @@ -452,4 +448,3 @@ func getNonZeroBalances(addr std.Address) string { return sb.String() } - From 3978697d2a704fdf6c3b5d3fe6e7a0261fc4f170 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Mon, 13 Jan 2025 17:19:15 +0100 Subject: [PATCH 16/78] add namespace support --- .../r/matijamarjanovic/tokenhub/tokenhub.gno | 57 ++++++++++++++++--- 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index f97ab5bb934..fd7b46e61c9 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -14,6 +14,7 @@ import ( "gno.land/p/demo/ufmt" "gno.land/p/moul/md" "gno.land/r/demo/grc20reg" + "gno.land/r/demo/users" ) type GRC1155TokenInfo struct { @@ -148,7 +149,7 @@ func Render(path string) string { } sb.WriteString(md.BulletList(links)) - case strings.HasPrefix(path, "token"): // grc20 tokens + case strings.HasPrefix(path, "token"): // grc20 sb.WriteString(md.H1("GRC20 Tokens")) var tokenItems []string @@ -197,7 +198,7 @@ func Render(path string) string { sb.WriteString("\n") } - case strings.HasPrefix(path, "nft"): // grc721 nfts + case strings.HasPrefix(path, "nft"): // grc721 sb.WriteString(md.H1("GRC721 NFTs")) var nftItems []string @@ -258,7 +259,7 @@ func Render(path string) string { sb.WriteString("\n") } - case strings.HasPrefix(path, "mt"): // grc1155 multi-tokens + case strings.HasPrefix(path, "mt"): // grc1155 sb.WriteString(md.H1("GRC1155 Multi-Tokens")) var mtItems []string @@ -333,10 +334,13 @@ func Render(path string) string { } func getBalances(addr std.Address) string { + if addr == "" { + panic("invalid address") + } balances := avl.NewTree() var sb strings.Builder - // NFT balances + // GRC721 registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { nftGetter := value.(grc721.NFTGetter) nft := nftGetter() @@ -345,7 +349,7 @@ func getBalances(addr std.Address) string { return false }) - // GRC20 balances + // GRC20 grc20reg.Iterate(func(key string, tokenGetter grc20.TokenGetter) bool { token := tokenGetter() balance := token.BalanceOf(addr) @@ -353,7 +357,7 @@ func getBalances(addr std.Address) string { return false }) - // GRC1155 token balances + // GRC1155 registeredMTs.Iterate("", "", func(key string, value interface{}) bool { info := value.(GRC1155TokenInfo) mt := info.Collection() @@ -390,10 +394,13 @@ func getBalances(addr std.Address) string { } func getNonZeroBalances(addr std.Address) string { + if addr == "" { + panic("invalid address") + } balances := avl.NewTree() var sb strings.Builder - // NFT balances + // GRC721 registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { nftGetter := value.(grc721.NFTGetter) nft := nftGetter() @@ -404,7 +411,7 @@ func getNonZeroBalances(addr std.Address) string { return false }) - // GRC20 balances + // GRC20 grc20reg.Iterate(func(key string, tokenGetter grc20.TokenGetter) bool { token := tokenGetter() balance := token.BalanceOf(addr) @@ -414,7 +421,7 @@ func getNonZeroBalances(addr std.Address) string { return false }) - // GRC1155 token balances + // GRC1155 registeredMTs.Iterate("", "", func(key string, value interface{}) bool { info := value.(GRC1155TokenInfo) mt := info.Collection() @@ -448,3 +455,35 @@ func getNonZeroBalances(addr std.Address) string { return sb.String() } + + +// these are just for testing purposes right now +func GetBalancesAll(input string) string { + addr := getAddressForUsername(input) + if addr == "" { + panic("invalid address or username: " + input) + } + return getBalances(addr) +} + +func GetBalances(input string) string { + addr := getAddressForUsername(input) + if addr == "" { + panic("invalid address or username: " + input) + } + return getNonZeroBalances(addr) +} + +func getAddressForUsername(addrOrName string) std.Address { + addr := std.Address(addrOrName) + + if addr.IsValid() { + return addr + } + if user := users.GetUserByName(addrOrName); user != nil { + return user.Address + } + + return "" +} + From fadc509b4226ec8d2945c6f7f69fc92c5fabce67 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Mon, 13 Jan 2025 20:12:45 +0100 Subject: [PATCH 17/78] fix fmt --- .../r/matijamarjanovic/tokenhub/tokenhub.gno | 53 +++++++++---------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index fd7b46e61c9..92aed4bdb0c 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -14,7 +14,7 @@ import ( "gno.land/p/demo/ufmt" "gno.land/p/moul/md" "gno.land/r/demo/grc20reg" - "gno.land/r/demo/users" + "gno.land/r/demo/users" ) type GRC1155TokenInfo struct { @@ -132,6 +132,22 @@ func MustGetGRC1155(key string) GRC1155TokenInfo { return info } +func GetBalancesAll(input string) string { + addr := getAddressForUsername(input) + if addr == "" { + panic("invalid address or username: " + input) + } + return getBalances(addr) +} + +func GetBalances(input string) string { + addr := getAddressForUsername(input) + if addr == "" { + panic("invalid address or username: " + input) + } + return getNonZeroBalances(addr) +} + func Render(path string) string { var sb strings.Builder sb.WriteString(test) @@ -149,7 +165,7 @@ func Render(path string) string { } sb.WriteString(md.BulletList(links)) - case strings.HasPrefix(path, "token"): // grc20 + case strings.HasPrefix(path, "token"): // grc20 sb.WriteString(md.H1("GRC20 Tokens")) var tokenItems []string @@ -198,7 +214,7 @@ func Render(path string) string { sb.WriteString("\n") } - case strings.HasPrefix(path, "nft"): // grc721 + case strings.HasPrefix(path, "nft"): // grc721 sb.WriteString(md.H1("GRC721 NFTs")) var nftItems []string @@ -259,7 +275,7 @@ func Render(path string) string { sb.WriteString("\n") } - case strings.HasPrefix(path, "mt"): // grc1155 + case strings.HasPrefix(path, "mt"): // grc1155 sb.WriteString(md.H1("GRC1155 Multi-Tokens")) var mtItems []string @@ -340,7 +356,7 @@ func getBalances(addr std.Address) string { balances := avl.NewTree() var sb strings.Builder - // GRC721 + // GRC721 registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { nftGetter := value.(grc721.NFTGetter) nft := nftGetter() @@ -349,7 +365,7 @@ func getBalances(addr std.Address) string { return false }) - // GRC20 + // GRC20 grc20reg.Iterate(func(key string, tokenGetter grc20.TokenGetter) bool { token := tokenGetter() balance := token.BalanceOf(addr) @@ -400,7 +416,7 @@ func getNonZeroBalances(addr std.Address) string { balances := avl.NewTree() var sb strings.Builder - // GRC721 + // GRC721 registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { nftGetter := value.(grc721.NFTGetter) nft := nftGetter() @@ -411,7 +427,7 @@ func getNonZeroBalances(addr std.Address) string { return false }) - // GRC20 + // GRC20 grc20reg.Iterate(func(key string, tokenGetter grc20.TokenGetter) bool { token := tokenGetter() balance := token.BalanceOf(addr) @@ -421,7 +437,7 @@ func getNonZeroBalances(addr std.Address) string { return false }) - // GRC1155 + // GRC1155 registeredMTs.Iterate("", "", func(key string, value interface{}) bool { info := value.(GRC1155TokenInfo) mt := info.Collection() @@ -456,24 +472,6 @@ func getNonZeroBalances(addr std.Address) string { return sb.String() } - -// these are just for testing purposes right now -func GetBalancesAll(input string) string { - addr := getAddressForUsername(input) - if addr == "" { - panic("invalid address or username: " + input) - } - return getBalances(addr) -} - -func GetBalances(input string) string { - addr := getAddressForUsername(input) - if addr == "" { - panic("invalid address or username: " + input) - } - return getNonZeroBalances(addr) -} - func getAddressForUsername(addrOrName string) std.Address { addr := std.Address(addrOrName) @@ -486,4 +484,3 @@ func getAddressForUsername(addrOrName string) std.Address { return "" } - From d3af61ce4ef2a6b070e5ca9bcbfeeef65347d46f Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 14 Jan 2025 02:06:24 +0100 Subject: [PATCH 18/78] add testing --- .../r/matijamarjanovic/tokenhub/tokenhub.gno | 20 +-- .../tokenhub/tokenhub_test.gno | 127 ++++++++++++++++++ 2 files changed, 137 insertions(+), 10 deletions(-) create mode 100644 examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index 92aed4bdb0c..fb1d77fe092 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -32,7 +32,7 @@ var ( ErrMTNotFound = errors.New("Multi-token not found") ErrMTInfoNotFound = errors.New("Multi-token info not found") - pageSize = 1 // tokens per page + pageSize = 2 // tokens per page test = "" ) @@ -187,9 +187,10 @@ func Render(path string) string { lastKey := grc20reg.IterateN(startKey, pageSize, func(key string, tokenGetter grc20.TokenGetter) bool { // get current page tokens token := tokenGetter() - tokenItems = append(tokenItems, ufmt.Sprintf("%s (%s)", + tokenItems = append(tokenItems, ufmt.Sprintf("%s (%s) [%s]", md.Bold(token.GetName()), - md.InlineCode(token.GetSymbol()))) + md.InlineCode(token.GetSymbol()), + md.InlineCode(key))) return false }) @@ -246,9 +247,10 @@ func Render(path string) string { return false } - nftItems = append(nftItems, ufmt.Sprintf("%s (%s)", + nftItems = append(nftItems, ufmt.Sprintf("%s (%s) [%s]", md.Bold(metadata.Name()), - md.InlineCode(metadata.Symbol()))) + md.InlineCode(metadata.Symbol()), + md.InlineCode(key))) count++ currentIdx++ @@ -301,12 +303,10 @@ func Render(path string) string { } info := value.(GRC1155TokenInfo) - mtItems = append(mtItems, ufmt.Sprintf("%s: %s", + mtItems = append(mtItems, ufmt.Sprintf("%s: %s [%s]", md.Bold("TokenID"), - md.InlineCode(info.TokenID)+ - " (Collection: "+ - md.InlineCode(strings.TrimPrefix(key, std.PrevRealm().PkgPath()+"."))+ - ")")) + md.InlineCode(info.TokenID), + md.InlineCode(key))) count++ currentIdx++ diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno new file mode 100644 index 00000000000..5f1fcf37286 --- /dev/null +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno @@ -0,0 +1,127 @@ +package tokenhub + +import ( + "std" + "strings" + "testing" + + "gno.land/p/demo/grc/grc1155" + "gno.land/p/demo/grc/grc20" + "gno.land/p/demo/grc/grc721" + "gno.land/p/demo/uassert" + "gno.land/p/demo/urequire" +) + +func setupTest() { + //std.TestSetOrigCaller(std.DerivePkgAddr("gno.land/r/matijamarjanovic/home")) + std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/home")) + std.TestSetOrigPkgAddr(std.DerivePkgAddr("gno.land/r/matijamarjanovic/home")) +} + +func TestTokenRegistration(t *testing.T) { + std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/home")) + std.TestSetOrigPkgAddr(std.DerivePkgAddr("gno.land/r/matijamarjanovic/home")) + + token, _ := grc20.NewToken("Test Token", "TEST", 6) + RegisterToken(token.Getter(), "test_token") + + + getter := GetGRC20("gno.land/r/matijamarjanovic/tokenhub.test_token") + urequire.True(t, getter != nil, "Should retrieve registered token") + + retrievedToken := getter() + uassert.Equal(t, "Test Token", retrievedToken.GetName(), "Token name should match") + uassert.Equal(t, "TEST", retrievedToken.GetSymbol(), "Token symbol should match") +} + +func TestNFTRegistration(t *testing.T) { + //ako probas da namestis realm koji je testiran sa settestrealm ne vraca se path + //std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/home")) + std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/home")) + std.TestSetOrigPkgAddr(std.DerivePkgAddr("gno.land/r/matijamarjanovic/home")) + + nft := grc721.NewBasicNFT("Test NFT", "TNFT") + err := RegisterNFT(nft.Getter(), "test_nft") + urequire.NoError(t, err, "Should register NFT without error") + + getter := GetGRC721("gno.land/r/matijamarjanovic/home.test_nft") + urequire.True(t, getter != nil, "Should retrieve registered NFT") + + retrievedNFT := getter() + metadata, ok := retrievedNFT.(grc721.IGRC721CollectionMetadata) + urequire.True(t, ok, "NFT should implement IGRC721CollectionMetadata") + uassert.Equal(t, "Test NFT", metadata.Name(), "NFT name should match") + uassert.Equal(t, "TNFT", metadata.Symbol(), "NFT symbol should match") + + err = RegisterNFT(nft.Getter(), "test_nft") + uassert.True(t, err!=nil, "Should not allow duplicate registration") +} + +func TestGRC1155Registration(t *testing.T) { + std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/home")) + std.TestSetOrigPkgAddr(std.DerivePkgAddr("gno.land/r/matijamarjanovic/home")) + + mt := grc1155.NewBasicGRC1155Token("test-uri") + err := RegisterGRC1155Token(mt.Getter(), "1") + urequire.NoError(t, err, "Should register multi-token without error") + + info, exists := GetGRC1155("gno.land/r/matijamarjanovic/home.1") + uassert.True(t, exists, "Should find registered multi-token") + urequire.True(t, info.Collection != nil, "Should retrieve multi-token collection") + uassert.Equal(t, "1", info.TokenID, "Token ID should match") + + err = RegisterGRC1155Token(mt.Getter(), "1") + uassert.True(t, err!=nil, "Should not allow duplicate registration") +} + +func TestBalanceRetrieval(t *testing.T) { + std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/home")) + std.TestSetOrigPkgAddr(std.DerivePkgAddr("gno.land/r/matijamarjanovic/home")) + + addr := std.Address("g1ej0qca5ptsw9kfr64ey8jvfy9eacga6mpj2z0y") + + token, ledger := grc20.NewToken("Test Token", "TEST", 6) + RegisterToken(token.Getter(), "test_tokenn") + ledger.Mint(addr, 1000) + + nft := grc721.NewBasicNFT("Test NFT", "TNFT") + RegisterNFT(nft.Getter(), "test_nftt") + nft.Mint(addr, grc721.TokenID("str")) + + mt := grc1155.NewBasicGRC1155Token("test-uri") + RegisterGRC1155Token(mt.Getter(), "11") + mt.SafeMint(addr, grc1155.TokenID("11"), 5) + + balances := GetBalancesAll(string(addr)) + uassert.True(t, strings.Contains(balances, "Token:gno.land/r/matijamarjanovic/tokenhub.test_tokenn:1000"), "Should show correct GRC20 balance") + uassert.True(t, strings.Contains(balances, "NFT:gno.land/r/matijamarjanovic/home.test_nftt:1"), "Should show correct NFT balance") + uassert.True(t, strings.Contains(balances, "MultiToken:gno.land/r/matijamarjanovic/home.11:5"), "Should show multi-token balance") + + nonZeroBalances := GetBalances(string(addr)) + uassert.True(t, strings.Contains(nonZeroBalances, "Token:gno.land/r/matijamarjanovic/tokenhub.test_tokenn:1000"), "Should show non-zero GRC20 balance") +} + +func TestErrorCases(t *testing.T) { + std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/home")) + std.TestSetOrigPkgAddr(std.DerivePkgAddr("gno.land/r/matijamarjanovic/home")) + + nft := grc721.NewBasicNFT("Test NFT", "TNFT") + err := RegisterNFT(nft.Getter(), "test_nft") + uassert.True(t, err!=nil, "Should not allow duplicate registration") + + err = RegisterNFT(nft.Getter(), "test_nft") + uassert.True(t, err!=nil, "Should not allow duplicate registration") + + mt := grc1155.NewBasicGRC1155Token("test-uri") + err = RegisterGRC1155Token(mt.Getter(), "1") + uassert.True(t, err!=nil, "Should not allow duplicate registration") + + err = RegisterGRC1155Token(mt.Getter(), "1") + uassert.True(t, err!=nil, "Should not allow duplicate registration") + + uassert.PanicsWithMessage(t, "invalid address or username: invalid", func() { + GetBalances("invalid") + }) +} + + From ad2b36870e9b75c59da9ef2148f1a24bf85d423e Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 14 Jan 2025 03:01:22 +0100 Subject: [PATCH 19/78] fix up test --- .../r/matijamarjanovic/tokenhub/tokenhub_test.gno | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno index 5f1fcf37286..ff965de81de 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno @@ -12,12 +12,6 @@ import ( "gno.land/p/demo/urequire" ) -func setupTest() { - //std.TestSetOrigCaller(std.DerivePkgAddr("gno.land/r/matijamarjanovic/home")) - std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/home")) - std.TestSetOrigPkgAddr(std.DerivePkgAddr("gno.land/r/matijamarjanovic/home")) -} - func TestTokenRegistration(t *testing.T) { std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/home")) std.TestSetOrigPkgAddr(std.DerivePkgAddr("gno.land/r/matijamarjanovic/home")) @@ -35,11 +29,9 @@ func TestTokenRegistration(t *testing.T) { } func TestNFTRegistration(t *testing.T) { - //ako probas da namestis realm koji je testiran sa settestrealm ne vraca se path - //std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/home")) - std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/home")) + std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/tokenhub")) std.TestSetOrigPkgAddr(std.DerivePkgAddr("gno.land/r/matijamarjanovic/home")) - + nft := grc721.NewBasicNFT("Test NFT", "TNFT") err := RegisterNFT(nft.Getter(), "test_nft") urequire.NoError(t, err, "Should register NFT without error") From 2770485844e3ee6b8c0bc3ce0e04749183275eba Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 14 Jan 2025 03:01:57 +0100 Subject: [PATCH 20/78] fix fmt --- .../tokenhub/tokenhub_test.gno | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno index ff965de81de..4083d19c637 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno @@ -19,7 +19,6 @@ func TestTokenRegistration(t *testing.T) { token, _ := grc20.NewToken("Test Token", "TEST", 6) RegisterToken(token.Getter(), "test_token") - getter := GetGRC20("gno.land/r/matijamarjanovic/tokenhub.test_token") urequire.True(t, getter != nil, "Should retrieve registered token") @@ -29,9 +28,9 @@ func TestTokenRegistration(t *testing.T) { } func TestNFTRegistration(t *testing.T) { - std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/tokenhub")) + std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/tokenhub")) std.TestSetOrigPkgAddr(std.DerivePkgAddr("gno.land/r/matijamarjanovic/home")) - + nft := grc721.NewBasicNFT("Test NFT", "TNFT") err := RegisterNFT(nft.Getter(), "test_nft") urequire.NoError(t, err, "Should register NFT without error") @@ -46,11 +45,11 @@ func TestNFTRegistration(t *testing.T) { uassert.Equal(t, "TNFT", metadata.Symbol(), "NFT symbol should match") err = RegisterNFT(nft.Getter(), "test_nft") - uassert.True(t, err!=nil, "Should not allow duplicate registration") + uassert.True(t, err != nil, "Should not allow duplicate registration") } func TestGRC1155Registration(t *testing.T) { - std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/home")) + std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/home")) std.TestSetOrigPkgAddr(std.DerivePkgAddr("gno.land/r/matijamarjanovic/home")) mt := grc1155.NewBasicGRC1155Token("test-uri") @@ -63,13 +62,13 @@ func TestGRC1155Registration(t *testing.T) { uassert.Equal(t, "1", info.TokenID, "Token ID should match") err = RegisterGRC1155Token(mt.Getter(), "1") - uassert.True(t, err!=nil, "Should not allow duplicate registration") + uassert.True(t, err != nil, "Should not allow duplicate registration") } func TestBalanceRetrieval(t *testing.T) { - std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/home")) + std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/home")) std.TestSetOrigPkgAddr(std.DerivePkgAddr("gno.land/r/matijamarjanovic/home")) - + addr := std.Address("g1ej0qca5ptsw9kfr64ey8jvfy9eacga6mpj2z0y") token, ledger := grc20.NewToken("Test Token", "TEST", 6) @@ -96,24 +95,22 @@ func TestBalanceRetrieval(t *testing.T) { func TestErrorCases(t *testing.T) { std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/home")) std.TestSetOrigPkgAddr(std.DerivePkgAddr("gno.land/r/matijamarjanovic/home")) - + nft := grc721.NewBasicNFT("Test NFT", "TNFT") err := RegisterNFT(nft.Getter(), "test_nft") - uassert.True(t, err!=nil, "Should not allow duplicate registration") + uassert.True(t, err != nil, "Should not allow duplicate registration") err = RegisterNFT(nft.Getter(), "test_nft") - uassert.True(t, err!=nil, "Should not allow duplicate registration") + uassert.True(t, err != nil, "Should not allow duplicate registration") mt := grc1155.NewBasicGRC1155Token("test-uri") err = RegisterGRC1155Token(mt.Getter(), "1") - uassert.True(t, err!=nil, "Should not allow duplicate registration") + uassert.True(t, err != nil, "Should not allow duplicate registration") err = RegisterGRC1155Token(mt.Getter(), "1") - uassert.True(t, err!=nil, "Should not allow duplicate registration") + uassert.True(t, err != nil, "Should not allow duplicate registration") uassert.PanicsWithMessage(t, "invalid address or username: invalid", func() { GetBalances("invalid") }) } - - From 99a5ca76519e646b15b52c8279541e17ad5672f4 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 14 Jan 2025 14:09:29 +0100 Subject: [PATCH 21/78] add getAll tokens + get tokens by type --- .../r/matijamarjanovic/tokenhub/tokenhub.gno | 44 +++++++++++++++---- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index fb1d77fe092..fb9f2af2570 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -32,7 +32,7 @@ var ( ErrMTNotFound = errors.New("Multi-token not found") ErrMTInfoNotFound = errors.New("Multi-token info not found") - pageSize = 2 // tokens per page + pageSize = 2 test = "" ) @@ -148,6 +148,40 @@ func GetBalances(input string) string { return getNonZeroBalances(addr) } +func GetAllNFTs() string { + var sb strings.Builder + registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { + sb.WriteString(ufmt.Sprintf("NFT:%s,", key)) + return false + }) + return sb.String() +} + +func GetAllGRC20Tokens() string { + var sb strings.Builder + grc20reg.Iterate(func(key string, tokenGetter grc20.TokenGetter) bool { + sb.WriteString(ufmt.Sprintf("Token:%s,", key)) + return false + }) + return sb.String() +} + +func GetAllMultiTokens() string { + var sb strings.Builder + registeredMTs.Iterate("", "", func(key string, value interface{}) bool { + sb.WriteString(ufmt.Sprintf("MultiToken:%s,", key)) + return false + }) + return sb.String() +} + +func GetAllTokens() string { + return ufmt.Sprintf("%s%s%s", + GetAllNFTs(), + GetAllGRC20Tokens(), + GetAllMultiTokens()) +} + func Render(path string) string { var sb strings.Builder sb.WriteString(test) @@ -351,12 +385,11 @@ func Render(path string) string { func getBalances(addr std.Address) string { if addr == "" { - panic("invalid address") + panic("invalid address") } balances := avl.NewTree() var sb strings.Builder - // GRC721 registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { nftGetter := value.(grc721.NFTGetter) nft := nftGetter() @@ -365,7 +398,6 @@ func getBalances(addr std.Address) string { return false }) - // GRC20 grc20reg.Iterate(func(key string, tokenGetter grc20.TokenGetter) bool { token := tokenGetter() balance := token.BalanceOf(addr) @@ -373,7 +405,6 @@ func getBalances(addr std.Address) string { return false }) - // GRC1155 registeredMTs.Iterate("", "", func(key string, value interface{}) bool { info := value.(GRC1155TokenInfo) mt := info.Collection() @@ -416,7 +447,6 @@ func getNonZeroBalances(addr std.Address) string { balances := avl.NewTree() var sb strings.Builder - // GRC721 registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { nftGetter := value.(grc721.NFTGetter) nft := nftGetter() @@ -427,7 +457,6 @@ func getNonZeroBalances(addr std.Address) string { return false }) - // GRC20 grc20reg.Iterate(func(key string, tokenGetter grc20.TokenGetter) bool { token := tokenGetter() balance := token.BalanceOf(addr) @@ -437,7 +466,6 @@ func getNonZeroBalances(addr std.Address) string { return false }) - // GRC1155 registeredMTs.Iterate("", "", func(key string, value interface{}) bool { info := value.(GRC1155TokenInfo) mt := info.Collection() From 2ab922161d16f0be4d3fdd013a6df84d04226a44 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 14 Jan 2025 14:22:40 +0100 Subject: [PATCH 22/78] add tests and remove (fake) test file --- .../tokenhub/tokenhub_test.gno | 39 ++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno index 4083d19c637..56c79a12f30 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno @@ -28,8 +28,7 @@ func TestTokenRegistration(t *testing.T) { } func TestNFTRegistration(t *testing.T) { - std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/tokenhub")) - std.TestSetOrigPkgAddr(std.DerivePkgAddr("gno.land/r/matijamarjanovic/home")) + std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/home")) nft := grc721.NewBasicNFT("Test NFT", "TNFT") err := RegisterNFT(nft.Getter(), "test_nft") @@ -50,7 +49,6 @@ func TestNFTRegistration(t *testing.T) { func TestGRC1155Registration(t *testing.T) { std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/home")) - std.TestSetOrigPkgAddr(std.DerivePkgAddr("gno.land/r/matijamarjanovic/home")) mt := grc1155.NewBasicGRC1155Token("test-uri") err := RegisterGRC1155Token(mt.Getter(), "1") @@ -67,7 +65,6 @@ func TestGRC1155Registration(t *testing.T) { func TestBalanceRetrieval(t *testing.T) { std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/home")) - std.TestSetOrigPkgAddr(std.DerivePkgAddr("gno.land/r/matijamarjanovic/home")) addr := std.Address("g1ej0qca5ptsw9kfr64ey8jvfy9eacga6mpj2z0y") @@ -94,7 +91,6 @@ func TestBalanceRetrieval(t *testing.T) { func TestErrorCases(t *testing.T) { std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/home")) - std.TestSetOrigPkgAddr(std.DerivePkgAddr("gno.land/r/matijamarjanovic/home")) nft := grc721.NewBasicNFT("Test NFT", "TNFT") err := RegisterNFT(nft.Getter(), "test_nft") @@ -114,3 +110,36 @@ func TestErrorCases(t *testing.T) { GetBalances("invalid") }) } + +func TestTokenListingFunctions(t *testing.T) { + std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/home")) + + grc20Token, _ := grc20.NewToken("Test Token", "TEST", 6) + RegisterToken(grc20Token.Getter(), "listing_token") + + nftToken := grc721.NewBasicNFT("Test NFT", "TNFT") + RegisterNFT(nftToken.Getter(), "listing_nft") + + multiToken := grc1155.NewBasicGRC1155Token("test-uri") + RegisterGRC1155Token(multiToken.Getter(), "listing_mt") + + nftList := GetAllNFTs() + uassert.True(t, strings.Contains(nftList, "NFT:gno.land/r/matijamarjanovic/home.listing_nft"), + "GetAllNFTs should list registered NFT") + + grc20List := GetAllGRC20Tokens() + uassert.True(t, strings.Contains(grc20List, "Token:gno.land/r/matijamarjanovic/tokenhub.listing_token"), + "GetAllGRC20Tokens should list registered token") + + grc1155List := GetAllMultiTokens() + uassert.True(t, strings.Contains(grc1155List, "MultiToken:gno.land/r/matijamarjanovic/home.listing_mt"), + "GetAllMultiTokens should list registered multi-token") + + completeList := GetAllTokens() + uassert.True(t, strings.Contains(completeList, "NFT:gno.land/r/matijamarjanovic/home.listing_nft"), + "GetAllTokens should list NFTs") + uassert.True(t, strings.Contains(completeList, "Token:gno.land/r/matijamarjanovic/tokenhub.listing_token"), + "GetAllTokens should list GRC20 tokens") + uassert.True(t, strings.Contains(completeList, "MultiToken:gno.land/r/matijamarjanovic/home.listing_mt"), + "GetAllTokens should list multi-tokens") +} From f084cebab03347ecaf61fc573e9a7aefcba22767 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 14 Jan 2025 14:23:57 +0100 Subject: [PATCH 23/78] remove test realm --- .../gno.land/r/matijamarjanovic/test/gno.mod | 1 - .../gno.land/r/matijamarjanovic/test/test.gno | 111 ------------------ 2 files changed, 112 deletions(-) delete mode 100644 examples/gno.land/r/matijamarjanovic/test/gno.mod delete mode 100644 examples/gno.land/r/matijamarjanovic/test/test.gno diff --git a/examples/gno.land/r/matijamarjanovic/test/gno.mod b/examples/gno.land/r/matijamarjanovic/test/gno.mod deleted file mode 100644 index aa2814cdb1e..00000000000 --- a/examples/gno.land/r/matijamarjanovic/test/gno.mod +++ /dev/null @@ -1 +0,0 @@ -module gno.land/r/matijamarjanovic/test diff --git a/examples/gno.land/r/matijamarjanovic/test/test.gno b/examples/gno.land/r/matijamarjanovic/test/test.gno deleted file mode 100644 index 1084dd48d57..00000000000 --- a/examples/gno.land/r/matijamarjanovic/test/test.gno +++ /dev/null @@ -1,111 +0,0 @@ -package test - -import ( - "std" - - "gno.land/p/demo/grc/grc20" - "gno.land/p/demo/grc/grc721" - "gno.land/p/demo/grc/grc1155" - "gno.land/p/demo/ufmt" - "gno.land/r/matijamarjanovic/tokenhub" -) - -var ( - nftA = grc721.NewBasicNFT("Collection A", "NFTA") - nftB = grc721.NewBasicNFT("Collection B", "NFTB") - nftC = grc721.NewBasicNFT("Collection C", "NFTC") - - tokenA, tokenAAdmin = grc20.NewToken("Token A", "TOKA", 6) - tokenB, tokenBAdmin = grc20.NewToken("Token B", "TOKB", 6) - tokenC, tokenCAdmin = grc20.NewToken("Token C", "TOKC", 6) - - mtGameItems = grc1155.NewBasicGRC1155Token("https://game.example.com/items/{id}.json") - mtArtworks = grc1155.NewBasicGRC1155Token("https://art.example.com/pieces/{id}.json") - mtCollectibles = grc1155.NewBasicGRC1155Token("https://collect.example.com/cards/{id}.json") -) - -func init() { - testAddr := std.Address("g1ej0qca5ptsw9kfr64ey8jvfy9eacga6mpj2z0y") - - tokenhub.RegisterNFT(func() grc721.IGRC721 { return nftA }, "nft-a") - tokenhub.RegisterNFT(func() grc721.IGRC721 { return nftB }, "nft-b") - tokenhub.RegisterNFT(func() grc721.IGRC721 { return nftC }, "nft-c") - - tokenhub.RegisterToken(tokenA.Getter(), "token-a") - tokenhub.RegisterToken(tokenB.Getter(), "token-b") - tokenhub.RegisterToken(tokenC.Getter(), "token-c") - - tokenhub.RegisterGRC1155Token(mtGameItems.Getter(), "sword") - tokenhub.RegisterGRC1155Token(mtGameItems.Getter(), "potion") - tokenhub.RegisterGRC1155Token(mtArtworks.Getter(), "artwork_1") - tokenhub.RegisterGRC1155Token(mtCollectibles.Getter(), "rare_card") - - MintTestNFTs(testAddr) - MintTestTokens(testAddr) - MintTestMultiTokens(testAddr) - -} - -func MintTestNFTs(to std.Address) error { - if !to.IsValid() { - return grc721.ErrInvalidAddress - } - - for i := 1; i <= 2; i++ { - tokenIDA := grc721.TokenID(ufmt.Sprintf("NFTA_%d", i)) - if err := nftA.Mint(to, tokenIDA); err != nil { - return err - } - - tokenIDB := grc721.TokenID(ufmt.Sprintf("NFTB_%d", i)) - if err := nftB.Mint(to, tokenIDB); err != nil { - return err - } - - tokenIDC := grc721.TokenID(ufmt.Sprintf("NFTC_%d", i)) - if err := nftC.Mint(to, tokenIDC); err != nil { - return err - } - } - - return nil -} - -func MintTestTokens(to std.Address) error { - if !to.IsValid() { - return grc20.ErrInvalidAddress - } - - if err := tokenAAdmin.Mint(to, 1000_000000); err != nil { - return err - } - if err := tokenBAdmin.Mint(to, 10000_000000); err != nil { - return err - } - if err := tokenCAdmin.Mint(to, 100_000000); err != nil { - return err - } - - return nil -} - -func MintTestMultiTokens(to std.Address) error { - if !to.IsValid() { - return grc1155.ErrInvalidAddress - } - - if err := mtGameItems.SafeMint(to, "sword", 10); err != nil { - return err - } - if err := mtGameItems.SafeMint(to, "potion", 50); err != nil { - return err - } - if err := mtArtworks.SafeMint(to, "artwork_1", 1); err != nil { - return err - } - if err := mtCollectibles.SafeMint(to, "rare_card", 1); err != nil { - return err - } - - return nil -} From c484774bbd16b011019283372aec39babc897bf0 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 14 Jan 2025 14:24:53 +0100 Subject: [PATCH 24/78] fix fmt --- .../r/matijamarjanovic/tokenhub/tokenhub.gno | 4 ++-- .../r/matijamarjanovic/tokenhub/tokenhub_test.gno | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index fb9f2af2570..dec1f89edd5 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -32,7 +32,7 @@ var ( ErrMTNotFound = errors.New("Multi-token not found") ErrMTInfoNotFound = errors.New("Multi-token info not found") - pageSize = 2 + pageSize = 2 test = "" ) @@ -385,7 +385,7 @@ func Render(path string) string { func getBalances(addr std.Address) string { if addr == "" { - panic("invalid address") + panic("invalid address") } balances := avl.NewTree() var sb strings.Builder diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno index 56c79a12f30..9c81b6ca768 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno @@ -124,22 +124,22 @@ func TestTokenListingFunctions(t *testing.T) { RegisterGRC1155Token(multiToken.Getter(), "listing_mt") nftList := GetAllNFTs() - uassert.True(t, strings.Contains(nftList, "NFT:gno.land/r/matijamarjanovic/home.listing_nft"), + uassert.True(t, strings.Contains(nftList, "NFT:gno.land/r/matijamarjanovic/home.listing_nft"), "GetAllNFTs should list registered NFT") grc20List := GetAllGRC20Tokens() - uassert.True(t, strings.Contains(grc20List, "Token:gno.land/r/matijamarjanovic/tokenhub.listing_token"), + uassert.True(t, strings.Contains(grc20List, "Token:gno.land/r/matijamarjanovic/tokenhub.listing_token"), "GetAllGRC20Tokens should list registered token") grc1155List := GetAllMultiTokens() - uassert.True(t, strings.Contains(grc1155List, "MultiToken:gno.land/r/matijamarjanovic/home.listing_mt"), + uassert.True(t, strings.Contains(grc1155List, "MultiToken:gno.land/r/matijamarjanovic/home.listing_mt"), "GetAllMultiTokens should list registered multi-token") completeList := GetAllTokens() - uassert.True(t, strings.Contains(completeList, "NFT:gno.land/r/matijamarjanovic/home.listing_nft"), + uassert.True(t, strings.Contains(completeList, "NFT:gno.land/r/matijamarjanovic/home.listing_nft"), "GetAllTokens should list NFTs") - uassert.True(t, strings.Contains(completeList, "Token:gno.land/r/matijamarjanovic/tokenhub.listing_token"), + uassert.True(t, strings.Contains(completeList, "Token:gno.land/r/matijamarjanovic/tokenhub.listing_token"), "GetAllTokens should list GRC20 tokens") - uassert.True(t, strings.Contains(completeList, "MultiToken:gno.land/r/matijamarjanovic/home.listing_mt"), + uassert.True(t, strings.Contains(completeList, "MultiToken:gno.land/r/matijamarjanovic/home.listing_mt"), "GetAllTokens should list multi-tokens") } From 48c6cd1f79fd5b4f840876b99aec32294edc923d Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 14 Jan 2025 14:28:01 +0100 Subject: [PATCH 25/78] more fix fmt --- examples/gno.land/r/demo/grc20reg/grc20reg.gno | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/gno.land/r/demo/grc20reg/grc20reg.gno b/examples/gno.land/r/demo/grc20reg/grc20reg.gno index a01610a4b41..c9715c1b7b5 100644 --- a/examples/gno.land/r/demo/grc20reg/grc20reg.gno +++ b/examples/gno.land/r/demo/grc20reg/grc20reg.gno @@ -84,19 +84,19 @@ func Iterate(callback func(key string, token grc20.TokenGetter) bool) { func IterateN(startKey string, n int, callback func(key string, token grc20.TokenGetter) bool) string { var lastKey string count := 0 - + registry.Iterate(startKey, "", func(key string, tokenI interface{}) bool { if count >= n { - return true + return true } - + tokenGetter := tokenI.(grc20.TokenGetter) lastKey = key count++ - + return callback(key, tokenGetter) }) - + return lastKey } From 87aac01e6b924512e08848fc16ebe50251fac5dc Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Fri, 17 Jan 2025 14:22:55 +0100 Subject: [PATCH 26/78] update so nft doesn't allow optional slug, but has to include tokenid --- .../gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index dec1f89edd5..223c08666e1 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -44,7 +44,8 @@ func RegisterToken(tokenGetter grc20.TokenGetter, slugs ...string) { grc20reg.Register(tokenGetter, slug) } -func RegisterNFT(nftGetter grc721.NFTGetter, slugs ...string) error { +// TokenId is mandatory for NFTs +func RegisterNFT(nftGetter grc721.NFTGetter, tokenId string) error { nft := nftGetter() _, ok := nft.(grc721.IGRC721CollectionMetadata) if !ok { @@ -52,11 +53,7 @@ func RegisterNFT(nftGetter grc721.NFTGetter, slugs ...string) error { } rlmPath := std.PrevRealm().PkgPath() - slug := "" - if len(slugs) > 0 { - slug = slugs[0] - } - key := fqname.Construct(rlmPath, slug) + key := fqname.Construct(rlmPath, tokenId) if registeredNFTs.Has(key) { return ErrNFTAlreadyRegistered From 8ab9a2651a1bd74a325fcad38bad5ffb14566787 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Fri, 17 Jan 2025 20:01:13 +0100 Subject: [PATCH 27/78] fix nft registration, now each minted can be registered --- .../r/matijamarjanovic/tokenhub/tokenhub.gno | 20 +++++++++---- .../tokenhub/tokenhub_test.gno | 28 +++++++++---------- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index 223c08666e1..6164b3466a3 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -23,7 +23,7 @@ type GRC1155TokenInfo struct { } var ( - registeredNFTs = avl.NewTree() // rlmPath[.slug] -> NFTGetter + registeredNFTs = avl.NewTree() // rlmPath[.slug] -> NFTInfo registeredMTs = avl.NewTree() // rlmPath[.slug] -> GRC1155TokenInfo ErrNFTAlreadyRegistered = errors.New("NFT already registered") @@ -31,6 +31,8 @@ var ( ErrMTAlreadyRegistered = errors.New("Multi-token already registered") ErrMTNotFound = errors.New("Multi-token not found") ErrMTInfoNotFound = errors.New("Multi-token info not found") + ErrNFTtokIDNotExists = errors.New("NFT token ID does not exists") + ErrNFTNotMetadata = errors.New("NFT must implement IGRC721CollectionMetadata") pageSize = 2 test = "" @@ -44,16 +46,23 @@ func RegisterToken(tokenGetter grc20.TokenGetter, slugs ...string) { grc20reg.Register(tokenGetter, slug) } -// TokenId is mandatory for NFTs -func RegisterNFT(nftGetter grc721.NFTGetter, tokenId string) error { +// registering a collection of NFTs +func RegisterNFT(nftGetter grc721.NFTGetter, collection string, tokenId string) error { + nft := nftGetter() _, ok := nft.(grc721.IGRC721CollectionMetadata) if !ok { - return errors.New("NFT must implement IGRC721CollectionMetadata") + return ErrNFTNotMetadata + } + + nftOwner, err := nft.OwnerOf(grc721.TokenID(tokenId)) + if string(nftOwner) == "" || err != nil { + return ErrNFTtokIDNotExists } rlmPath := std.PrevRealm().PkgPath() - key := fqname.Construct(rlmPath, tokenId) + k := ufmt.Sprintf("%s.%s", collection, tokenId) + key := fqname.Construct(rlmPath, k) if registeredNFTs.Has(key) { return ErrNFTAlreadyRegistered @@ -419,7 +428,6 @@ func getBalances(addr std.Address) string { balance := value.(uint64) symbol := strings.TrimPrefix(key, "nft:") sb.WriteString(ufmt.Sprintf("NFT:%s:%d,", symbol, balance)) - sb.WriteString(ufmt.Sprintf("Token:%s:%d,", symbol, balance)) case strings.HasPrefix(key, "token:"): balance := value.(uint64) symbol := strings.TrimPrefix(key, "token:") diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno index 9c81b6ca768..df65f27751b 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno @@ -30,11 +30,14 @@ func TestTokenRegistration(t *testing.T) { func TestNFTRegistration(t *testing.T) { std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/home")) + addr := std.Address("g1ej0qca5ptsw9kfr64ey8jvfy9eacga6mpj2z0y") + nft := grc721.NewBasicNFT("Test NFT", "TNFT") - err := RegisterNFT(nft.Getter(), "test_nft") + nft.Mint(addr, grc721.TokenID("1")) + err := RegisterNFT(nft.Getter(), "test_nft", "1") urequire.NoError(t, err, "Should register NFT without error") - getter := GetGRC721("gno.land/r/matijamarjanovic/home.test_nft") + getter := GetGRC721("gno.land/r/matijamarjanovic/home.test_nft.1") urequire.True(t, getter != nil, "Should retrieve registered NFT") retrievedNFT := getter() @@ -43,7 +46,7 @@ func TestNFTRegistration(t *testing.T) { uassert.Equal(t, "Test NFT", metadata.Name(), "NFT name should match") uassert.Equal(t, "TNFT", metadata.Symbol(), "NFT symbol should match") - err = RegisterNFT(nft.Getter(), "test_nft") + err = RegisterNFT(nft.Getter(), "test_nft", "1") uassert.True(t, err != nil, "Should not allow duplicate registration") } @@ -72,17 +75,13 @@ func TestBalanceRetrieval(t *testing.T) { RegisterToken(token.Getter(), "test_tokenn") ledger.Mint(addr, 1000) - nft := grc721.NewBasicNFT("Test NFT", "TNFT") - RegisterNFT(nft.Getter(), "test_nftt") - nft.Mint(addr, grc721.TokenID("str")) - mt := grc1155.NewBasicGRC1155Token("test-uri") RegisterGRC1155Token(mt.Getter(), "11") mt.SafeMint(addr, grc1155.TokenID("11"), 5) balances := GetBalancesAll(string(addr)) uassert.True(t, strings.Contains(balances, "Token:gno.land/r/matijamarjanovic/tokenhub.test_tokenn:1000"), "Should show correct GRC20 balance") - uassert.True(t, strings.Contains(balances, "NFT:gno.land/r/matijamarjanovic/home.test_nftt:1"), "Should show correct NFT balance") + uassert.True(t, strings.Contains(balances, "NFT:gno.land/r/matijamarjanovic/home.test_nft.1:1"), "Should show correct NFT balance") //already minted in test register uassert.True(t, strings.Contains(balances, "MultiToken:gno.land/r/matijamarjanovic/home.11:5"), "Should show multi-token balance") nonZeroBalances := GetBalances(string(addr)) @@ -93,10 +92,10 @@ func TestErrorCases(t *testing.T) { std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/home")) nft := grc721.NewBasicNFT("Test NFT", "TNFT") - err := RegisterNFT(nft.Getter(), "test_nft") + err := RegisterNFT(nft.Getter(), "test_nft", "1") uassert.True(t, err != nil, "Should not allow duplicate registration") - err = RegisterNFT(nft.Getter(), "test_nft") + err = RegisterNFT(nft.Getter(), "test_nft", "1") uassert.True(t, err != nil, "Should not allow duplicate registration") mt := grc1155.NewBasicGRC1155Token("test-uri") @@ -117,14 +116,15 @@ func TestTokenListingFunctions(t *testing.T) { grc20Token, _ := grc20.NewToken("Test Token", "TEST", 6) RegisterToken(grc20Token.Getter(), "listing_token") - nftToken := grc721.NewBasicNFT("Test NFT", "TNFT") - RegisterNFT(nftToken.Getter(), "listing_nft") + nftToken := grc721.NewBasicNFT("Listing NFT", "LNFT") + nftToken.Mint(std.Address("g1ej0qca5ptsw9kfr64ey8jvfy9eacga6mpj2z0y"), grc721.TokenID("1")) + RegisterNFT(nftToken.Getter(), "listing_nft", "1") multiToken := grc1155.NewBasicGRC1155Token("test-uri") RegisterGRC1155Token(multiToken.Getter(), "listing_mt") nftList := GetAllNFTs() - uassert.True(t, strings.Contains(nftList, "NFT:gno.land/r/matijamarjanovic/home.listing_nft"), + uassert.True(t, strings.Contains(nftList, "NFT:gno.land/r/matijamarjanovic/home.listing_nft.1"), "GetAllNFTs should list registered NFT") grc20List := GetAllGRC20Tokens() @@ -136,7 +136,7 @@ func TestTokenListingFunctions(t *testing.T) { "GetAllMultiTokens should list registered multi-token") completeList := GetAllTokens() - uassert.True(t, strings.Contains(completeList, "NFT:gno.land/r/matijamarjanovic/home.listing_nft"), + uassert.True(t, strings.Contains(completeList, "NFT:gno.land/r/matijamarjanovic/home.listing_nft.1"), "GetAllTokens should list NFTs") uassert.True(t, strings.Contains(completeList, "Token:gno.land/r/matijamarjanovic/tokenhub.listing_token"), "GetAllTokens should list GRC20 tokens") From b107e7d79e4cdbba958cdced2bace05072f2a118 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Fri, 17 Jan 2025 20:16:57 +0100 Subject: [PATCH 28/78] fix: show only nfts and grc1155 owner has, because there could be who knows how many of those he does not --- .../r/matijamarjanovic/tokenhub/tokenhub.gno | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index 6164b3466a3..47e885ad260 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -399,15 +399,20 @@ func getBalances(addr std.Address) string { registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { nftGetter := value.(grc721.NFTGetter) nft := nftGetter() - balance, _ := nft.BalanceOf(addr) - balances.Set("nft:"+key, balance) + key_parts := strings.Split(key, ".") + if addr == nft.OwnerOf(grc721.TokenID(key_parts[len(key_parts)-1])) { // show only the nfts owner owns + balances.Set("nft:"+key) + } return false }) grc20reg.Iterate(func(key string, tokenGetter grc20.TokenGetter) bool { token := tokenGetter() balance := token.BalanceOf(addr) - balances.Set("token:"+key, balance) + balance := token.BalanceOf(addr) + if balance > 0 { + balances.Set("token:"+key, balance) + } return false }) @@ -455,9 +460,9 @@ func getNonZeroBalances(addr std.Address) string { registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { nftGetter := value.(grc721.NFTGetter) nft := nftGetter() - balance, _ := nft.BalanceOf(addr) - if balance > 0 { - balances.Set("nft:"+key, balance) + key_parts := strings.Split(key, ".") + if addr == nft.OwnerOf(grc721.TokenID(key_parts[len(key_parts)-1])) { // show only the nfts owner owns + balances.Set("nft:"+key) } return false }) From 936f1817676aecf0a56c59ef09cd80c79b00087d Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Fri, 17 Jan 2025 20:27:00 +0100 Subject: [PATCH 29/78] write only key for nfts, no need for balance --- .../r/matijamarjanovic/tokenhub/tokenhub.gno | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index 47e885ad260..374b5e4a5df 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -400,8 +400,9 @@ func getBalances(addr std.Address) string { nftGetter := value.(grc721.NFTGetter) nft := nftGetter() key_parts := strings.Split(key, ".") - if addr == nft.OwnerOf(grc721.TokenID(key_parts[len(key_parts)-1])) { // show only the nfts owner owns - balances.Set("nft:"+key) + owner, err := nft.OwnerOf(grc721.TokenID(key_parts[len(key_parts)-1])) + if err == nil && addr == owner { // show only the nfts owner owns + balances.Set("nft:"+key, 1) } return false }) @@ -409,7 +410,6 @@ func getBalances(addr std.Address) string { grc20reg.Iterate(func(key string, tokenGetter grc20.TokenGetter) bool { token := tokenGetter() balance := token.BalanceOf(addr) - balance := token.BalanceOf(addr) if balance > 0 { balances.Set("token:"+key, balance) } @@ -430,9 +430,8 @@ func getBalances(addr std.Address) string { balances.Iterate("", "", func(key string, value interface{}) bool { switch { case strings.HasPrefix(key, "nft:"): - balance := value.(uint64) symbol := strings.TrimPrefix(key, "nft:") - sb.WriteString(ufmt.Sprintf("NFT:%s:%d,", symbol, balance)) + sb.WriteString(ufmt.Sprintf("NFT:%s,", symbol)) case strings.HasPrefix(key, "token:"): balance := value.(uint64) symbol := strings.TrimPrefix(key, "token:") @@ -461,8 +460,9 @@ func getNonZeroBalances(addr std.Address) string { nftGetter := value.(grc721.NFTGetter) nft := nftGetter() key_parts := strings.Split(key, ".") - if addr == nft.OwnerOf(grc721.TokenID(key_parts[len(key_parts)-1])) { // show only the nfts owner owns - balances.Set("nft:"+key) + owner, err := nft.OwnerOf(grc721.TokenID(key_parts[len(key_parts)-1])) + if err == nil && addr == owner { // show only the nfts owner owns + balances.Set("nft:"+key, 1) } return false }) @@ -490,9 +490,8 @@ func getNonZeroBalances(addr std.Address) string { balances.Iterate("", "", func(key string, value interface{}) bool { switch { case strings.HasPrefix(key, "nft:"): - balance := value.(uint64) symbol := strings.TrimPrefix(key, "nft:") - sb.WriteString(ufmt.Sprintf("NFT:%s:%d,", symbol, balance)) + sb.WriteString(ufmt.Sprintf("NFT:%s,", symbol)) case strings.HasPrefix(key, "token:"): balance := value.(uint64) symbol := strings.TrimPrefix(key, "token:") From dabf342676ce9335087ca2759b06f49c60d627b5 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Sat, 18 Jan 2025 00:39:47 +0100 Subject: [PATCH 30/78] - add get all tokens without nfts - remove public get all tokens for a specific address --- .../r/matijamarjanovic/tokenhub/tokenhub.gno | 39 ++++++++----------- .../tokenhub/tokenhub_test.gno | 20 +++------- 2 files changed, 23 insertions(+), 36 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index 374b5e4a5df..41fd7a11fb7 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -91,11 +91,11 @@ func RegisterGRC1155Token(mtGetter grc1155.MultiTokenGetter, tokenID string) err } func GetMyBalancesAll() string { - return getBalances(std.GetOrigCaller()) + return getBalances(string(std.PrevRealm().Addr())) } func GetMyBalances() string { - return getNonZeroBalances(std.GetOrigCaller()) + return getNonZeroBalances(string(std.PrevRealm().Addr())) } func GetGRC20(key string) grc20.TokenGetter { @@ -138,22 +138,6 @@ func MustGetGRC1155(key string) GRC1155TokenInfo { return info } -func GetBalancesAll(input string) string { - addr := getAddressForUsername(input) - if addr == "" { - panic("invalid address or username: " + input) - } - return getBalances(addr) -} - -func GetBalances(input string) string { - addr := getAddressForUsername(input) - if addr == "" { - panic("invalid address or username: " + input) - } - return getNonZeroBalances(addr) -} - func GetAllNFTs() string { var sb strings.Builder registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { @@ -188,6 +172,12 @@ func GetAllTokens() string { GetAllMultiTokens()) } +func GetAllTokensNoNFTs() string { + return ufmt.Sprintf("%s%s", + GetAllGRC20Tokens(), + GetAllMultiTokens()) +} + func Render(path string) string { var sb strings.Builder sb.WriteString(test) @@ -389,10 +379,13 @@ func Render(path string) string { return sb.String() } -func getBalances(addr std.Address) string { +func getBalances(input string) string { + + addr := getAddressForUsername(input) if addr == "" { - panic("invalid address") + panic("invalid address or username: " + input) } + balances := avl.NewTree() var sb strings.Builder @@ -449,10 +442,12 @@ func getBalances(addr std.Address) string { return sb.String() } -func getNonZeroBalances(addr std.Address) string { +func getNonZeroBalances(input string) string { + addr := getAddressForUsername(input) if addr == "" { - panic("invalid address") + panic("invalid address or username: " + input) } + balances := avl.NewTree() var sb strings.Builder diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno index df65f27751b..7a5340cfbcf 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno @@ -30,10 +30,8 @@ func TestTokenRegistration(t *testing.T) { func TestNFTRegistration(t *testing.T) { std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/home")) - addr := std.Address("g1ej0qca5ptsw9kfr64ey8jvfy9eacga6mpj2z0y") - nft := grc721.NewBasicNFT("Test NFT", "TNFT") - nft.Mint(addr, grc721.TokenID("1")) + nft.Mint(std.CurrentRealm().Addr(), grc721.TokenID("1")) err := RegisterNFT(nft.Getter(), "test_nft", "1") urequire.NoError(t, err, "Should register NFT without error") @@ -69,22 +67,20 @@ func TestGRC1155Registration(t *testing.T) { func TestBalanceRetrieval(t *testing.T) { std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/home")) - addr := std.Address("g1ej0qca5ptsw9kfr64ey8jvfy9eacga6mpj2z0y") - token, ledger := grc20.NewToken("Test Token", "TEST", 6) RegisterToken(token.Getter(), "test_tokenn") - ledger.Mint(addr, 1000) + ledger.Mint(std.CurrentRealm().Addr(), 1000) mt := grc1155.NewBasicGRC1155Token("test-uri") RegisterGRC1155Token(mt.Getter(), "11") - mt.SafeMint(addr, grc1155.TokenID("11"), 5) + mt.SafeMint(std.CurrentRealm().Addr(), grc1155.TokenID("11"), 5) - balances := GetBalancesAll(string(addr)) + balances := GetMyBalancesAll() uassert.True(t, strings.Contains(balances, "Token:gno.land/r/matijamarjanovic/tokenhub.test_tokenn:1000"), "Should show correct GRC20 balance") - uassert.True(t, strings.Contains(balances, "NFT:gno.land/r/matijamarjanovic/home.test_nft.1:1"), "Should show correct NFT balance") //already minted in test register + uassert.True(t, strings.Contains(balances, "NFT:gno.land/r/matijamarjanovic/home.test_nft.1"), "Should show correct NFT balance") //already minted in test register uassert.True(t, strings.Contains(balances, "MultiToken:gno.land/r/matijamarjanovic/home.11:5"), "Should show multi-token balance") - nonZeroBalances := GetBalances(string(addr)) + nonZeroBalances := GetMyBalances() uassert.True(t, strings.Contains(nonZeroBalances, "Token:gno.land/r/matijamarjanovic/tokenhub.test_tokenn:1000"), "Should show non-zero GRC20 balance") } @@ -104,10 +100,6 @@ func TestErrorCases(t *testing.T) { err = RegisterGRC1155Token(mt.Getter(), "1") uassert.True(t, err != nil, "Should not allow duplicate registration") - - uassert.PanicsWithMessage(t, "invalid address or username: invalid", func() { - GetBalances("invalid") - }) } func TestTokenListingFunctions(t *testing.T) { From c333c42236a5d479031053a90dc4c04b0542940a Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Sat, 18 Jan 2025 01:02:07 +0100 Subject: [PATCH 31/78] add different funcs for prevrealm and orig caller --- .../gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index 41fd7a11fb7..ec032c2dd3e 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -90,6 +90,7 @@ func RegisterGRC1155Token(mtGetter grc1155.MultiTokenGetter, tokenID string) err return nil } +// for realm use func GetMyBalancesAll() string { return getBalances(string(std.PrevRealm().Addr())) } @@ -98,6 +99,15 @@ func GetMyBalances() string { return getNonZeroBalances(string(std.PrevRealm().Addr())) } +// for user use +func GetMyBalancesAllOrigCaller() string { + return getBalances(string(std.GetOrigCaller())) +} + +func GetMyBalancesOrigCaller() string { + return getNonZeroBalances(string(std.GetOrigCaller())) +} + func GetGRC20(key string) grc20.TokenGetter { return grc20reg.Get(key) } From 038cb5008def32e797cf59cbd5342c211e0d66a4 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Mon, 20 Jan 2025 17:04:50 +0100 Subject: [PATCH 32/78] - add seing anyones balances - minor fix (show non zero balances) --- .../r/matijamarjanovic/tokenhub/tokenhub.gno | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index ec032c2dd3e..5ba3eaf68ca 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -108,6 +108,14 @@ func GetMyBalancesOrigCaller() string { return getNonZeroBalances(string(std.GetOrigCaller())) } +func GetUserBalancesAll(userNameOrAddress string) string { + return getBalances(userNameOrAddress) +} + +func GetUserBalances(userNameOrAddress string) string { + return getNonZeroBalances(userNameOrAddress) +} + func GetGRC20(key string) grc20.TokenGetter { return grc20reg.Get(key) } @@ -413,9 +421,7 @@ func getBalances(input string) string { grc20reg.Iterate(func(key string, tokenGetter grc20.TokenGetter) bool { token := tokenGetter() balance := token.BalanceOf(addr) - if balance > 0 { - balances.Set("token:"+key, balance) - } + balances.Set("token:"+key, balance) return false }) From 8c097251df0ffc0d1dc7a2d9a6279796dcb72d1f Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 21 Jan 2025 11:22:28 +0100 Subject: [PATCH 33/78] fix page size, remove test --- examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index 5ba3eaf68ca..df4b9578bb1 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -33,11 +33,10 @@ var ( ErrMTInfoNotFound = errors.New("Multi-token info not found") ErrNFTtokIDNotExists = errors.New("NFT token ID does not exists") ErrNFTNotMetadata = errors.New("NFT must implement IGRC721CollectionMetadata") - - pageSize = 2 - test = "" ) +const pageSize = 2 + func RegisterToken(tokenGetter grc20.TokenGetter, slugs ...string) { slug := "" if len(slugs) > 0 { @@ -198,7 +197,6 @@ func GetAllTokensNoNFTs() string { func Render(path string) string { var sb strings.Builder - sb.WriteString(test) switch { case path == "": From 88b0a7ee5436de12426a91d52f22b215e0b7f223 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 21 Jan 2025 11:35:16 +0100 Subject: [PATCH 34/78] make errors start with lowercase --- examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index df4b9578bb1..89864d83a18 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -28,9 +28,9 @@ var ( ErrNFTAlreadyRegistered = errors.New("NFT already registered") ErrNFTNotFound = errors.New("NFT not found") - ErrMTAlreadyRegistered = errors.New("Multi-token already registered") - ErrMTNotFound = errors.New("Multi-token not found") - ErrMTInfoNotFound = errors.New("Multi-token info not found") + ErrMTAlreadyRegistered = errors.New("multi-token already registered") + ErrMTNotFound = errors.New("multi-token not found") + ErrMTInfoNotFound = errors.New("multi-token info not found") ErrNFTtokIDNotExists = errors.New("NFT token ID does not exists") ErrNFTNotMetadata = errors.New("NFT must implement IGRC721CollectionMetadata") ) From 1c4b72a4336cd9afb2f95497e4e56126b1e6e0a9 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 21 Jan 2025 11:41:42 +0100 Subject: [PATCH 35/78] remove not needed new lines --- examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index 89864d83a18..28138c85ead 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -47,7 +47,6 @@ func RegisterToken(tokenGetter grc20.TokenGetter, slugs ...string) { // registering a collection of NFTs func RegisterNFT(nftGetter grc721.NFTGetter, collection string, tokenId string) error { - nft := nftGetter() _, ok := nft.(grc721.IGRC721CollectionMetadata) if !ok { @@ -396,7 +395,6 @@ func Render(path string) string { } func getBalances(input string) string { - addr := getAddressForUsername(input) if addr == "" { panic("invalid address or username: " + input) From 005e64b635dfcabdfb227aff84d41787a603a30d Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 21 Jan 2025 11:57:56 +0100 Subject: [PATCH 36/78] - change slugs to slug - use addr.isvalid --- .../r/matijamarjanovic/tokenhub/tokenhub.gno | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index 28138c85ead..754110ed410 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -37,11 +37,7 @@ var ( const pageSize = 2 -func RegisterToken(tokenGetter grc20.TokenGetter, slugs ...string) { - slug := "" - if len(slugs) > 0 { - slug = slugs[0] - } +func RegisterToken(tokenGetter grc20.TokenGetter, slug string) { grc20reg.Register(tokenGetter, slug) } @@ -54,7 +50,11 @@ func RegisterNFT(nftGetter grc721.NFTGetter, collection string, tokenId string) } nftOwner, err := nft.OwnerOf(grc721.TokenID(tokenId)) - if string(nftOwner) == "" || err != nil { + + if err != nil { + return err + } + if !nftOwner.IsValid() { return ErrNFTtokIDNotExists } From 7ac568f7cbc18d36ce1fc7b726762d6dc14df913 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 21 Jan 2025 12:29:08 +0100 Subject: [PATCH 37/78] - remove getorigcaller use function - remove comments --- .../gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index 754110ed410..6623acc9cc3 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -88,7 +88,6 @@ func RegisterGRC1155Token(mtGetter grc1155.MultiTokenGetter, tokenID string) err return nil } -// for realm use func GetMyBalancesAll() string { return getBalances(string(std.PrevRealm().Addr())) } @@ -97,15 +96,6 @@ func GetMyBalances() string { return getNonZeroBalances(string(std.PrevRealm().Addr())) } -// for user use -func GetMyBalancesAllOrigCaller() string { - return getBalances(string(std.GetOrigCaller())) -} - -func GetMyBalancesOrigCaller() string { - return getNonZeroBalances(string(std.GetOrigCaller())) -} - func GetUserBalancesAll(userNameOrAddress string) string { return getBalances(userNameOrAddress) } From 35fa7e292b214995d8215cace86d236a5de8a087 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 21 Jan 2025 12:49:18 +0100 Subject: [PATCH 38/78] remove get my balacnces - does the same as getuserbalances for the caller address rename getusrebalancesall to withoutzeros --- .../r/matijamarjanovic/tokenhub/tokenhub.gno | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index 6623acc9cc3..6025114f56f 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -88,19 +88,11 @@ func RegisterGRC1155Token(mtGetter grc1155.MultiTokenGetter, tokenID string) err return nil } -func GetMyBalancesAll() string { - return getBalances(string(std.PrevRealm().Addr())) -} - -func GetMyBalances() string { - return getNonZeroBalances(string(std.PrevRealm().Addr())) -} - -func GetUserBalancesAll(userNameOrAddress string) string { +func GetUserBalances(userNameOrAddress string) string { return getBalances(userNameOrAddress) } -func GetUserBalances(userNameOrAddress string) string { +func GetUserBalancesWithoutZeros(userNameOrAddress string) string { return getNonZeroBalances(userNameOrAddress) } From 3b94b2ee35c24bc570155781cab8e65ba7feac71 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 21 Jan 2025 12:51:41 +0100 Subject: [PATCH 39/78] fix naming --- examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index 6025114f56f..64fa052af06 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -88,11 +88,11 @@ func RegisterGRC1155Token(mtGetter grc1155.MultiTokenGetter, tokenID string) err return nil } -func GetUserBalances(userNameOrAddress string) string { +func GetUserBalancesWithZeros(userNameOrAddress string) string { return getBalances(userNameOrAddress) } -func GetUserBalancesWithoutZeros(userNameOrAddress string) string { +func GetUserBalances(userNameOrAddress string) string { return getNonZeroBalances(userNameOrAddress) } From 0a63572698f787a524948b1a7e869e9c4f721665 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 21 Jan 2025 15:18:42 +0100 Subject: [PATCH 40/78] return the tokens themselves instead of getters --- .../r/matijamarjanovic/tokenhub/tokenhub.gno | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index 64fa052af06..ad217c640a4 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -96,23 +96,26 @@ func GetUserBalances(userNameOrAddress string) string { return getNonZeroBalances(userNameOrAddress) } -func GetGRC20(key string) grc20.TokenGetter { - return grc20reg.Get(key) +func GetGRC20(key string) *grc20.Token { + getter := grc20reg.Get(key) + return getter() } -func MustGetGRC20(key string) grc20.TokenGetter { - return grc20reg.MustGet(key) +func MustGetGRC20(key string) *grc20.Token { + getter := grc20reg.MustGet(key) + return getter() } -func GetGRC721(key string) grc721.NFTGetter { +func GetGRC721(key string) grc721.IGRC721 { nftGetter, ok := registeredNFTs.Get(key) if !ok { return nil } - return nftGetter.(grc721.NFTGetter) + nft := nftGetter.(grc721.IGRC721) + return nft } -func MustGetGRC721(key string) grc721.NFTGetter { +func MustGetGRC721(key string) grc721.IGRC721 { nftGetter := GetGRC721(key) if nftGetter == nil { panic("unknown NFT: " + key) From 99b44ac30e964636bf3760f5aa1b31dad6f866b7 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 21 Jan 2025 15:33:50 +0100 Subject: [PATCH 41/78] implement return tokens themselves instead of getters in grc1155 too (and a simple test) --- examples/gno.land/r/matijamarjanovic/test/gno.mod | 1 + examples/gno.land/r/matijamarjanovic/test/test.gno | 9 +++++++++ .../r/matijamarjanovic/tokenhub/tokenhub.gno | 13 +++++++------ 3 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 examples/gno.land/r/matijamarjanovic/test/gno.mod create mode 100644 examples/gno.land/r/matijamarjanovic/test/test.gno diff --git a/examples/gno.land/r/matijamarjanovic/test/gno.mod b/examples/gno.land/r/matijamarjanovic/test/gno.mod new file mode 100644 index 00000000000..a27bf924173 --- /dev/null +++ b/examples/gno.land/r/matijamarjanovic/test/gno.mod @@ -0,0 +1 @@ +module "gno.land/r/matijamarjanovic/test" diff --git a/examples/gno.land/r/matijamarjanovic/test/test.gno b/examples/gno.land/r/matijamarjanovic/test/test.gno new file mode 100644 index 00000000000..88599c8c01b --- /dev/null +++ b/examples/gno.land/r/matijamarjanovic/test/test.gno @@ -0,0 +1,9 @@ +package test +//please ignore this file it will be deleted soon in last commit + +func Test() { +} + +func Render(path string) string { + return "" +} diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index ad217c640a4..7bcbe176e01 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -123,17 +123,18 @@ func MustGetGRC721(key string) grc721.IGRC721 { return nftGetter } -func GetGRC1155(key string) (GRC1155TokenInfo, bool) { +func GetGRC1155(key string) grc1155.IGRC1155 { info, ok := registeredMTs.Get(key) if !ok { - return GRC1155TokenInfo{}, false + return nil } - return info.(GRC1155TokenInfo), true + mt := info.(GRC1155TokenInfo).Collection + return mt() } -func MustGetGRC1155(key string) GRC1155TokenInfo { - info, ok := GetGRC1155(key) - if !ok { +func MustGetGRC1155(key string) grc1155.IGRC1155 { + info := GetGRC1155(key) + if info == nil { panic("unknown multi-token: " + key) } return info From 3f8bb518008f482ffea9852e9786b3fc6f0552e7 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 21 Jan 2025 15:50:49 +0100 Subject: [PATCH 42/78] update naming to be consistent --- .../r/matijamarjanovic/tokenhub/tokenhub.gno | 33 +++++++------------ 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index 7bcbe176e01..2a7b9bf3d0f 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -72,7 +72,7 @@ func RegisterNFT(nftGetter grc721.NFTGetter, collection string, tokenId string) // for grc1155 is a little bit different, since the one collection can have multiple tokens, this is why don't use slugs but tokenIDs, // and the collection is saved as a part of the :value in the node -func RegisterGRC1155Token(mtGetter grc1155.MultiTokenGetter, tokenID string) error { +func RegisterMultiToken(mtGetter grc1155.MultiTokenGetter, tokenID string) error { rlmPath := std.PrevRealm().PkgPath() key := fqname.Construct(rlmPath, tokenID) @@ -96,17 +96,17 @@ func GetUserBalances(userNameOrAddress string) string { return getNonZeroBalances(userNameOrAddress) } -func GetGRC20(key string) *grc20.Token { +func GetToken(key string) *grc20.Token { getter := grc20reg.Get(key) return getter() } -func MustGetGRC20(key string) *grc20.Token { +func MustGetToken(key string) *grc20.Token { getter := grc20reg.MustGet(key) return getter() } -func GetGRC721(key string) grc721.IGRC721 { +func GetNFT(key string) grc721.IGRC721 { nftGetter, ok := registeredNFTs.Get(key) if !ok { return nil @@ -115,15 +115,15 @@ func GetGRC721(key string) grc721.IGRC721 { return nft } -func MustGetGRC721(key string) grc721.IGRC721 { - nftGetter := GetGRC721(key) +func MustGetNFT(key string) grc721.IGRC721 { + nftGetter := GetNFT(key) if nftGetter == nil { panic("unknown NFT: " + key) } return nftGetter } -func GetGRC1155(key string) grc1155.IGRC1155 { +func GetMultiToken(key string) grc1155.IGRC1155 { info, ok := registeredMTs.Get(key) if !ok { return nil @@ -132,8 +132,8 @@ func GetGRC1155(key string) grc1155.IGRC1155 { return mt() } -func MustGetGRC1155(key string) grc1155.IGRC1155 { - info := GetGRC1155(key) +func MustGetMultiToken(key string) grc1155.IGRC1155 { + info := GetMultiToken(key) if info == nil { panic("unknown multi-token: " + key) } @@ -149,7 +149,7 @@ func GetAllNFTs() string { return sb.String() } -func GetAllGRC20Tokens() string { +func GetAllTokens() string { var sb strings.Builder grc20reg.Iterate(func(key string, tokenGetter grc20.TokenGetter) bool { sb.WriteString(ufmt.Sprintf("Token:%s,", key)) @@ -167,17 +167,8 @@ func GetAllMultiTokens() string { return sb.String() } -func GetAllTokens() string { - return ufmt.Sprintf("%s%s%s", - GetAllNFTs(), - GetAllGRC20Tokens(), - GetAllMultiTokens()) -} - -func GetAllTokensNoNFTs() string { - return ufmt.Sprintf("%s%s", - GetAllGRC20Tokens(), - GetAllMultiTokens()) +func GetAllRegistered() string { + return GetAllNFTs() + GetAllTokens() + GetAllMultiTokens() } func Render(path string) string { From b5a8206c11594eec8106f0c0542844d303f923eb Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 21 Jan 2025 15:58:38 +0100 Subject: [PATCH 43/78] remove fqname and just use concat --- examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index 2a7b9bf3d0f..7ab713e13dd 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -7,7 +7,6 @@ import ( "strings" "gno.land/p/demo/avl" - "gno.land/p/demo/fqname" "gno.land/p/demo/grc/grc1155" "gno.land/p/demo/grc/grc20" "gno.land/p/demo/grc/grc721" @@ -59,8 +58,7 @@ func RegisterNFT(nftGetter grc721.NFTGetter, collection string, tokenId string) } rlmPath := std.PrevRealm().PkgPath() - k := ufmt.Sprintf("%s.%s", collection, tokenId) - key := fqname.Construct(rlmPath, k) + key := rlmPath + "." + collection + "." + tokenId if registeredNFTs.Has(key) { return ErrNFTAlreadyRegistered @@ -75,7 +73,7 @@ func RegisterNFT(nftGetter grc721.NFTGetter, collection string, tokenId string) func RegisterMultiToken(mtGetter grc1155.MultiTokenGetter, tokenID string) error { rlmPath := std.PrevRealm().PkgPath() - key := fqname.Construct(rlmPath, tokenID) + key := rlmPath + "." + tokenID if registeredMTs.Has(key) { return ErrMTAlreadyRegistered From 3c5d8dc5174f58d17a79d0d2a801eb23bf07fc55 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 21 Jan 2025 16:29:32 +0100 Subject: [PATCH 44/78] divide realm into multiple files for better readability --- .../r/matijamarjanovic/tokenhub/getters.gno | 94 ++++++ .../r/matijamarjanovic/tokenhub/render.gno | 213 +++++++++++++ .../r/matijamarjanovic/tokenhub/tokenhub.gno | 285 ------------------ 3 files changed, 307 insertions(+), 285 deletions(-) create mode 100644 examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno create mode 100644 examples/gno.land/r/matijamarjanovic/tokenhub/render.gno diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno new file mode 100644 index 00000000000..21866d6e6bf --- /dev/null +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno @@ -0,0 +1,94 @@ +package tokenhub + +import ( + //"std" + "strings" + "gno.land/p/demo/grc/grc20" + "gno.land/p/demo/grc/grc721" + "gno.land/p/demo/grc/grc1155" + "gno.land/r/demo/grc20reg" + "gno.land/p/demo/ufmt" +) + +func GetUserBalancesWithZeros(userNameOrAddress string) string { + return getBalances(userNameOrAddress) +} + +func GetUserBalances(userNameOrAddress string) string { + return getNonZeroBalances(userNameOrAddress) +} + +func GetToken(key string) *grc20.Token { + getter := grc20reg.Get(key) + return getter() +} + +func MustGetToken(key string) *grc20.Token { + getter := grc20reg.MustGet(key) + return getter() +} + +func GetNFT(key string) grc721.IGRC721 { + nftGetter, ok := registeredNFTs.Get(key) + if !ok { + return nil + } + nft := nftGetter.(grc721.IGRC721) + return nft +} + +func MustGetNFT(key string) grc721.IGRC721 { + nftGetter := GetNFT(key) + if nftGetter == nil { + panic("unknown NFT: " + key) + } + return nftGetter +} + +func GetMultiToken(key string) grc1155.IGRC1155 { + info, ok := registeredMTs.Get(key) + if !ok { + return nil + } + mt := info.(GRC1155TokenInfo).Collection + return mt() +} + +func MustGetMultiToken(key string) grc1155.IGRC1155 { + info := GetMultiToken(key) + if info == nil { + panic("unknown multi-token: " + key) + } + return info +} + +func GetAllNFTs() string { + var sb strings.Builder + registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { + sb.WriteString(ufmt.Sprintf("NFT:%s,", key)) + return false + }) + return sb.String() +} + +func GetAllTokens() string { + var sb strings.Builder + grc20reg.Iterate(func(key string, tokenGetter grc20.TokenGetter) bool { + sb.WriteString(ufmt.Sprintf("Token:%s,", key)) + return false + }) + return sb.String() +} + +func GetAllMultiTokens() string { + var sb strings.Builder + registeredMTs.Iterate("", "", func(key string, value interface{}) bool { + sb.WriteString(ufmt.Sprintf("MultiToken:%s,", key)) + return false + }) + return sb.String() +} + +func GetAllRegistered() string { + return GetAllNFTs() + GetAllTokens() + GetAllMultiTokens() +} diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno new file mode 100644 index 00000000000..e22c0fc7df8 --- /dev/null +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno @@ -0,0 +1,213 @@ +package tokenhub + +import ( + "strings" + "strconv" + "gno.land/p/demo/ufmt" + "gno.land/p/moul/md" + "gno.land/r/demo/grc20reg" + "gno.land/p/demo/grc/grc20" + "gno.land/p/demo/grc/grc721" +) + + + +func Render(path string) string { + var sb strings.Builder + + switch { + case path == "": + // home page + sb.WriteString(md.H1("Token Hub")) + sb.WriteString(md.Paragraph("A central registry for GRC721 NFTs, GRC20 tokens, and GRC1155 multi-tokens on gno.land")) + + links := []string{ + md.Link("GRC20 Tokens", "http://localhost:8888/r/matijamarjanovic/tokenhub:token_1"), + md.Link("GRC721 NFTs", "http://localhost:8888/r/matijamarjanovic/tokenhub:nft_1"), + md.Link("GRC1155 Multi-Tokens", "http://localhost:8888/r/matijamarjanovic/tokenhub:mt_1"), + } + sb.WriteString(md.BulletList(links)) + + case strings.HasPrefix(path, "token"): // grc20 + sb.WriteString(md.H1("GRC20 Tokens")) + var tokenItems []string + + page := 1 + if strings.HasPrefix(path, "token_") { // get page number from path + pageStr := strings.TrimPrefix(path, "token_") + if p, err := strconv.Atoi(pageStr); err == nil { + page = p + } + } + + startKey := "" + if page > 1 { + lastKey := grc20reg.IterateN("", (page-1)*pageSize, func(key string, tokenGetter grc20.TokenGetter) bool { // start position + return false + }) + startKey = lastKey + } + + lastKey := grc20reg.IterateN(startKey, pageSize, func(key string, tokenGetter grc20.TokenGetter) bool { // get current page tokens + token := tokenGetter() + tokenItems = append(tokenItems, ufmt.Sprintf("%s (%s) [%s]", + md.Bold(token.GetName()), + md.InlineCode(token.GetSymbol()), + md.InlineCode(key))) + return false + }) + + if len(tokenItems) > 0 { + sb.WriteString(md.BulletList(tokenItems)) + + sb.WriteString("\n") + sb.WriteString(md.HorizontalRule()) + + if page > 1 { + prevPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:token_%d", page-1) + sb.WriteString(md.Link("Previous", prevPage)) + sb.WriteString(" | ") + } + + if len(tokenItems) == pageSize && lastKey != "" { + nextPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:token_%d", page+1) + sb.WriteString(md.Link("Next", nextPage)) + } + } else { + sb.WriteString(md.Italic("No tokens registered yet")) + sb.WriteString("\n") + } + + case strings.HasPrefix(path, "nft"): // grc721 + sb.WriteString(md.H1("GRC721 NFTs")) + var nftItems []string + + page := 1 + if strings.HasPrefix(path, "nft_") { + pageStr := strings.TrimPrefix(path, "nft_") + if p, err := strconv.Atoi(pageStr); err == nil { + page = p + } + } + + startIdx := (page - 1) * pageSize + count := 0 + currentIdx := 0 + + registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { + if currentIdx < startIdx { + currentIdx++ + return false + } + if count >= pageSize { + return true + } + + nftGetter := value.(grc721.NFTGetter) + nft := nftGetter() + metadata, ok := nft.(grc721.IGRC721CollectionMetadata) + if !ok { + return false + } + + nftItems = append(nftItems, ufmt.Sprintf("%s (%s) [%s]", + md.Bold(metadata.Name()), + md.InlineCode(metadata.Symbol()), + md.InlineCode(key))) + + count++ + currentIdx++ + return false + }) + + if len(nftItems) > 0 { + sb.WriteString(md.BulletList(nftItems)) + sb.WriteString("\n") + sb.WriteString(md.HorizontalRule()) + + if page > 1 { + prevPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:nft_%d", page-1) + sb.WriteString(md.Link("Previous", prevPage)) + sb.WriteString(" | ") + } + + if len(nftItems) == pageSize { + nextPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:nft_%d", page+1) + sb.WriteString(md.Link("Next", nextPage)) + } + } else { + sb.WriteString(md.Italic("No NFTs registered yet")) + sb.WriteString("\n") + } + + case strings.HasPrefix(path, "mt"): // grc1155 + sb.WriteString(md.H1("GRC1155 Multi-Tokens")) + var mtItems []string + + page := 1 + if strings.HasPrefix(path, "mt_") { + pageStr := strings.TrimPrefix(path, "mt_") + if p, err := strconv.Atoi(pageStr); err == nil { + page = p + } + } + + startIdx := (page - 1) * pageSize + count := 0 + currentIdx := 0 + + registeredMTs.Iterate("", "", func(key string, value interface{}) bool { + if currentIdx < startIdx { + currentIdx++ + return false + } + if count >= pageSize { + return true + } + + info := value.(GRC1155TokenInfo) + mtItems = append(mtItems, ufmt.Sprintf("%s: %s [%s]", + md.Bold("TokenID"), + md.InlineCode(info.TokenID), + md.InlineCode(key))) + + count++ + currentIdx++ + return false + }) + + if len(mtItems) > 0 { + sb.WriteString(md.BulletList(mtItems)) + sb.WriteString("\n") + sb.WriteString(md.HorizontalRule()) + + if page > 1 { + prevPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:mt_%d", page-1) + sb.WriteString(md.Link("Previous", prevPage)) + sb.WriteString(" | ") + } + + if len(mtItems) == pageSize { + nextPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:mt_%d", page+1) + sb.WriteString(md.Link("Next", nextPage)) + } + } else { + sb.WriteString(md.Italic("No multi-tokens registered yet")) + sb.WriteString("\n") + } + + default: + sb.WriteString(md.H1("404 Not Found")) + sb.WriteString(md.Paragraph("The requested page does not exist.")) + sb.WriteString(md.Link("Back to home", "http://localhost:8888/r/matijamarjanovic/tokenhub")) + } + + // navigation footer if not on home page + if path != "" { + sb.WriteString("\n") + sb.WriteString(md.HorizontalRule()) + sb.WriteString(md.Link("Back to home", "http://localhost:8888/r/matijamarjanovic/tokenhub")) + } + + return sb.String() +} diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index 7ab713e13dd..fd12260f9f8 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -3,7 +3,6 @@ package tokenhub import ( "errors" "std" - "strconv" "strings" "gno.land/p/demo/avl" @@ -11,7 +10,6 @@ import ( "gno.land/p/demo/grc/grc20" "gno.land/p/demo/grc/grc721" "gno.land/p/demo/ufmt" - "gno.land/p/moul/md" "gno.land/r/demo/grc20reg" "gno.land/r/demo/users" ) @@ -86,289 +84,6 @@ func RegisterMultiToken(mtGetter grc1155.MultiTokenGetter, tokenID string) error return nil } -func GetUserBalancesWithZeros(userNameOrAddress string) string { - return getBalances(userNameOrAddress) -} - -func GetUserBalances(userNameOrAddress string) string { - return getNonZeroBalances(userNameOrAddress) -} - -func GetToken(key string) *grc20.Token { - getter := grc20reg.Get(key) - return getter() -} - -func MustGetToken(key string) *grc20.Token { - getter := grc20reg.MustGet(key) - return getter() -} - -func GetNFT(key string) grc721.IGRC721 { - nftGetter, ok := registeredNFTs.Get(key) - if !ok { - return nil - } - nft := nftGetter.(grc721.IGRC721) - return nft -} - -func MustGetNFT(key string) grc721.IGRC721 { - nftGetter := GetNFT(key) - if nftGetter == nil { - panic("unknown NFT: " + key) - } - return nftGetter -} - -func GetMultiToken(key string) grc1155.IGRC1155 { - info, ok := registeredMTs.Get(key) - if !ok { - return nil - } - mt := info.(GRC1155TokenInfo).Collection - return mt() -} - -func MustGetMultiToken(key string) grc1155.IGRC1155 { - info := GetMultiToken(key) - if info == nil { - panic("unknown multi-token: " + key) - } - return info -} - -func GetAllNFTs() string { - var sb strings.Builder - registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { - sb.WriteString(ufmt.Sprintf("NFT:%s,", key)) - return false - }) - return sb.String() -} - -func GetAllTokens() string { - var sb strings.Builder - grc20reg.Iterate(func(key string, tokenGetter grc20.TokenGetter) bool { - sb.WriteString(ufmt.Sprintf("Token:%s,", key)) - return false - }) - return sb.String() -} - -func GetAllMultiTokens() string { - var sb strings.Builder - registeredMTs.Iterate("", "", func(key string, value interface{}) bool { - sb.WriteString(ufmt.Sprintf("MultiToken:%s,", key)) - return false - }) - return sb.String() -} - -func GetAllRegistered() string { - return GetAllNFTs() + GetAllTokens() + GetAllMultiTokens() -} - -func Render(path string) string { - var sb strings.Builder - - switch { - case path == "": - // home page - sb.WriteString(md.H1("Token Hub")) - sb.WriteString(md.Paragraph("A central registry for GRC721 NFTs, GRC20 tokens, and GRC1155 multi-tokens on gno.land")) - - links := []string{ - md.Link("GRC20 Tokens", "http://localhost:8888/r/matijamarjanovic/tokenhub:token_1"), - md.Link("GRC721 NFTs", "http://localhost:8888/r/matijamarjanovic/tokenhub:nft_1"), - md.Link("GRC1155 Multi-Tokens", "http://localhost:8888/r/matijamarjanovic/tokenhub:mt_1"), - } - sb.WriteString(md.BulletList(links)) - - case strings.HasPrefix(path, "token"): // grc20 - sb.WriteString(md.H1("GRC20 Tokens")) - var tokenItems []string - - page := 1 - if strings.HasPrefix(path, "token_") { // get page number from path - pageStr := strings.TrimPrefix(path, "token_") - if p, err := strconv.Atoi(pageStr); err == nil { - page = p - } - } - - startKey := "" - if page > 1 { - lastKey := grc20reg.IterateN("", (page-1)*pageSize, func(key string, tokenGetter grc20.TokenGetter) bool { // start position - return false - }) - startKey = lastKey - } - - lastKey := grc20reg.IterateN(startKey, pageSize, func(key string, tokenGetter grc20.TokenGetter) bool { // get current page tokens - token := tokenGetter() - tokenItems = append(tokenItems, ufmt.Sprintf("%s (%s) [%s]", - md.Bold(token.GetName()), - md.InlineCode(token.GetSymbol()), - md.InlineCode(key))) - return false - }) - - if len(tokenItems) > 0 { - sb.WriteString(md.BulletList(tokenItems)) - - sb.WriteString("\n") - sb.WriteString(md.HorizontalRule()) - - if page > 1 { - prevPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:token_%d", page-1) - sb.WriteString(md.Link("Previous", prevPage)) - sb.WriteString(" | ") - } - - if len(tokenItems) == pageSize && lastKey != "" { - nextPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:token_%d", page+1) - sb.WriteString(md.Link("Next", nextPage)) - } - } else { - sb.WriteString(md.Italic("No tokens registered yet")) - sb.WriteString("\n") - } - - case strings.HasPrefix(path, "nft"): // grc721 - sb.WriteString(md.H1("GRC721 NFTs")) - var nftItems []string - - page := 1 - if strings.HasPrefix(path, "nft_") { - pageStr := strings.TrimPrefix(path, "nft_") - if p, err := strconv.Atoi(pageStr); err == nil { - page = p - } - } - - startIdx := (page - 1) * pageSize - count := 0 - currentIdx := 0 - - registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { - if currentIdx < startIdx { - currentIdx++ - return false - } - if count >= pageSize { - return true - } - - nftGetter := value.(grc721.NFTGetter) - nft := nftGetter() - metadata, ok := nft.(grc721.IGRC721CollectionMetadata) - if !ok { - return false - } - - nftItems = append(nftItems, ufmt.Sprintf("%s (%s) [%s]", - md.Bold(metadata.Name()), - md.InlineCode(metadata.Symbol()), - md.InlineCode(key))) - - count++ - currentIdx++ - return false - }) - - if len(nftItems) > 0 { - sb.WriteString(md.BulletList(nftItems)) - sb.WriteString("\n") - sb.WriteString(md.HorizontalRule()) - - if page > 1 { - prevPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:nft_%d", page-1) - sb.WriteString(md.Link("Previous", prevPage)) - sb.WriteString(" | ") - } - - if len(nftItems) == pageSize { - nextPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:nft_%d", page+1) - sb.WriteString(md.Link("Next", nextPage)) - } - } else { - sb.WriteString(md.Italic("No NFTs registered yet")) - sb.WriteString("\n") - } - - case strings.HasPrefix(path, "mt"): // grc1155 - sb.WriteString(md.H1("GRC1155 Multi-Tokens")) - var mtItems []string - - page := 1 - if strings.HasPrefix(path, "mt_") { - pageStr := strings.TrimPrefix(path, "mt_") - if p, err := strconv.Atoi(pageStr); err == nil { - page = p - } - } - - startIdx := (page - 1) * pageSize - count := 0 - currentIdx := 0 - - registeredMTs.Iterate("", "", func(key string, value interface{}) bool { - if currentIdx < startIdx { - currentIdx++ - return false - } - if count >= pageSize { - return true - } - - info := value.(GRC1155TokenInfo) - mtItems = append(mtItems, ufmt.Sprintf("%s: %s [%s]", - md.Bold("TokenID"), - md.InlineCode(info.TokenID), - md.InlineCode(key))) - - count++ - currentIdx++ - return false - }) - - if len(mtItems) > 0 { - sb.WriteString(md.BulletList(mtItems)) - sb.WriteString("\n") - sb.WriteString(md.HorizontalRule()) - - if page > 1 { - prevPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:mt_%d", page-1) - sb.WriteString(md.Link("Previous", prevPage)) - sb.WriteString(" | ") - } - - if len(mtItems) == pageSize { - nextPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:mt_%d", page+1) - sb.WriteString(md.Link("Next", nextPage)) - } - } else { - sb.WriteString(md.Italic("No multi-tokens registered yet")) - sb.WriteString("\n") - } - - default: - sb.WriteString(md.H1("404 Not Found")) - sb.WriteString(md.Paragraph("The requested page does not exist.")) - sb.WriteString(md.Link("Back to home", "http://localhost:8888/r/matijamarjanovic/tokenhub")) - } - - // navigation footer if not on home page - if path != "" { - sb.WriteString("\n") - sb.WriteString(md.HorizontalRule()) - sb.WriteString(md.Link("Back to home", "http://localhost:8888/r/matijamarjanovic/tokenhub")) - } - - return sb.String() -} - func getBalances(input string) string { addr := getAddressForUsername(input) if addr == "" { From 43b4b5227cc72bb6781b1c290a043a3d8920bc17 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 21 Jan 2025 16:59:09 +0100 Subject: [PATCH 45/78] render refactor; new functions for each case, use string concat instead of sb --- .../r/matijamarjanovic/tokenhub/render.gno | 214 ++++++++++-------- .../r/matijamarjanovic/tokenhub/tokenhub.gno | 5 +- 2 files changed, 123 insertions(+), 96 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno index e22c0fc7df8..35adac53c3f 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno @@ -10,85 +10,118 @@ import ( "gno.land/p/demo/grc/grc721" ) - - func Render(path string) string { - var sb strings.Builder + var out string switch { case path == "": - // home page - sb.WriteString(md.H1("Token Hub")) - sb.WriteString(md.Paragraph("A central registry for GRC721 NFTs, GRC20 tokens, and GRC1155 multi-tokens on gno.land")) - - links := []string{ - md.Link("GRC20 Tokens", "http://localhost:8888/r/matijamarjanovic/tokenhub:token_1"), - md.Link("GRC721 NFTs", "http://localhost:8888/r/matijamarjanovic/tokenhub:nft_1"), - md.Link("GRC1155 Multi-Tokens", "http://localhost:8888/r/matijamarjanovic/tokenhub:mt_1"), - } - sb.WriteString(md.BulletList(links)) + out = RenderHome() - case strings.HasPrefix(path, "token"): // grc20 - sb.WriteString(md.H1("GRC20 Tokens")) - var tokenItems []string + case strings.HasPrefix(path, token): + out = RenderToken(path) - page := 1 - if strings.HasPrefix(path, "token_") { // get page number from path - pageStr := strings.TrimPrefix(path, "token_") - if p, err := strconv.Atoi(pageStr); err == nil { - page = p - } - } + case strings.HasPrefix(path, nft): + out = RenderNFT(path) - startKey := "" - if page > 1 { - lastKey := grc20reg.IterateN("", (page-1)*pageSize, func(key string, tokenGetter grc20.TokenGetter) bool { // start position - return false - }) - startKey = lastKey + case strings.HasPrefix(path, mt): + out = RenderMT(path) + + default: + out = md.H1("404 Not Found") + out += md.Paragraph("The requested page does not exist.") + out += md.Link("Back to home", "http://localhost:8888/r/matijamarjanovic/tokenhub") + } + + + // navigation footer if not on home page + if path != "" { + out += "\n" + out += md.HorizontalRule() + out += md.Link("Back to home", "http://localhost:8888/r/matijamarjanovic/tokenhub") + } + + return out +} + +func RenderHome() string { + out := md.H1("Token Hub") + out += md.Paragraph("A central registry for GRC721 NFTs, GRC20 tokens, and GRC1155 multi-tokens on gno.land") + + links := []string{ + md.Link("GRC20 Tokens", "http://localhost:8888/r/matijamarjanovic/tokenhub:token_1"), + md.Link("GRC721 NFTs", "http://localhost:8888/r/matijamarjanovic/tokenhub:nft_1"), + md.Link("GRC1155 Multi-Tokens", "http://localhost:8888/r/matijamarjanovic/tokenhub:mt_1"), + } + out +=md.BulletList(links) + + return out +} + +func RenderToken(path string) string { + out := md.H1("GRC20 Tokens") + var tokenItems []string + + page := 1 + if strings.HasPrefix(path, token + "_") { // get page number from path + pageStr := strings.TrimPrefix(path, token + "_") + if p, err := strconv.Atoi(pageStr); err == nil { + page = p } + } - lastKey := grc20reg.IterateN(startKey, pageSize, func(key string, tokenGetter grc20.TokenGetter) bool { // get current page tokens - token := tokenGetter() - tokenItems = append(tokenItems, ufmt.Sprintf("%s (%s) [%s]", - md.Bold(token.GetName()), - md.InlineCode(token.GetSymbol()), - md.InlineCode(key))) + startKey := "" + if page > 1 { + lastKey := grc20reg.IterateN("", (page-1)*pageSize, func(key string, tokenGetter grc20.TokenGetter) bool { // start position return false }) + startKey = lastKey + } - if len(tokenItems) > 0 { - sb.WriteString(md.BulletList(tokenItems)) + lastKey := grc20reg.IterateN(startKey, pageSize, func(key string, tokenGetter grc20.TokenGetter) bool { // get current page tokens + token := tokenGetter() + tokenItems = append(tokenItems, ufmt.Sprintf("%s (%s) [%s]", + md.Bold(token.GetName()), + md.InlineCode(token.GetSymbol()), + md.InlineCode(key))) + return false + }) - sb.WriteString("\n") - sb.WriteString(md.HorizontalRule()) + if len(tokenItems) > 0 { + out += md.BulletList(tokenItems) - if page > 1 { - prevPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:token_%d", page-1) - sb.WriteString(md.Link("Previous", prevPage)) - sb.WriteString(" | ") - } + out += "\n" - if len(tokenItems) == pageSize && lastKey != "" { - nextPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:token_%d", page+1) - sb.WriteString(md.Link("Next", nextPage)) - } - } else { - sb.WriteString(md.Italic("No tokens registered yet")) - sb.WriteString("\n") + if page > 1 { + out += md.HorizontalRule() + prevPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:token_%d", page-1) + out += md.Link("Previous", prevPage) + out += " | " } - case strings.HasPrefix(path, "nft"): // grc721 - sb.WriteString(md.H1("GRC721 NFTs")) - var nftItems []string + if len(tokenItems) == pageSize && lastKey != "" { + nextPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:token_%d", page+1) + out += md.Link("Next", nextPage) + } + } else { + out += md.Italic("No tokens registered yet") + out += "\n" + } - page := 1 - if strings.HasPrefix(path, "nft_") { - pageStr := strings.TrimPrefix(path, "nft_") - if p, err := strconv.Atoi(pageStr); err == nil { - page = p - } + return out +} + +func RenderNFT(path string) string { + var out string + out = md.H1("GRC721 NFTs") + var nftItems []string + + page := 1 + if strings.HasPrefix(path, nft + "_") { + pageStr := strings.TrimPrefix(path, nft + "_") + if p, err := strconv.Atoi(pageStr); err == nil { + page = p } + } startIdx := (page - 1) * pageSize count := 0 @@ -121,32 +154,36 @@ func Render(path string) string { }) if len(nftItems) > 0 { - sb.WriteString(md.BulletList(nftItems)) - sb.WriteString("\n") - sb.WriteString(md.HorizontalRule()) + out += md.BulletList(nftItems) + out += "\n" + out += md.HorizontalRule() if page > 1 { prevPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:nft_%d", page-1) - sb.WriteString(md.Link("Previous", prevPage)) - sb.WriteString(" | ") + out += md.Link("Previous", prevPage) + out += " | " } if len(nftItems) == pageSize { nextPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:nft_%d", page+1) - sb.WriteString(md.Link("Next", nextPage)) + out += md.Link("Next", nextPage) } } else { - sb.WriteString(md.Italic("No NFTs registered yet")) - sb.WriteString("\n") + out += md.Italic("No NFTs registered yet") + out += "\n" } - case strings.HasPrefix(path, "mt"): // grc1155 - sb.WriteString(md.H1("GRC1155 Multi-Tokens")) - var mtItems []string + return out +} + +func RenderMT(path string) string { + var out string + out = md.H1("GRC1155 Multi-Tokens") + var mtItems []string page := 1 - if strings.HasPrefix(path, "mt_") { - pageStr := strings.TrimPrefix(path, "mt_") + if strings.HasPrefix(path, mt + "_") { + pageStr := strings.TrimPrefix(path, mt + "_") if p, err := strconv.Atoi(pageStr); err == nil { page = p } @@ -177,37 +214,24 @@ func Render(path string) string { }) if len(mtItems) > 0 { - sb.WriteString(md.BulletList(mtItems)) - sb.WriteString("\n") - sb.WriteString(md.HorizontalRule()) + out += md.BulletList(mtItems) + out += "\n" + out += md.HorizontalRule() if page > 1 { prevPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:mt_%d", page-1) - sb.WriteString(md.Link("Previous", prevPage)) - sb.WriteString(" | ") + out += md.Link("Previous", prevPage) + out += " | " } if len(mtItems) == pageSize { nextPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:mt_%d", page+1) - sb.WriteString(md.Link("Next", nextPage)) + out += md.Link("Next", nextPage) } } else { - sb.WriteString(md.Italic("No multi-tokens registered yet")) - sb.WriteString("\n") - } - - default: - sb.WriteString(md.H1("404 Not Found")) - sb.WriteString(md.Paragraph("The requested page does not exist.")) - sb.WriteString(md.Link("Back to home", "http://localhost:8888/r/matijamarjanovic/tokenhub")) - } - - // navigation footer if not on home page - if path != "" { - sb.WriteString("\n") - sb.WriteString(md.HorizontalRule()) - sb.WriteString(md.Link("Back to home", "http://localhost:8888/r/matijamarjanovic/tokenhub")) + out += md.Italic("No multi-tokens registered yet") + out += "\n" } - return sb.String() + return out } diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index fd12260f9f8..a4502f80907 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -32,7 +32,10 @@ var ( ErrNFTNotMetadata = errors.New("NFT must implement IGRC721CollectionMetadata") ) -const pageSize = 2 +const pageSize = 5 +const token = "token" // grc20 +const nft = "nft" // grc721 +const mt = "mt" // grc1155 func RegisterToken(tokenGetter grc20.TokenGetter, slug string) { grc20reg.Register(tokenGetter, slug) From 59586e50ccccfd7ef9056d294b7d249d530b6c0e Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 21 Jan 2025 17:50:10 +0100 Subject: [PATCH 46/78] - change the pagination implement pager from p/demo/avl - instead of iteration in grc20reg use read only tree from avl --- .../gno.land/r/demo/grc20reg/grc20reg.gno | 30 +-- .../r/matijamarjanovic/tokenhub/getters.gno | 22 +- .../r/matijamarjanovic/tokenhub/render.gno | 204 ++++++------------ .../r/matijamarjanovic/tokenhub/tokenhub.gno | 11 +- 4 files changed, 89 insertions(+), 178 deletions(-) diff --git a/examples/gno.land/r/demo/grc20reg/grc20reg.gno b/examples/gno.land/r/demo/grc20reg/grc20reg.gno index c9715c1b7b5..a69fad338ae 100644 --- a/examples/gno.land/r/demo/grc20reg/grc20reg.gno +++ b/examples/gno.land/r/demo/grc20reg/grc20reg.gno @@ -4,6 +4,7 @@ import ( "std" "gno.land/p/demo/avl" + "gno.land/p/demo/avl/rotree" "gno.land/p/demo/fqname" "gno.land/p/demo/grc/grc20" "gno.land/p/demo/ufmt" @@ -73,31 +74,8 @@ func Render(path string) string { } } -func Iterate(callback func(key string, token grc20.TokenGetter) bool) { - registry.Iterate("", "", func(key string, tokenI interface{}) bool { - tokenGetter := tokenI.(grc20.TokenGetter) - return callback(key, tokenGetter) - }) -} - -// iterate through n items starting from the startKey (pagination) -func IterateN(startKey string, n int, callback func(key string, token grc20.TokenGetter) bool) string { - var lastKey string - count := 0 - - registry.Iterate(startKey, "", func(key string, tokenI interface{}) bool { - if count >= n { - return true - } - - tokenGetter := tokenI.(grc20.TokenGetter) - lastKey = key - count++ - - return callback(key, tokenGetter) - }) +const registerEvent = "register" - return lastKey +func GetRegistry() *rotree.ReadOnlyTree { + return rotree.Wrap(registry, nil) } - -const registerEvent = "register" diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno index 21866d6e6bf..d1ab439a96f 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno @@ -2,7 +2,7 @@ package tokenhub import ( //"std" - "strings" + //"strings" "gno.land/p/demo/grc/grc20" "gno.land/p/demo/grc/grc721" "gno.land/p/demo/grc/grc1155" @@ -63,30 +63,30 @@ func MustGetMultiToken(key string) grc1155.IGRC1155 { } func GetAllNFTs() string { - var sb strings.Builder + var out string registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { - sb.WriteString(ufmt.Sprintf("NFT:%s,", key)) + out += ufmt.Sprintf("NFT:%s,", key) return false }) - return sb.String() + return out } func GetAllTokens() string { - var sb strings.Builder - grc20reg.Iterate(func(key string, tokenGetter grc20.TokenGetter) bool { - sb.WriteString(ufmt.Sprintf("Token:%s,", key)) + var out string + grc20reg.GetRegistry().Iterate("", "", func(key string, value interface{}) bool{ + out += ufmt.Sprintf("Token:%s,", key) return false }) - return sb.String() + return out } func GetAllMultiTokens() string { - var sb strings.Builder + var out string registeredMTs.Iterate("", "", func(key string, value interface{}) bool { - sb.WriteString(ufmt.Sprintf("MultiToken:%s,", key)) + out += ufmt.Sprintf("MultiToken:%s,", key) return false }) - return sb.String() + return out } func GetAllRegistered() string { diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno index 35adac53c3f..b211fe2bc64 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno @@ -2,12 +2,13 @@ package tokenhub import ( "strings" - "strconv" + //"strconv" "gno.land/p/demo/ufmt" "gno.land/p/moul/md" "gno.land/r/demo/grc20reg" "gno.land/p/demo/grc/grc20" "gno.land/p/demo/grc/grc721" + "gno.land/p/demo/avl/pager" ) func Render(path string) string { @@ -36,7 +37,6 @@ func Render(path string) string { // navigation footer if not on home page if path != "" { out += "\n" - out += md.HorizontalRule() out += md.Link("Back to home", "http://localhost:8888/r/matijamarjanovic/tokenhub") } @@ -48,9 +48,9 @@ func RenderHome() string { out += md.Paragraph("A central registry for GRC721 NFTs, GRC20 tokens, and GRC1155 multi-tokens on gno.land") links := []string{ - md.Link("GRC20 Tokens", "http://localhost:8888/r/matijamarjanovic/tokenhub:token_1"), - md.Link("GRC721 NFTs", "http://localhost:8888/r/matijamarjanovic/tokenhub:nft_1"), - md.Link("GRC1155 Multi-Tokens", "http://localhost:8888/r/matijamarjanovic/tokenhub:mt_1"), + md.Link("GRC20 Tokens", "http://localhost:8888/r/matijamarjanovic/tokenhub:tokens"), + md.Link("GRC721 NFTs", "http://localhost:8888/r/matijamarjanovic/tokenhub:nfts"), + md.Link("GRC1155 Multi-Tokens", "http://localhost:8888/r/matijamarjanovic/tokenhub:mts"), } out +=md.BulletList(links) @@ -61,46 +61,28 @@ func RenderToken(path string) string { out := md.H1("GRC20 Tokens") var tokenItems []string - page := 1 - if strings.HasPrefix(path, token + "_") { // get page number from path - pageStr := strings.TrimPrefix(path, token + "_") - if p, err := strconv.Atoi(pageStr); err == nil { - page = p - } - } + tokenPager := pager.NewPager(grc20reg.GetRegistry(), pageSize, false) - startKey := "" - if page > 1 { - lastKey := grc20reg.IterateN("", (page-1)*pageSize, func(key string, tokenGetter grc20.TokenGetter) bool { // start position - return false - }) - startKey = lastKey - } + page := tokenPager.MustGetPageByPath(path) - lastKey := grc20reg.IterateN(startKey, pageSize, func(key string, tokenGetter grc20.TokenGetter) bool { // get current page tokens + for _, item := range page.Items { + tokenGetter := item.Value.(grc20.TokenGetter) token := tokenGetter() tokenItems = append(tokenItems, ufmt.Sprintf("%s (%s) [%s]", md.Bold(token.GetName()), md.InlineCode(token.GetSymbol()), - md.InlineCode(key))) - return false - }) + md.InlineCode(item.Key))) + } if len(tokenItems) > 0 { out += md.BulletList(tokenItems) - out += "\n" - - if page > 1 { + + if picker := page.Picker(); picker != "" { + out += md.HorizontalRule() + out += md.Paragraph(picker) + }else{ out += md.HorizontalRule() - prevPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:token_%d", page-1) - out += md.Link("Previous", prevPage) - out += " | " - } - - if len(tokenItems) == pageSize && lastKey != "" { - nextPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:token_%d", page+1) - out += md.Link("Next", nextPage) } } else { out += md.Italic("No tokens registered yet") @@ -111,124 +93,72 @@ func RenderToken(path string) string { } func RenderNFT(path string) string { - var out string - out = md.H1("GRC721 NFTs") + out := md.H1("GRC721 NFTs") var nftItems []string - page := 1 - if strings.HasPrefix(path, nft + "_") { - pageStr := strings.TrimPrefix(path, nft + "_") - if p, err := strconv.Atoi(pageStr); err == nil { - page = p + nftPager := pager.NewPager(registeredNFTs, pageSize, false) + + page := nftPager.MustGetPageByPath(path) + + for _, item := range page.Items { + nftGetter := item.Value.(grc721.NFTGetter) + nft := nftGetter() + metadata, ok := nft.(grc721.IGRC721CollectionMetadata) + if !ok { + continue } + + nftItems = append(nftItems, ufmt.Sprintf("%s (%s) [%s]", + md.Bold(metadata.Name()), + md.InlineCode(metadata.Symbol()), + md.InlineCode(item.Key))) } - startIdx := (page - 1) * pageSize - count := 0 - currentIdx := 0 - - registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { - if currentIdx < startIdx { - currentIdx++ - return false - } - if count >= pageSize { - return true - } - - nftGetter := value.(grc721.NFTGetter) - nft := nftGetter() - metadata, ok := nft.(grc721.IGRC721CollectionMetadata) - if !ok { - return false - } - - nftItems = append(nftItems, ufmt.Sprintf("%s (%s) [%s]", - md.Bold(metadata.Name()), - md.InlineCode(metadata.Symbol()), - md.InlineCode(key))) - - count++ - currentIdx++ - return false - }) - - if len(nftItems) > 0 { - out += md.BulletList(nftItems) - out += "\n" + if len(nftItems) > 0 { + out += md.BulletList(nftItems) + out += "\n" + + if picker := page.Picker(); picker != "" { + out += md.HorizontalRule() + out += md.Paragraph(picker) + }else{ out += md.HorizontalRule() - - if page > 1 { - prevPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:nft_%d", page-1) - out += md.Link("Previous", prevPage) - out += " | " - } - - if len(nftItems) == pageSize { - nextPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:nft_%d", page+1) - out += md.Link("Next", nextPage) - } - } else { - out += md.Italic("No NFTs registered yet") - out += "\n" } + } else { + out += md.Italic("No NFTs registered yet") + out += "\n" + } - return out + return out } func RenderMT(path string) string { - var out string - out = md.H1("GRC1155 Multi-Tokens") + out := md.H1("GRC1155 Multi-Tokens") var mtItems []string - page := 1 - if strings.HasPrefix(path, mt + "_") { - pageStr := strings.TrimPrefix(path, mt + "_") - if p, err := strconv.Atoi(pageStr); err == nil { - page = p - } - } + mtPager := pager.NewPager(registeredMTs, pageSize, false) - startIdx := (page - 1) * pageSize - count := 0 - currentIdx := 0 - - registeredMTs.Iterate("", "", func(key string, value interface{}) bool { - if currentIdx < startIdx { - currentIdx++ - return false - } - if count >= pageSize { - return true - } - - info := value.(GRC1155TokenInfo) - mtItems = append(mtItems, ufmt.Sprintf("%s: %s [%s]", - md.Bold("TokenID"), - md.InlineCode(info.TokenID), - md.InlineCode(key))) - - count++ - currentIdx++ - return false - }) - - if len(mtItems) > 0 { - out += md.BulletList(mtItems) - out += "\n" - out += md.HorizontalRule() + page := mtPager.MustGetPageByPath(path) - if page > 1 { - prevPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:mt_%d", page-1) - out += md.Link("Previous", prevPage) - out += " | " - } - - if len(mtItems) == pageSize { - nextPage := ufmt.Sprintf("http://localhost:8888/r/matijamarjanovic/tokenhub:mt_%d", page+1) - out += md.Link("Next", nextPage) - } - } else { + for _, item := range page.Items { + info := item.Value.(GRC1155TokenInfo) + mtItems = append(mtItems, ufmt.Sprintf("%s: %s [%s]", + md.Bold("TokenID"), + md.InlineCode(info.TokenID), + md.InlineCode(item.Key))) + } + + if len(mtItems) > 0 { + out += md.BulletList(mtItems) + out += "\n" + + if picker := page.Picker(); picker != "" { + out += md.HorizontalRule() + out += md.Paragraph(picker) + }else{ + out += md.HorizontalRule() + } + } else { out += md.Italic("No multi-tokens registered yet") out += "\n" } diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index a4502f80907..6d6cd3b2be9 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -20,7 +20,8 @@ type GRC1155TokenInfo struct { } var ( - registeredNFTs = avl.NewTree() // rlmPath[.slug] -> NFTInfo + registeredTokens = avl.NewTree() // rlmPath[.slug] -> grc20.TokenGetter + registeredNFTs = avl.NewTree() // rlmPath[.slug] -> grc721.NFTGetter registeredMTs = avl.NewTree() // rlmPath[.slug] -> GRC1155TokenInfo ErrNFTAlreadyRegistered = errors.New("NFT already registered") @@ -32,7 +33,7 @@ var ( ErrNFTNotMetadata = errors.New("NFT must implement IGRC721CollectionMetadata") ) -const pageSize = 5 +const pageSize = 1 const token = "token" // grc20 const nft = "nft" // grc721 const mt = "mt" // grc1155 @@ -107,7 +108,8 @@ func getBalances(input string) string { return false }) - grc20reg.Iterate(func(key string, tokenGetter grc20.TokenGetter) bool { + grc20reg.GetRegistry().Iterate("", "", func(key string, value interface{}) bool { + tokenGetter := value.(grc20.TokenGetter) token := tokenGetter() balance := token.BalanceOf(addr) balances.Set("token:"+key, balance) @@ -167,7 +169,8 @@ func getNonZeroBalances(input string) string { return false }) - grc20reg.Iterate(func(key string, tokenGetter grc20.TokenGetter) bool { + grc20reg.GetRegistry().Iterate("", "", func(key string, value interface{}) bool { + tokenGetter := value.(grc20.TokenGetter) token := tokenGetter() balance := token.BalanceOf(addr) if balance > 0 { From f48d034fab3869fc4a755deced2417862490a629 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 21 Jan 2025 18:05:22 +0100 Subject: [PATCH 47/78] -refactor private getter -remove test --- .../gno.land/r/matijamarjanovic/test/gno.mod | 1 - .../gno.land/r/matijamarjanovic/test/test.gno | 9 -- .../r/matijamarjanovic/tokenhub/getters.gno | 146 +++++++++++++++++- .../r/matijamarjanovic/tokenhub/render.gno | 1 - .../r/matijamarjanovic/tokenhub/tokenhub.gno | 140 ----------------- 5 files changed, 144 insertions(+), 153 deletions(-) delete mode 100644 examples/gno.land/r/matijamarjanovic/test/gno.mod delete mode 100644 examples/gno.land/r/matijamarjanovic/test/test.gno diff --git a/examples/gno.land/r/matijamarjanovic/test/gno.mod b/examples/gno.land/r/matijamarjanovic/test/gno.mod deleted file mode 100644 index a27bf924173..00000000000 --- a/examples/gno.land/r/matijamarjanovic/test/gno.mod +++ /dev/null @@ -1 +0,0 @@ -module "gno.land/r/matijamarjanovic/test" diff --git a/examples/gno.land/r/matijamarjanovic/test/test.gno b/examples/gno.land/r/matijamarjanovic/test/test.gno deleted file mode 100644 index 88599c8c01b..00000000000 --- a/examples/gno.land/r/matijamarjanovic/test/test.gno +++ /dev/null @@ -1,9 +0,0 @@ -package test -//please ignore this file it will be deleted soon in last commit - -func Test() { -} - -func Render(path string) string { - return "" -} diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno index d1ab439a96f..cddd98825cd 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno @@ -1,13 +1,17 @@ package tokenhub import ( - //"std" - //"strings" + "std" + "strings" + + "gno.land/p/demo/avl" "gno.land/p/demo/grc/grc20" "gno.land/p/demo/grc/grc721" "gno.land/p/demo/grc/grc1155" "gno.land/r/demo/grc20reg" "gno.land/p/demo/ufmt" + "gno.land/r/demo/users" + ) func GetUserBalancesWithZeros(userNameOrAddress string) string { @@ -92,3 +96,141 @@ func GetAllMultiTokens() string { func GetAllRegistered() string { return GetAllNFTs() + GetAllTokens() + GetAllMultiTokens() } + + +func getBalances(input string) string { + addr := getAddressForUsername(input) + if addr == "" { + panic("invalid address or username: " + input) + } + + balances := avl.NewTree() + var sb strings.Builder + + registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { + nftGetter := value.(grc721.NFTGetter) + nft := nftGetter() + key_parts := strings.Split(key, ".") + owner, err := nft.OwnerOf(grc721.TokenID(key_parts[len(key_parts)-1])) + if err == nil && addr == owner { // show only the nfts owner owns + balances.Set("nft:"+key, 1) + } + return false + }) + + grc20reg.GetRegistry().Iterate("", "", func(key string, value interface{}) bool { + tokenGetter := value.(grc20.TokenGetter) + token := tokenGetter() + balance := token.BalanceOf(addr) + balances.Set("token:"+key, balance) + return false + }) + + registeredMTs.Iterate("", "", func(key string, value interface{}) bool { + info := value.(GRC1155TokenInfo) + mt := info.Collection() + balance, err := mt.BalanceOf(addr, grc1155.TokenID(info.TokenID)) + if err == nil { + balances.Set("mt:"+key, balance) + } + return false + }) + + sb.WriteString("Balances: ") + balances.Iterate("", "", func(key string, value interface{}) bool { + switch { + case strings.HasPrefix(key, "nft:"): + symbol := strings.TrimPrefix(key, "nft:") + sb.WriteString(ufmt.Sprintf("NFT:%s,", symbol)) + case strings.HasPrefix(key, "token:"): + balance := value.(uint64) + symbol := strings.TrimPrefix(key, "token:") + sb.WriteString(ufmt.Sprintf("Token:%s:%d,", symbol, balance)) + case strings.HasPrefix(key, "mt:"): + balance := value.(uint64) + symbol := strings.TrimPrefix(key, "mt:") + sb.WriteString(ufmt.Sprintf("MultiToken:%s:%d,", + symbol, + balance)) + } + return false + }) + + return sb.String() +} + +func getNonZeroBalances(input string) string { + addr := getAddressForUsername(input) + if addr == "" { + panic("invalid address or username: " + input) + } + + balances := avl.NewTree() + var sb strings.Builder + + registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { + nftGetter := value.(grc721.NFTGetter) + nft := nftGetter() + key_parts := strings.Split(key, ".") + owner, err := nft.OwnerOf(grc721.TokenID(key_parts[len(key_parts)-1])) + if err == nil && addr == owner { // show only the nfts owner owns + balances.Set("nft:"+key, 1) + } + return false + }) + + grc20reg.GetRegistry().Iterate("", "", func(key string, value interface{}) bool { + tokenGetter := value.(grc20.TokenGetter) + token := tokenGetter() + balance := token.BalanceOf(addr) + if balance > 0 { + balances.Set("token:"+key, balance) + } + return false + }) + + registeredMTs.Iterate("", "", func(key string, value interface{}) bool { + info := value.(GRC1155TokenInfo) + mt := info.Collection() + balance, err := mt.BalanceOf(addr, grc1155.TokenID(info.TokenID)) + if err == nil && balance > 0 { + balances.Set("mt:"+key, balance) + } + return false + }) + + sb.WriteString("Non-Zero Balances: ") + balances.Iterate("", "", func(key string, value interface{}) bool { + switch { + case strings.HasPrefix(key, "nft:"): + symbol := strings.TrimPrefix(key, "nft:") + sb.WriteString(ufmt.Sprintf("NFT:%s,", symbol)) + case strings.HasPrefix(key, "token:"): + balance := value.(uint64) + symbol := strings.TrimPrefix(key, "token:") + sb.WriteString(ufmt.Sprintf("Token:%s:%d,", symbol, balance)) + case strings.HasPrefix(key, "mt:"): + balance := value.(uint64) + symbol := strings.TrimPrefix(key, "mt:") + sb.WriteString(ufmt.Sprintf("MultiToken:%s:%d,", + symbol, + balance)) + } + return false + }) + + return sb.String() +} + +func getAddressForUsername(addrOrName string) std.Address { + addr := std.Address(addrOrName) + + if addr.IsValid() { + return addr + } + if user := users.GetUserByName(addrOrName); user != nil { + return user.Address + } + + return "" +} diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno index b211fe2bc64..3127d9c2d02 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno @@ -2,7 +2,6 @@ package tokenhub import ( "strings" - //"strconv" "gno.land/p/demo/ufmt" "gno.land/p/moul/md" "gno.land/r/demo/grc20reg" diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index 6d6cd3b2be9..5e4864c7dd8 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -3,15 +3,12 @@ package tokenhub import ( "errors" "std" - "strings" "gno.land/p/demo/avl" "gno.land/p/demo/grc/grc1155" "gno.land/p/demo/grc/grc20" "gno.land/p/demo/grc/grc721" - "gno.land/p/demo/ufmt" "gno.land/r/demo/grc20reg" - "gno.land/r/demo/users" ) type GRC1155TokenInfo struct { @@ -87,140 +84,3 @@ func RegisterMultiToken(mtGetter grc1155.MultiTokenGetter, tokenID string) error }) return nil } - -func getBalances(input string) string { - addr := getAddressForUsername(input) - if addr == "" { - panic("invalid address or username: " + input) - } - - balances := avl.NewTree() - var sb strings.Builder - - registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { - nftGetter := value.(grc721.NFTGetter) - nft := nftGetter() - key_parts := strings.Split(key, ".") - owner, err := nft.OwnerOf(grc721.TokenID(key_parts[len(key_parts)-1])) - if err == nil && addr == owner { // show only the nfts owner owns - balances.Set("nft:"+key, 1) - } - return false - }) - - grc20reg.GetRegistry().Iterate("", "", func(key string, value interface{}) bool { - tokenGetter := value.(grc20.TokenGetter) - token := tokenGetter() - balance := token.BalanceOf(addr) - balances.Set("token:"+key, balance) - return false - }) - - registeredMTs.Iterate("", "", func(key string, value interface{}) bool { - info := value.(GRC1155TokenInfo) - mt := info.Collection() - balance, err := mt.BalanceOf(addr, grc1155.TokenID(info.TokenID)) - if err == nil { - balances.Set("mt:"+key, balance) - } - return false - }) - - sb.WriteString("Balances: ") - balances.Iterate("", "", func(key string, value interface{}) bool { - switch { - case strings.HasPrefix(key, "nft:"): - symbol := strings.TrimPrefix(key, "nft:") - sb.WriteString(ufmt.Sprintf("NFT:%s,", symbol)) - case strings.HasPrefix(key, "token:"): - balance := value.(uint64) - symbol := strings.TrimPrefix(key, "token:") - sb.WriteString(ufmt.Sprintf("Token:%s:%d,", symbol, balance)) - case strings.HasPrefix(key, "mt:"): - balance := value.(uint64) - symbol := strings.TrimPrefix(key, "mt:") - sb.WriteString(ufmt.Sprintf("MultiToken:%s:%d,", - symbol, - balance)) - } - return false - }) - - return sb.String() -} - -func getNonZeroBalances(input string) string { - addr := getAddressForUsername(input) - if addr == "" { - panic("invalid address or username: " + input) - } - - balances := avl.NewTree() - var sb strings.Builder - - registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { - nftGetter := value.(grc721.NFTGetter) - nft := nftGetter() - key_parts := strings.Split(key, ".") - owner, err := nft.OwnerOf(grc721.TokenID(key_parts[len(key_parts)-1])) - if err == nil && addr == owner { // show only the nfts owner owns - balances.Set("nft:"+key, 1) - } - return false - }) - - grc20reg.GetRegistry().Iterate("", "", func(key string, value interface{}) bool { - tokenGetter := value.(grc20.TokenGetter) - token := tokenGetter() - balance := token.BalanceOf(addr) - if balance > 0 { - balances.Set("token:"+key, balance) - } - return false - }) - - registeredMTs.Iterate("", "", func(key string, value interface{}) bool { - info := value.(GRC1155TokenInfo) - mt := info.Collection() - balance, err := mt.BalanceOf(addr, grc1155.TokenID(info.TokenID)) - if err == nil && balance > 0 { - balances.Set("mt:"+key, balance) - } - return false - }) - - sb.WriteString("Non-Zero Balances: ") - balances.Iterate("", "", func(key string, value interface{}) bool { - switch { - case strings.HasPrefix(key, "nft:"): - symbol := strings.TrimPrefix(key, "nft:") - sb.WriteString(ufmt.Sprintf("NFT:%s,", symbol)) - case strings.HasPrefix(key, "token:"): - balance := value.(uint64) - symbol := strings.TrimPrefix(key, "token:") - sb.WriteString(ufmt.Sprintf("Token:%s:%d,", symbol, balance)) - case strings.HasPrefix(key, "mt:"): - balance := value.(uint64) - symbol := strings.TrimPrefix(key, "mt:") - sb.WriteString(ufmt.Sprintf("MultiToken:%s:%d,", - symbol, - balance)) - } - return false - }) - - return sb.String() -} - -func getAddressForUsername(addrOrName string) std.Address { - addr := std.Address(addrOrName) - - if addr.IsValid() { - return addr - } - if user := users.GetUserByName(addrOrName); user != nil { - return user.Address - } - - return "" -} From 9b9767f56b6c4b1eb9bde33ce893c9d4e9ae6c4d Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 21 Jan 2025 19:14:56 +0100 Subject: [PATCH 48/78] optimization so each token types should be gettable separately to reduce gas costs --- .../r/matijamarjanovic/tokenhub/getters.gno | 127 +++++------------- 1 file changed, 36 insertions(+), 91 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno index cddd98825cd..8ac865fd4c6 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno @@ -14,12 +14,16 @@ import ( ) -func GetUserBalancesWithZeros(userNameOrAddress string) string { - return getBalances(userNameOrAddress) +func GetUserTokenBalances(userNameOrAddress string) string { + return getTokenBalances(userNameOrAddress, false) } -func GetUserBalances(userNameOrAddress string) string { - return getNonZeroBalances(userNameOrAddress) +func GetUserNFTBalances(userNameOrAddress string) string { + return getNFTBalances(userNameOrAddress) +} + +func GetUserMultiTokenBalances(userNameOrAddress string) string { + return getMultiTokenBalances(userNameOrAddress, false) } func GetToken(key string) *grc20.Token { @@ -97,15 +101,12 @@ func GetAllRegistered() string { return GetAllNFTs() + GetAllTokens() + GetAllMultiTokens() } - -func getBalances(input string) string { +func getNFTBalances(input string) string { addr := getAddressForUsername(input) - if addr == "" { + if !addr.IsValid() { panic("invalid address or username: " + input) } - - balances := avl.NewTree() - var sb strings.Builder + var out string registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { nftGetter := value.(grc721.NFTGetter) @@ -113,113 +114,57 @@ func getBalances(input string) string { key_parts := strings.Split(key, ".") owner, err := nft.OwnerOf(grc721.TokenID(key_parts[len(key_parts)-1])) if err == nil && addr == owner { // show only the nfts owner owns - balances.Set("nft:"+key, 1) + out += "NFT:" + key + "," } return false }) + return out +} + +func getTokenBalances(input string, nonZero bool) string { + addr := getAddressForUsername(input) + if !addr.IsValid() { + panic("invalid address or username: " + input) + } + var out string grc20reg.GetRegistry().Iterate("", "", func(key string, value interface{}) bool { tokenGetter := value.(grc20.TokenGetter) token := tokenGetter() balance := token.BalanceOf(addr) - balances.Set("token:"+key, balance) - return false - }) - - registeredMTs.Iterate("", "", func(key string, value interface{}) bool { - info := value.(GRC1155TokenInfo) - mt := info.Collection() - balance, err := mt.BalanceOf(addr, grc1155.TokenID(info.TokenID)) - if err == nil { - balances.Set("mt:"+key, balance) + if nonZero && balance > 0 { + out += "Token:" + key + ":" + balance + "," + } else if !nonZero { + out += "Token:" + key + ":" + balance + "," } return false }) - sb.WriteString("Balances: ") - balances.Iterate("", "", func(key string, value interface{}) bool { - switch { - case strings.HasPrefix(key, "nft:"): - symbol := strings.TrimPrefix(key, "nft:") - sb.WriteString(ufmt.Sprintf("NFT:%s,", symbol)) - case strings.HasPrefix(key, "token:"): - balance := value.(uint64) - symbol := strings.TrimPrefix(key, "token:") - sb.WriteString(ufmt.Sprintf("Token:%s:%d,", symbol, balance)) - case strings.HasPrefix(key, "mt:"): - balance := value.(uint64) - symbol := strings.TrimPrefix(key, "mt:") - sb.WriteString(ufmt.Sprintf("MultiToken:%s:%d,", - symbol, - balance)) - } - return false - }) - - return sb.String() + return out } -func getNonZeroBalances(input string) string { +func getMultiTokenBalances(input string, nonZero bool) string { addr := getAddressForUsername(input) - if addr == "" { + if !addr.IsValid() { panic("invalid address or username: " + input) } - - balances := avl.NewTree() - var sb strings.Builder - - registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { - nftGetter := value.(grc721.NFTGetter) - nft := nftGetter() - key_parts := strings.Split(key, ".") - owner, err := nft.OwnerOf(grc721.TokenID(key_parts[len(key_parts)-1])) - if err == nil && addr == owner { // show only the nfts owner owns - balances.Set("nft:"+key, 1) - } - return false - }) - - grc20reg.GetRegistry().Iterate("", "", func(key string, value interface{}) bool { - tokenGetter := value.(grc20.TokenGetter) - token := tokenGetter() - balance := token.BalanceOf(addr) - if balance > 0 { - balances.Set("token:"+key, balance) - } - return false - }) + var out string registeredMTs.Iterate("", "", func(key string, value interface{}) bool { info := value.(GRC1155TokenInfo) mt := info.Collection() balance, err := mt.BalanceOf(addr, grc1155.TokenID(info.TokenID)) - if err == nil && balance > 0 { - balances.Set("mt:"+key, balance) - } - return false - }) - - sb.WriteString("Non-Zero Balances: ") - balances.Iterate("", "", func(key string, value interface{}) bool { - switch { - case strings.HasPrefix(key, "nft:"): - symbol := strings.TrimPrefix(key, "nft:") - sb.WriteString(ufmt.Sprintf("NFT:%s,", symbol)) - case strings.HasPrefix(key, "token:"): - balance := value.(uint64) - symbol := strings.TrimPrefix(key, "token:") - sb.WriteString(ufmt.Sprintf("Token:%s:%d,", symbol, balance)) - case strings.HasPrefix(key, "mt:"): - balance := value.(uint64) - symbol := strings.TrimPrefix(key, "mt:") - sb.WriteString(ufmt.Sprintf("MultiToken:%s:%d,", - symbol, - balance)) + if err == nil { + if nonZero && balance > 0 { + out += "MultiToken:" + key + ":" + balance + "," + } else if !nonZero { + out += "MultiToken:" + key + ":" + balance + "," + } } return false }) - return sb.String() + return out } func getAddressForUsername(addrOrName string) std.Address { From 385fdf90d53182ef89a6150eb7e4f6d6352e12f7 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 21 Jan 2025 19:17:28 +0100 Subject: [PATCH 49/78] add get non zero balances --- examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno index 8ac865fd4c6..ddca48c7aea 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno @@ -18,6 +18,10 @@ func GetUserTokenBalances(userNameOrAddress string) string { return getTokenBalances(userNameOrAddress, false) } +func GetUserTokenBalancesNonZero(userNameOrAddress string) string { + return getTokenBalances(userNameOrAddress, true) +} + func GetUserNFTBalances(userNameOrAddress string) string { return getNFTBalances(userNameOrAddress) } @@ -26,6 +30,10 @@ func GetUserMultiTokenBalances(userNameOrAddress string) string { return getMultiTokenBalances(userNameOrAddress, false) } +func GetUserMultiTokenBalancesNonZero(userNameOrAddress string) string { + return getMultiTokenBalances(userNameOrAddress, true) +} + func GetToken(key string) *grc20.Token { getter := grc20reg.Get(key) return getter() From c9edff9bce43fc184f29afbf46b86391cc95a904 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 21 Jan 2025 19:22:21 +0100 Subject: [PATCH 50/78] add formatting, fix ufmt print --- .../r/matijamarjanovic/tokenhub/getters.gno | 16 +++++------ .../r/matijamarjanovic/tokenhub/render.gno | 28 +++++++++---------- .../r/matijamarjanovic/tokenhub/tokenhub.gno | 10 +++---- 3 files changed, 26 insertions(+), 28 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno index ddca48c7aea..9053453b585 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno @@ -4,14 +4,12 @@ import ( "std" "strings" - "gno.land/p/demo/avl" + "gno.land/p/demo/grc/grc1155" "gno.land/p/demo/grc/grc20" "gno.land/p/demo/grc/grc721" - "gno.land/p/demo/grc/grc1155" - "gno.land/r/demo/grc20reg" "gno.land/p/demo/ufmt" + "gno.land/r/demo/grc20reg" "gno.land/r/demo/users" - ) func GetUserTokenBalances(userNameOrAddress string) string { @@ -89,7 +87,7 @@ func GetAllNFTs() string { func GetAllTokens() string { var out string - grc20reg.GetRegistry().Iterate("", "", func(key string, value interface{}) bool{ + grc20reg.GetRegistry().Iterate("", "", func(key string, value interface{}) bool { out += ufmt.Sprintf("Token:%s,", key) return false }) @@ -141,9 +139,9 @@ func getTokenBalances(input string, nonZero bool) string { token := tokenGetter() balance := token.BalanceOf(addr) if nonZero && balance > 0 { - out += "Token:" + key + ":" + balance + "," + out += ufmt.Sprintf("Token:%s:%d,", key, balance) } else if !nonZero { - out += "Token:" + key + ":" + balance + "," + out += ufmt.Sprintf("Token:%s:%d,", key, balance) } return false }) @@ -164,9 +162,9 @@ func getMultiTokenBalances(input string, nonZero bool) string { balance, err := mt.BalanceOf(addr, grc1155.TokenID(info.TokenID)) if err == nil { if nonZero && balance > 0 { - out += "MultiToken:" + key + ":" + balance + "," + out += ufmt.Sprintf("MultiToken:%s:%d,", key, balance) } else if !nonZero { - out += "MultiToken:" + key + ":" + balance + "," + out += ufmt.Sprintf("MultiToken:%s:%d,", key, balance) } } return false diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno index 3127d9c2d02..43adcdd21f8 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno @@ -2,12 +2,13 @@ package tokenhub import ( "strings" + + "gno.land/p/demo/avl/pager" + "gno.land/p/demo/grc/grc20" + "gno.land/p/demo/grc/grc721" "gno.land/p/demo/ufmt" "gno.land/p/moul/md" "gno.land/r/demo/grc20reg" - "gno.land/p/demo/grc/grc20" - "gno.land/p/demo/grc/grc721" - "gno.land/p/demo/avl/pager" ) func Render(path string) string { @@ -17,13 +18,13 @@ func Render(path string) string { case path == "": out = RenderHome() - case strings.HasPrefix(path, token): + case strings.HasPrefix(path, token): out = RenderToken(path) - case strings.HasPrefix(path, nft): + case strings.HasPrefix(path, nft): out = RenderNFT(path) - case strings.HasPrefix(path, mt): + case strings.HasPrefix(path, mt): out = RenderMT(path) default: @@ -31,7 +32,6 @@ func Render(path string) string { out += md.Paragraph("The requested page does not exist.") out += md.Link("Back to home", "http://localhost:8888/r/matijamarjanovic/tokenhub") } - // navigation footer if not on home page if path != "" { @@ -51,7 +51,7 @@ func RenderHome() string { md.Link("GRC721 NFTs", "http://localhost:8888/r/matijamarjanovic/tokenhub:nfts"), md.Link("GRC1155 Multi-Tokens", "http://localhost:8888/r/matijamarjanovic/tokenhub:mts"), } - out +=md.BulletList(links) + out += md.BulletList(links) return out } @@ -76,11 +76,11 @@ func RenderToken(path string) string { if len(tokenItems) > 0 { out += md.BulletList(tokenItems) out += "\n" - + if picker := page.Picker(); picker != "" { out += md.HorizontalRule() out += md.Paragraph(picker) - }else{ + } else { out += md.HorizontalRule() } } else { @@ -116,11 +116,11 @@ func RenderNFT(path string) string { if len(nftItems) > 0 { out += md.BulletList(nftItems) out += "\n" - + if picker := page.Picker(); picker != "" { out += md.HorizontalRule() out += md.Paragraph(picker) - }else{ + } else { out += md.HorizontalRule() } } else { @@ -150,11 +150,11 @@ func RenderMT(path string) string { if len(mtItems) > 0 { out += md.BulletList(mtItems) out += "\n" - + if picker := page.Picker(); picker != "" { out += md.HorizontalRule() out += md.Paragraph(picker) - }else{ + } else { out += md.HorizontalRule() } } else { diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index 5e4864c7dd8..b632633aa51 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -18,8 +18,8 @@ type GRC1155TokenInfo struct { var ( registeredTokens = avl.NewTree() // rlmPath[.slug] -> grc20.TokenGetter - registeredNFTs = avl.NewTree() // rlmPath[.slug] -> grc721.NFTGetter - registeredMTs = avl.NewTree() // rlmPath[.slug] -> GRC1155TokenInfo + registeredNFTs = avl.NewTree() // rlmPath[.slug] -> grc721.NFTGetter + registeredMTs = avl.NewTree() // rlmPath[.slug] -> GRC1155TokenInfo ErrNFTAlreadyRegistered = errors.New("NFT already registered") ErrNFTNotFound = errors.New("NFT not found") @@ -30,10 +30,10 @@ var ( ErrNFTNotMetadata = errors.New("NFT must implement IGRC721CollectionMetadata") ) -const pageSize = 1 +const pageSize = 1 const token = "token" // grc20 -const nft = "nft" // grc721 -const mt = "mt" // grc1155 +const nft = "nft" // grc721 +const mt = "mt" // grc1155 func RegisterToken(tokenGetter grc20.TokenGetter, slug string) { grc20reg.Register(tokenGetter, slug) From 1c598a364bfa1d9d9cd6da10fd3e8bd4c18aa0dd Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 21 Jan 2025 19:25:15 +0100 Subject: [PATCH 51/78] move errors in separate file for better readability --- .../r/matijamarjanovic/tokenhub/errors.gno | 15 +++++++++++++++ .../r/matijamarjanovic/tokenhub/tokenhub.gno | 9 --------- 2 files changed, 15 insertions(+), 9 deletions(-) create mode 100644 examples/gno.land/r/matijamarjanovic/tokenhub/errors.gno diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/errors.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/errors.gno new file mode 100644 index 00000000000..f4030a06cf7 --- /dev/null +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/errors.gno @@ -0,0 +1,15 @@ +package tokenhub + +import ( + "errors" +) + +var ( + ErrNFTAlreadyRegistered = errors.New("NFT already registered") + ErrNFTNotFound = errors.New("NFT not found") + ErrMTAlreadyRegistered = errors.New("multi-token already registered") + ErrMTNotFound = errors.New("multi-token not found") + ErrMTInfoNotFound = errors.New("multi-token info not found") + ErrNFTtokIDNotExists = errors.New("NFT token ID does not exists") + ErrNFTNotMetadata = errors.New("NFT must implement IGRC721CollectionMetadata") +) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index b632633aa51..0df2cf88865 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -1,7 +1,6 @@ package tokenhub import ( - "errors" "std" "gno.land/p/demo/avl" @@ -20,14 +19,6 @@ var ( registeredTokens = avl.NewTree() // rlmPath[.slug] -> grc20.TokenGetter registeredNFTs = avl.NewTree() // rlmPath[.slug] -> grc721.NFTGetter registeredMTs = avl.NewTree() // rlmPath[.slug] -> GRC1155TokenInfo - - ErrNFTAlreadyRegistered = errors.New("NFT already registered") - ErrNFTNotFound = errors.New("NFT not found") - ErrMTAlreadyRegistered = errors.New("multi-token already registered") - ErrMTNotFound = errors.New("multi-token not found") - ErrMTInfoNotFound = errors.New("multi-token info not found") - ErrNFTtokIDNotExists = errors.New("NFT token ID does not exists") - ErrNFTNotMetadata = errors.New("NFT must implement IGRC721CollectionMetadata") ) const pageSize = 1 From a946cc45f044fb25eaee254285021ea2abc5bf15 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 21 Jan 2025 19:35:03 +0100 Subject: [PATCH 52/78] add documentation --- .../r/matijamarjanovic/tokenhub/getters.gno | 26 +++++++++++++++++-- .../r/matijamarjanovic/tokenhub/tokenhub.gno | 9 ++++--- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno index 9053453b585..826f351c50b 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno @@ -12,36 +12,47 @@ import ( "gno.land/r/demo/users" ) +// GetUserTokenBalances returns a string of all the grc20 tokens the user owns func GetUserTokenBalances(userNameOrAddress string) string { return getTokenBalances(userNameOrAddress, false) } +// GetUserTokenBalancesNonZero returns a string of all the grc20 tokens the user owns, but only the ones that have a balance greater than 0 func GetUserTokenBalancesNonZero(userNameOrAddress string) string { return getTokenBalances(userNameOrAddress, true) } +// GetUserNFTBalances returns a string of all the NFTs the user owns func GetUserNFTBalances(userNameOrAddress string) string { return getNFTBalances(userNameOrAddress) } +// GetUserMultiTokenBalances returns a string of all the multi-tokens the user owns func GetUserMultiTokenBalances(userNameOrAddress string) string { return getMultiTokenBalances(userNameOrAddress, false) } +// GetUserMultiTokenBalancesNonZero returns a string of all the multi-tokens the user owns, but only the ones that have a balance greater than 0 func GetUserMultiTokenBalancesNonZero(userNameOrAddress string) string { return getMultiTokenBalances(userNameOrAddress, true) } +// GetToken returns a token instance for a given key func GetToken(key string) *grc20.Token { getter := grc20reg.Get(key) return getter() } +// MustGetToken returns a token instance for a given key, panics if the token is not found func MustGetToken(key string) *grc20.Token { getter := grc20reg.MustGet(key) + if getter == nil { + panic("unknown token: " + key) + } return getter() } +// GetNFT returns an NFT instance for a given key func GetNFT(key string) grc721.IGRC721 { nftGetter, ok := registeredNFTs.Get(key) if !ok { @@ -51,6 +62,7 @@ func GetNFT(key string) grc721.IGRC721 { return nft } +// MustGetNFT returns an NFT instance for a given key, panics if the NFT is not found func MustGetNFT(key string) grc721.IGRC721 { nftGetter := GetNFT(key) if nftGetter == nil { @@ -59,6 +71,7 @@ func MustGetNFT(key string) grc721.IGRC721 { return nftGetter } +// GetMultiToken returns a multi-token instance for a given key func GetMultiToken(key string) grc1155.IGRC1155 { info, ok := registeredMTs.Get(key) if !ok { @@ -68,6 +81,7 @@ func GetMultiToken(key string) grc1155.IGRC1155 { return mt() } +// MustGetMultiToken returns a multi-token instance for a given key, panics if the multi-token is not found func MustGetMultiToken(key string) grc1155.IGRC1155 { info := GetMultiToken(key) if info == nil { @@ -76,6 +90,7 @@ func MustGetMultiToken(key string) grc1155.IGRC1155 { return info } +// GetAllNFTs returns a string of all the NFTs registered func GetAllNFTs() string { var out string registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { @@ -85,28 +100,32 @@ func GetAllNFTs() string { return out } +// GetAllTokens returns a string of all the tokens registered func GetAllTokens() string { var out string grc20reg.GetRegistry().Iterate("", "", func(key string, value interface{}) bool { - out += ufmt.Sprintf("Token:%s,", key) + out += "Token:" + key + "," return false }) return out } +// GetAllMultiTokens returns a string of all the multi-tokens registered func GetAllMultiTokens() string { var out string registeredMTs.Iterate("", "", func(key string, value interface{}) bool { - out += ufmt.Sprintf("MultiToken:%s,", key) + out += "MultiToken:" + key + "," return false }) return out } +// GetAllRegistered returns a string of all the registered tokens, NFTs and multi-tokens func GetAllRegistered() string { return GetAllNFTs() + GetAllTokens() + GetAllMultiTokens() } +// getNFTBalances returns a string of all the NFTs the user owns func getNFTBalances(input string) string { addr := getAddressForUsername(input) if !addr.IsValid() { @@ -128,6 +147,7 @@ func getNFTBalances(input string) string { return out } +// getTokenBalances returns a string of all the tokens the user owns func getTokenBalances(input string, nonZero bool) string { addr := getAddressForUsername(input) if !addr.IsValid() { @@ -149,6 +169,7 @@ func getTokenBalances(input string, nonZero bool) string { return out } +// getMultiTokenBalances returns a string of all the multi-tokens the user owns func getMultiTokenBalances(input string, nonZero bool) string { addr := getAddressForUsername(input) if !addr.IsValid() { @@ -173,6 +194,7 @@ func getMultiTokenBalances(input string, nonZero bool) string { return out } +// getAddressForUsername returns an address for a given username or address func getAddressForUsername(addrOrName string) std.Address { addr := std.Address(addrOrName) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index 0df2cf88865..3e16f9dc8a8 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -26,11 +26,13 @@ const token = "token" // grc20 const nft = "nft" // grc721 const mt = "mt" // grc1155 +// RegisterToken is a function that uses gno.land/r/demo/grc20reg to register a token +// After doing so, the token hub realm uses grc20reg's registry as a read-only avl.Tree func RegisterToken(tokenGetter grc20.TokenGetter, slug string) { grc20reg.Register(tokenGetter, slug) } -// registering a collection of NFTs +// RegisterNFT is a function that registers an NFT in an avl.Tree func RegisterNFT(nftGetter grc721.NFTGetter, collection string, tokenId string) error { nft := nftGetter() _, ok := nft.(grc721.IGRC721CollectionMetadata) @@ -57,9 +59,8 @@ func RegisterNFT(nftGetter grc721.NFTGetter, collection string, tokenId string) registeredNFTs.Set(key, nftGetter) return nil } - -// for grc1155 is a little bit different, since the one collection can have multiple tokens, this is why don't use slugs but tokenIDs, -// and the collection is saved as a part of the :value in the node +// RegisterMultiToken is a function that registers a multi-token in an avl.Tree +// The avl.Tree value is a struct defined in this realm. It contains not only the getter (like other token types) but also the tokenID func RegisterMultiToken(mtGetter grc1155.MultiTokenGetter, tokenID string) error { rlmPath := std.PrevRealm().PkgPath() From 93075f3483f5dc7428e090e91861c05d9b0bdfb5 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 21 Jan 2025 21:30:46 +0100 Subject: [PATCH 53/78] add working tests --- .../r/matijamarjanovic/tokenhub/getters.gno | 3 +- .../r/matijamarjanovic/tokenhub/tokenhub.gno | 1 + .../tokenhub/tokenhub_test.gno | 46 +++++++++---------- 3 files changed, 24 insertions(+), 26 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno index 826f351c50b..0627937d18e 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno @@ -58,8 +58,7 @@ func GetNFT(key string) grc721.IGRC721 { if !ok { return nil } - nft := nftGetter.(grc721.IGRC721) - return nft + return (nftGetter.(grc721.NFTGetter))() } // MustGetNFT returns an NFT instance for a given key, panics if the NFT is not found diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index 3e16f9dc8a8..128ee427b01 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -59,6 +59,7 @@ func RegisterNFT(nftGetter grc721.NFTGetter, collection string, tokenId string) registeredNFTs.Set(key, nftGetter) return nil } + // RegisterMultiToken is a function that registers a multi-token in an avl.Tree // The avl.Tree value is a struct defined in this realm. It contains not only the getter (like other token types) but also the tokenID func RegisterMultiToken(mtGetter grc1155.MultiTokenGetter, tokenID string) error { diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno index 7a5340cfbcf..d525d4966b8 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno @@ -14,15 +14,13 @@ import ( func TestTokenRegistration(t *testing.T) { std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/home")) - std.TestSetOrigPkgAddr(std.DerivePkgAddr("gno.land/r/matijamarjanovic/home")) token, _ := grc20.NewToken("Test Token", "TEST", 6) RegisterToken(token.Getter(), "test_token") - getter := GetGRC20("gno.land/r/matijamarjanovic/tokenhub.test_token") - urequire.True(t, getter != nil, "Should retrieve registered token") + retrievedToken := GetToken("gno.land/r/matijamarjanovic/tokenhub.test_token") + urequire.True(t, retrievedToken != nil, "Should retrieve registered token") - retrievedToken := getter() uassert.Equal(t, "Test Token", retrievedToken.GetName(), "Token name should match") uassert.Equal(t, "TEST", retrievedToken.GetSymbol(), "Token symbol should match") } @@ -35,32 +33,28 @@ func TestNFTRegistration(t *testing.T) { err := RegisterNFT(nft.Getter(), "test_nft", "1") urequire.NoError(t, err, "Should register NFT without error") - getter := GetGRC721("gno.land/r/matijamarjanovic/home.test_nft.1") - urequire.True(t, getter != nil, "Should retrieve registered NFT") + retrievedNFT := GetNFT("gno.land/r/matijamarjanovic/home.test_nft.1") + urequire.True(t, retrievedNFT != nil, "Should retrieve registered NFT") - retrievedNFT := getter() metadata, ok := retrievedNFT.(grc721.IGRC721CollectionMetadata) urequire.True(t, ok, "NFT should implement IGRC721CollectionMetadata") uassert.Equal(t, "Test NFT", metadata.Name(), "NFT name should match") uassert.Equal(t, "TNFT", metadata.Symbol(), "NFT symbol should match") - - err = RegisterNFT(nft.Getter(), "test_nft", "1") - uassert.True(t, err != nil, "Should not allow duplicate registration") } func TestGRC1155Registration(t *testing.T) { std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/home")) mt := grc1155.NewBasicGRC1155Token("test-uri") - err := RegisterGRC1155Token(mt.Getter(), "1") + err := RegisterMultiToken(mt.Getter(), "1") urequire.NoError(t, err, "Should register multi-token without error") - info, exists := GetGRC1155("gno.land/r/matijamarjanovic/home.1") - uassert.True(t, exists, "Should find registered multi-token") - urequire.True(t, info.Collection != nil, "Should retrieve multi-token collection") - uassert.Equal(t, "1", info.TokenID, "Token ID should match") + multiToken := GetMultiToken("gno.land/r/matijamarjanovic/home.1") + urequire.True(t, multiToken != nil, "Should retrieve multi-token") + _, ok := multiToken.(grc1155.IGRC1155) + urequire.True(t, ok, "Retrieved multi-token should implement IGRC1155") - err = RegisterGRC1155Token(mt.Getter(), "1") + err = RegisterMultiToken(mt.Getter(), "1") uassert.True(t, err != nil, "Should not allow duplicate registration") } @@ -72,15 +66,19 @@ func TestBalanceRetrieval(t *testing.T) { ledger.Mint(std.CurrentRealm().Addr(), 1000) mt := grc1155.NewBasicGRC1155Token("test-uri") - RegisterGRC1155Token(mt.Getter(), "11") + RegisterMultiToken(mt.Getter(), "11") mt.SafeMint(std.CurrentRealm().Addr(), grc1155.TokenID("11"), 5) - balances := GetMyBalancesAll() + balances := GetUserTokenBalances(std.CurrentRealm().Addr().String()) uassert.True(t, strings.Contains(balances, "Token:gno.land/r/matijamarjanovic/tokenhub.test_tokenn:1000"), "Should show correct GRC20 balance") + + balances = GetUserNFTBalances(std.CurrentRealm().Addr().String()) uassert.True(t, strings.Contains(balances, "NFT:gno.land/r/matijamarjanovic/home.test_nft.1"), "Should show correct NFT balance") //already minted in test register + + balances = GetUserMultiTokenBalances(std.CurrentRealm().Addr().String()) uassert.True(t, strings.Contains(balances, "MultiToken:gno.land/r/matijamarjanovic/home.11:5"), "Should show multi-token balance") - nonZeroBalances := GetMyBalances() + nonZeroBalances := GetUserTokenBalancesNonZero(std.CurrentRealm().Addr().String()) uassert.True(t, strings.Contains(nonZeroBalances, "Token:gno.land/r/matijamarjanovic/tokenhub.test_tokenn:1000"), "Should show non-zero GRC20 balance") } @@ -95,10 +93,10 @@ func TestErrorCases(t *testing.T) { uassert.True(t, err != nil, "Should not allow duplicate registration") mt := grc1155.NewBasicGRC1155Token("test-uri") - err = RegisterGRC1155Token(mt.Getter(), "1") + err = RegisterMultiToken(mt.Getter(), "1") uassert.True(t, err != nil, "Should not allow duplicate registration") - err = RegisterGRC1155Token(mt.Getter(), "1") + err = RegisterMultiToken(mt.Getter(), "1") uassert.True(t, err != nil, "Should not allow duplicate registration") } @@ -113,13 +111,13 @@ func TestTokenListingFunctions(t *testing.T) { RegisterNFT(nftToken.Getter(), "listing_nft", "1") multiToken := grc1155.NewBasicGRC1155Token("test-uri") - RegisterGRC1155Token(multiToken.Getter(), "listing_mt") + RegisterMultiToken(multiToken.Getter(), "listing_mt") nftList := GetAllNFTs() uassert.True(t, strings.Contains(nftList, "NFT:gno.land/r/matijamarjanovic/home.listing_nft.1"), "GetAllNFTs should list registered NFT") - grc20List := GetAllGRC20Tokens() + grc20List := GetAllTokens() uassert.True(t, strings.Contains(grc20List, "Token:gno.land/r/matijamarjanovic/tokenhub.listing_token"), "GetAllGRC20Tokens should list registered token") @@ -127,7 +125,7 @@ func TestTokenListingFunctions(t *testing.T) { uassert.True(t, strings.Contains(grc1155List, "MultiToken:gno.land/r/matijamarjanovic/home.listing_mt"), "GetAllMultiTokens should list registered multi-token") - completeList := GetAllTokens() + completeList := GetAllRegistered() uassert.True(t, strings.Contains(completeList, "NFT:gno.land/r/matijamarjanovic/home.listing_nft.1"), "GetAllTokens should list NFTs") uassert.True(t, strings.Contains(completeList, "Token:gno.land/r/matijamarjanovic/tokenhub.listing_token"), From 387ebd9e5fdfe97891b0d92af3fafd5200524c32 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 21 Jan 2025 21:32:11 +0100 Subject: [PATCH 54/78] \n placement --- examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno index 0627937d18e..44a453803ba 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno @@ -196,10 +196,10 @@ func getMultiTokenBalances(input string, nonZero bool) string { // getAddressForUsername returns an address for a given username or address func getAddressForUsername(addrOrName string) std.Address { addr := std.Address(addrOrName) - if addr.IsValid() { return addr } + if user := users.GetUserByName(addrOrName); user != nil { return user.Address } From 7be170b55fbf32ece95e4d94bf5e8bf8a368d16b Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 21 Jan 2025 21:37:28 +0100 Subject: [PATCH 55/78] fix fmt --- examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno index 44a453803ba..69bbdbaa869 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno @@ -199,7 +199,7 @@ func getAddressForUsername(addrOrName string) std.Address { if addr.IsValid() { return addr } - + if user := users.GetUserByName(addrOrName); user != nil { return user.Address } From 1e0f60c4cbcaba2fb6cf32345b8dde30b98334fc Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 21 Jan 2025 21:40:50 +0100 Subject: [PATCH 56/78] fix fmt again --- examples/gno.land/r/demo/grc20reg/grc20reg.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/r/demo/grc20reg/grc20reg.gno b/examples/gno.land/r/demo/grc20reg/grc20reg.gno index a69fad338ae..c7bd50188f2 100644 --- a/examples/gno.land/r/demo/grc20reg/grc20reg.gno +++ b/examples/gno.land/r/demo/grc20reg/grc20reg.gno @@ -77,5 +77,5 @@ func Render(path string) string { const registerEvent = "register" func GetRegistry() *rotree.ReadOnlyTree { - return rotree.Wrap(registry, nil) + return rotree.Wrap(registry, nil) } From 0adea0ec8955b9c33e518ba74423ee89ce7d6264 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Wed, 22 Jan 2025 14:38:01 +0100 Subject: [PATCH 57/78] set page size to 10 --- examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index 128ee427b01..be505a4477e 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -21,7 +21,7 @@ var ( registeredMTs = avl.NewTree() // rlmPath[.slug] -> GRC1155TokenInfo ) -const pageSize = 1 +const pageSize = 10 const token = "token" // grc20 const nft = "nft" // grc721 const mt = "mt" // grc1155 From 13e4992c6a099119b1d2eabbf4eb1a0b72e55548 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Thu, 23 Jan 2025 19:54:47 +0100 Subject: [PATCH 58/78] -move token hub to r/demo -adapt grc20reg to use the right key --- .../gno.land/r/demo/grc20reg/grc20reg.gno | 16 ++ examples/gno.land/r/demo/test/gno.mod | 1 + examples/gno.land/r/demo/test/test.gno | 11 + .../tokenhub/errors.gno | 0 examples/gno.land/r/demo/tokenhub/getters.gno | 208 ++++++++++++++++++ examples/gno.land/r/demo/tokenhub/gno.mod | 1 + .../tokenhub/render.gno | 10 +- .../tokenhub/tokenhub.gno | 7 +- .../tokenhub/tokenhub_test.gno | 10 +- .../r/matijamarjanovic/tokenhub/gno.mod | 1 - 10 files changed, 253 insertions(+), 12 deletions(-) create mode 100644 examples/gno.land/r/demo/test/gno.mod create mode 100644 examples/gno.land/r/demo/test/test.gno rename examples/gno.land/r/{matijamarjanovic => demo}/tokenhub/errors.gno (100%) create mode 100644 examples/gno.land/r/demo/tokenhub/getters.gno create mode 100644 examples/gno.land/r/demo/tokenhub/gno.mod rename examples/gno.land/r/{matijamarjanovic => demo}/tokenhub/render.gno (88%) rename examples/gno.land/r/{matijamarjanovic => demo}/tokenhub/tokenhub.gno (85%) rename examples/gno.land/r/{matijamarjanovic => demo}/tokenhub/tokenhub_test.gno (91%) delete mode 100644 examples/gno.land/r/matijamarjanovic/tokenhub/gno.mod diff --git a/examples/gno.land/r/demo/grc20reg/grc20reg.gno b/examples/gno.land/r/demo/grc20reg/grc20reg.gno index c7bd50188f2..193eca65b3f 100644 --- a/examples/gno.land/r/demo/grc20reg/grc20reg.gno +++ b/examples/gno.land/r/demo/grc20reg/grc20reg.gno @@ -23,6 +23,22 @@ func Register(tokenGetter grc20.TokenGetter, slug string) { ) } +func RegisterWithTokenhub(tokenGetter grc20.TokenGetter, key string) { + prevRealmPath := std.PrevRealm().PkgPath() + if prevRealmPath != "gno.land/r/demo/tokenhub" { + return + } + + registry.Set(key, tokenGetter) + + rlmPath, slug := fqname.Parse(key) + std.Emit( + registerEvent, + "pkgpath", rlmPath, + "slug", slug, + ) +} + func Get(key string) grc20.TokenGetter { tokenGetter, ok := registry.Get(key) if !ok { diff --git a/examples/gno.land/r/demo/test/gno.mod b/examples/gno.land/r/demo/test/gno.mod new file mode 100644 index 00000000000..bb4857b02cd --- /dev/null +++ b/examples/gno.land/r/demo/test/gno.mod @@ -0,0 +1 @@ +module gno.land/r/demo/test diff --git a/examples/gno.land/r/demo/test/test.gno b/examples/gno.land/r/demo/test/test.gno new file mode 100644 index 00000000000..90d5f368d88 --- /dev/null +++ b/examples/gno.land/r/demo/test/test.gno @@ -0,0 +1,11 @@ +package test + +import ( + "gno.land/p/demo/grc/grc20" + "gno.land/r/demo/tokenhub" +) + +func Test() { + token, _ := grc20.NewToken("Test123", "TEST123", 18) + tokenhub.RegisterToken(token.Getter(), "test123") +} diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/errors.gno b/examples/gno.land/r/demo/tokenhub/errors.gno similarity index 100% rename from examples/gno.land/r/matijamarjanovic/tokenhub/errors.gno rename to examples/gno.land/r/demo/tokenhub/errors.gno diff --git a/examples/gno.land/r/demo/tokenhub/getters.gno b/examples/gno.land/r/demo/tokenhub/getters.gno new file mode 100644 index 00000000000..69bbdbaa869 --- /dev/null +++ b/examples/gno.land/r/demo/tokenhub/getters.gno @@ -0,0 +1,208 @@ +package tokenhub + +import ( + "std" + "strings" + + "gno.land/p/demo/grc/grc1155" + "gno.land/p/demo/grc/grc20" + "gno.land/p/demo/grc/grc721" + "gno.land/p/demo/ufmt" + "gno.land/r/demo/grc20reg" + "gno.land/r/demo/users" +) + +// GetUserTokenBalances returns a string of all the grc20 tokens the user owns +func GetUserTokenBalances(userNameOrAddress string) string { + return getTokenBalances(userNameOrAddress, false) +} + +// GetUserTokenBalancesNonZero returns a string of all the grc20 tokens the user owns, but only the ones that have a balance greater than 0 +func GetUserTokenBalancesNonZero(userNameOrAddress string) string { + return getTokenBalances(userNameOrAddress, true) +} + +// GetUserNFTBalances returns a string of all the NFTs the user owns +func GetUserNFTBalances(userNameOrAddress string) string { + return getNFTBalances(userNameOrAddress) +} + +// GetUserMultiTokenBalances returns a string of all the multi-tokens the user owns +func GetUserMultiTokenBalances(userNameOrAddress string) string { + return getMultiTokenBalances(userNameOrAddress, false) +} + +// GetUserMultiTokenBalancesNonZero returns a string of all the multi-tokens the user owns, but only the ones that have a balance greater than 0 +func GetUserMultiTokenBalancesNonZero(userNameOrAddress string) string { + return getMultiTokenBalances(userNameOrAddress, true) +} + +// GetToken returns a token instance for a given key +func GetToken(key string) *grc20.Token { + getter := grc20reg.Get(key) + return getter() +} + +// MustGetToken returns a token instance for a given key, panics if the token is not found +func MustGetToken(key string) *grc20.Token { + getter := grc20reg.MustGet(key) + if getter == nil { + panic("unknown token: " + key) + } + return getter() +} + +// GetNFT returns an NFT instance for a given key +func GetNFT(key string) grc721.IGRC721 { + nftGetter, ok := registeredNFTs.Get(key) + if !ok { + return nil + } + return (nftGetter.(grc721.NFTGetter))() +} + +// MustGetNFT returns an NFT instance for a given key, panics if the NFT is not found +func MustGetNFT(key string) grc721.IGRC721 { + nftGetter := GetNFT(key) + if nftGetter == nil { + panic("unknown NFT: " + key) + } + return nftGetter +} + +// GetMultiToken returns a multi-token instance for a given key +func GetMultiToken(key string) grc1155.IGRC1155 { + info, ok := registeredMTs.Get(key) + if !ok { + return nil + } + mt := info.(GRC1155TokenInfo).Collection + return mt() +} + +// MustGetMultiToken returns a multi-token instance for a given key, panics if the multi-token is not found +func MustGetMultiToken(key string) grc1155.IGRC1155 { + info := GetMultiToken(key) + if info == nil { + panic("unknown multi-token: " + key) + } + return info +} + +// GetAllNFTs returns a string of all the NFTs registered +func GetAllNFTs() string { + var out string + registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { + out += ufmt.Sprintf("NFT:%s,", key) + return false + }) + return out +} + +// GetAllTokens returns a string of all the tokens registered +func GetAllTokens() string { + var out string + grc20reg.GetRegistry().Iterate("", "", func(key string, value interface{}) bool { + out += "Token:" + key + "," + return false + }) + return out +} + +// GetAllMultiTokens returns a string of all the multi-tokens registered +func GetAllMultiTokens() string { + var out string + registeredMTs.Iterate("", "", func(key string, value interface{}) bool { + out += "MultiToken:" + key + "," + return false + }) + return out +} + +// GetAllRegistered returns a string of all the registered tokens, NFTs and multi-tokens +func GetAllRegistered() string { + return GetAllNFTs() + GetAllTokens() + GetAllMultiTokens() +} + +// getNFTBalances returns a string of all the NFTs the user owns +func getNFTBalances(input string) string { + addr := getAddressForUsername(input) + if !addr.IsValid() { + panic("invalid address or username: " + input) + } + var out string + + registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { + nftGetter := value.(grc721.NFTGetter) + nft := nftGetter() + key_parts := strings.Split(key, ".") + owner, err := nft.OwnerOf(grc721.TokenID(key_parts[len(key_parts)-1])) + if err == nil && addr == owner { // show only the nfts owner owns + out += "NFT:" + key + "," + } + return false + }) + + return out +} + +// getTokenBalances returns a string of all the tokens the user owns +func getTokenBalances(input string, nonZero bool) string { + addr := getAddressForUsername(input) + if !addr.IsValid() { + panic("invalid address or username: " + input) + } + var out string + grc20reg.GetRegistry().Iterate("", "", func(key string, value interface{}) bool { + tokenGetter := value.(grc20.TokenGetter) + token := tokenGetter() + balance := token.BalanceOf(addr) + if nonZero && balance > 0 { + out += ufmt.Sprintf("Token:%s:%d,", key, balance) + } else if !nonZero { + out += ufmt.Sprintf("Token:%s:%d,", key, balance) + } + return false + }) + + return out +} + +// getMultiTokenBalances returns a string of all the multi-tokens the user owns +func getMultiTokenBalances(input string, nonZero bool) string { + addr := getAddressForUsername(input) + if !addr.IsValid() { + panic("invalid address or username: " + input) + } + var out string + + registeredMTs.Iterate("", "", func(key string, value interface{}) bool { + info := value.(GRC1155TokenInfo) + mt := info.Collection() + balance, err := mt.BalanceOf(addr, grc1155.TokenID(info.TokenID)) + if err == nil { + if nonZero && balance > 0 { + out += ufmt.Sprintf("MultiToken:%s:%d,", key, balance) + } else if !nonZero { + out += ufmt.Sprintf("MultiToken:%s:%d,", key, balance) + } + } + return false + }) + + return out +} + +// getAddressForUsername returns an address for a given username or address +func getAddressForUsername(addrOrName string) std.Address { + addr := std.Address(addrOrName) + if addr.IsValid() { + return addr + } + + if user := users.GetUserByName(addrOrName); user != nil { + return user.Address + } + + return "" +} diff --git a/examples/gno.land/r/demo/tokenhub/gno.mod b/examples/gno.land/r/demo/tokenhub/gno.mod new file mode 100644 index 00000000000..bec9a924427 --- /dev/null +++ b/examples/gno.land/r/demo/tokenhub/gno.mod @@ -0,0 +1 @@ +module gno.land/r/demo/tokenhub diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno b/examples/gno.land/r/demo/tokenhub/render.gno similarity index 88% rename from examples/gno.land/r/matijamarjanovic/tokenhub/render.gno rename to examples/gno.land/r/demo/tokenhub/render.gno index 43adcdd21f8..9b2109bfa5c 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno +++ b/examples/gno.land/r/demo/tokenhub/render.gno @@ -30,13 +30,13 @@ func Render(path string) string { default: out = md.H1("404 Not Found") out += md.Paragraph("The requested page does not exist.") - out += md.Link("Back to home", "http://localhost:8888/r/matijamarjanovic/tokenhub") + out += md.Link("Back to home", "http://localhost:8888/r/demo/tokenhub") } // navigation footer if not on home page if path != "" { out += "\n" - out += md.Link("Back to home", "http://localhost:8888/r/matijamarjanovic/tokenhub") + out += md.Link("Back to home", "http://localhost:8888/r/demo/tokenhub") } return out @@ -47,9 +47,9 @@ func RenderHome() string { out += md.Paragraph("A central registry for GRC721 NFTs, GRC20 tokens, and GRC1155 multi-tokens on gno.land") links := []string{ - md.Link("GRC20 Tokens", "http://localhost:8888/r/matijamarjanovic/tokenhub:tokens"), - md.Link("GRC721 NFTs", "http://localhost:8888/r/matijamarjanovic/tokenhub:nfts"), - md.Link("GRC1155 Multi-Tokens", "http://localhost:8888/r/matijamarjanovic/tokenhub:mts"), + md.Link("GRC20 Tokens", "http://localhost:8888/r/demo/tokenhub:tokens"), + md.Link("GRC721 NFTs", "http://localhost:8888/r/demo/tokenhub:nfts"), + md.Link("GRC1155 Multi-Tokens", "http://localhost:8888/r/demo/tokenhub:mts"), } out += md.BulletList(links) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/gno.land/r/demo/tokenhub/tokenhub.gno similarity index 85% rename from examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno rename to examples/gno.land/r/demo/tokenhub/tokenhub.gno index be505a4477e..f5bd9196bec 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/demo/tokenhub/tokenhub.gno @@ -4,6 +4,7 @@ import ( "std" "gno.land/p/demo/avl" + "gno.land/p/demo/fqname" "gno.land/p/demo/grc/grc1155" "gno.land/p/demo/grc/grc20" "gno.land/p/demo/grc/grc721" @@ -27,9 +28,13 @@ const nft = "nft" // grc721 const mt = "mt" // grc1155 // RegisterToken is a function that uses gno.land/r/demo/grc20reg to register a token +// It uses the slug to construct a key and then registers the token in the registry +// The logic is the same as in grc20reg, but it's done here so the key path is callers pkgpath and not of this realm // After doing so, the token hub realm uses grc20reg's registry as a read-only avl.Tree func RegisterToken(tokenGetter grc20.TokenGetter, slug string) { - grc20reg.Register(tokenGetter, slug) + rlmPath := std.PrevRealm().PkgPath() + key := fqname.Construct(rlmPath, slug) + grc20reg.RegisterWithTokenhub(tokenGetter, key) } // RegisterNFT is a function that registers an NFT in an avl.Tree diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno b/examples/gno.land/r/demo/tokenhub/tokenhub_test.gno similarity index 91% rename from examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno rename to examples/gno.land/r/demo/tokenhub/tokenhub_test.gno index d525d4966b8..ebb47640fa1 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno +++ b/examples/gno.land/r/demo/tokenhub/tokenhub_test.gno @@ -18,7 +18,7 @@ func TestTokenRegistration(t *testing.T) { token, _ := grc20.NewToken("Test Token", "TEST", 6) RegisterToken(token.Getter(), "test_token") - retrievedToken := GetToken("gno.land/r/matijamarjanovic/tokenhub.test_token") + retrievedToken := GetToken("gno.land/r/demo/tokenhub.test_token") urequire.True(t, retrievedToken != nil, "Should retrieve registered token") uassert.Equal(t, "Test Token", retrievedToken.GetName(), "Token name should match") @@ -70,7 +70,7 @@ func TestBalanceRetrieval(t *testing.T) { mt.SafeMint(std.CurrentRealm().Addr(), grc1155.TokenID("11"), 5) balances := GetUserTokenBalances(std.CurrentRealm().Addr().String()) - uassert.True(t, strings.Contains(balances, "Token:gno.land/r/matijamarjanovic/tokenhub.test_tokenn:1000"), "Should show correct GRC20 balance") + uassert.True(t, strings.Contains(balances, "Token:gno.land/r/demo/tokenhub.test_tokenn:1000"), "Should show correct GRC20 balance") balances = GetUserNFTBalances(std.CurrentRealm().Addr().String()) uassert.True(t, strings.Contains(balances, "NFT:gno.land/r/matijamarjanovic/home.test_nft.1"), "Should show correct NFT balance") //already minted in test register @@ -79,7 +79,7 @@ func TestBalanceRetrieval(t *testing.T) { uassert.True(t, strings.Contains(balances, "MultiToken:gno.land/r/matijamarjanovic/home.11:5"), "Should show multi-token balance") nonZeroBalances := GetUserTokenBalancesNonZero(std.CurrentRealm().Addr().String()) - uassert.True(t, strings.Contains(nonZeroBalances, "Token:gno.land/r/matijamarjanovic/tokenhub.test_tokenn:1000"), "Should show non-zero GRC20 balance") + uassert.True(t, strings.Contains(nonZeroBalances, "Token:gno.land/r/demo/tokenhub.test_tokenn:1000"), "Should show non-zero GRC20 balance") } func TestErrorCases(t *testing.T) { @@ -118,7 +118,7 @@ func TestTokenListingFunctions(t *testing.T) { "GetAllNFTs should list registered NFT") grc20List := GetAllTokens() - uassert.True(t, strings.Contains(grc20List, "Token:gno.land/r/matijamarjanovic/tokenhub.listing_token"), + uassert.True(t, strings.Contains(grc20List, "Token:gno.land/r/demo/tokenhub.listing_token"), "GetAllGRC20Tokens should list registered token") grc1155List := GetAllMultiTokens() @@ -128,7 +128,7 @@ func TestTokenListingFunctions(t *testing.T) { completeList := GetAllRegistered() uassert.True(t, strings.Contains(completeList, "NFT:gno.land/r/matijamarjanovic/home.listing_nft.1"), "GetAllTokens should list NFTs") - uassert.True(t, strings.Contains(completeList, "Token:gno.land/r/matijamarjanovic/tokenhub.listing_token"), + uassert.True(t, strings.Contains(completeList, "Token:gno.land/r/demo/tokenhub.listing_token"), "GetAllTokens should list GRC20 tokens") uassert.True(t, strings.Contains(completeList, "MultiToken:gno.land/r/matijamarjanovic/home.listing_mt"), "GetAllTokens should list multi-tokens") diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/gno.mod b/examples/gno.land/r/matijamarjanovic/tokenhub/gno.mod deleted file mode 100644 index 19deb1b830d..00000000000 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/gno.mod +++ /dev/null @@ -1 +0,0 @@ -module gno.land/r/matijamarjanovic/tokenhub From baeadcb795d898119d0b1ad40dda390fea482f9b Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Thu, 23 Jan 2025 19:55:42 +0100 Subject: [PATCH 59/78] remove files --- examples/gno.land/r/demo/test/gno.mod | 1 - examples/gno.land/r/demo/test/test.gno | 11 - .../r/matijamarjanovic/tokenhub/getters.gno | 208 ------------------ 3 files changed, 220 deletions(-) delete mode 100644 examples/gno.land/r/demo/test/gno.mod delete mode 100644 examples/gno.land/r/demo/test/test.gno delete mode 100644 examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno diff --git a/examples/gno.land/r/demo/test/gno.mod b/examples/gno.land/r/demo/test/gno.mod deleted file mode 100644 index bb4857b02cd..00000000000 --- a/examples/gno.land/r/demo/test/gno.mod +++ /dev/null @@ -1 +0,0 @@ -module gno.land/r/demo/test diff --git a/examples/gno.land/r/demo/test/test.gno b/examples/gno.land/r/demo/test/test.gno deleted file mode 100644 index 90d5f368d88..00000000000 --- a/examples/gno.land/r/demo/test/test.gno +++ /dev/null @@ -1,11 +0,0 @@ -package test - -import ( - "gno.land/p/demo/grc/grc20" - "gno.land/r/demo/tokenhub" -) - -func Test() { - token, _ := grc20.NewToken("Test123", "TEST123", 18) - tokenhub.RegisterToken(token.Getter(), "test123") -} diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno deleted file mode 100644 index 69bbdbaa869..00000000000 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno +++ /dev/null @@ -1,208 +0,0 @@ -package tokenhub - -import ( - "std" - "strings" - - "gno.land/p/demo/grc/grc1155" - "gno.land/p/demo/grc/grc20" - "gno.land/p/demo/grc/grc721" - "gno.land/p/demo/ufmt" - "gno.land/r/demo/grc20reg" - "gno.land/r/demo/users" -) - -// GetUserTokenBalances returns a string of all the grc20 tokens the user owns -func GetUserTokenBalances(userNameOrAddress string) string { - return getTokenBalances(userNameOrAddress, false) -} - -// GetUserTokenBalancesNonZero returns a string of all the grc20 tokens the user owns, but only the ones that have a balance greater than 0 -func GetUserTokenBalancesNonZero(userNameOrAddress string) string { - return getTokenBalances(userNameOrAddress, true) -} - -// GetUserNFTBalances returns a string of all the NFTs the user owns -func GetUserNFTBalances(userNameOrAddress string) string { - return getNFTBalances(userNameOrAddress) -} - -// GetUserMultiTokenBalances returns a string of all the multi-tokens the user owns -func GetUserMultiTokenBalances(userNameOrAddress string) string { - return getMultiTokenBalances(userNameOrAddress, false) -} - -// GetUserMultiTokenBalancesNonZero returns a string of all the multi-tokens the user owns, but only the ones that have a balance greater than 0 -func GetUserMultiTokenBalancesNonZero(userNameOrAddress string) string { - return getMultiTokenBalances(userNameOrAddress, true) -} - -// GetToken returns a token instance for a given key -func GetToken(key string) *grc20.Token { - getter := grc20reg.Get(key) - return getter() -} - -// MustGetToken returns a token instance for a given key, panics if the token is not found -func MustGetToken(key string) *grc20.Token { - getter := grc20reg.MustGet(key) - if getter == nil { - panic("unknown token: " + key) - } - return getter() -} - -// GetNFT returns an NFT instance for a given key -func GetNFT(key string) grc721.IGRC721 { - nftGetter, ok := registeredNFTs.Get(key) - if !ok { - return nil - } - return (nftGetter.(grc721.NFTGetter))() -} - -// MustGetNFT returns an NFT instance for a given key, panics if the NFT is not found -func MustGetNFT(key string) grc721.IGRC721 { - nftGetter := GetNFT(key) - if nftGetter == nil { - panic("unknown NFT: " + key) - } - return nftGetter -} - -// GetMultiToken returns a multi-token instance for a given key -func GetMultiToken(key string) grc1155.IGRC1155 { - info, ok := registeredMTs.Get(key) - if !ok { - return nil - } - mt := info.(GRC1155TokenInfo).Collection - return mt() -} - -// MustGetMultiToken returns a multi-token instance for a given key, panics if the multi-token is not found -func MustGetMultiToken(key string) grc1155.IGRC1155 { - info := GetMultiToken(key) - if info == nil { - panic("unknown multi-token: " + key) - } - return info -} - -// GetAllNFTs returns a string of all the NFTs registered -func GetAllNFTs() string { - var out string - registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { - out += ufmt.Sprintf("NFT:%s,", key) - return false - }) - return out -} - -// GetAllTokens returns a string of all the tokens registered -func GetAllTokens() string { - var out string - grc20reg.GetRegistry().Iterate("", "", func(key string, value interface{}) bool { - out += "Token:" + key + "," - return false - }) - return out -} - -// GetAllMultiTokens returns a string of all the multi-tokens registered -func GetAllMultiTokens() string { - var out string - registeredMTs.Iterate("", "", func(key string, value interface{}) bool { - out += "MultiToken:" + key + "," - return false - }) - return out -} - -// GetAllRegistered returns a string of all the registered tokens, NFTs and multi-tokens -func GetAllRegistered() string { - return GetAllNFTs() + GetAllTokens() + GetAllMultiTokens() -} - -// getNFTBalances returns a string of all the NFTs the user owns -func getNFTBalances(input string) string { - addr := getAddressForUsername(input) - if !addr.IsValid() { - panic("invalid address or username: " + input) - } - var out string - - registeredNFTs.Iterate("", "", func(key string, value interface{}) bool { - nftGetter := value.(grc721.NFTGetter) - nft := nftGetter() - key_parts := strings.Split(key, ".") - owner, err := nft.OwnerOf(grc721.TokenID(key_parts[len(key_parts)-1])) - if err == nil && addr == owner { // show only the nfts owner owns - out += "NFT:" + key + "," - } - return false - }) - - return out -} - -// getTokenBalances returns a string of all the tokens the user owns -func getTokenBalances(input string, nonZero bool) string { - addr := getAddressForUsername(input) - if !addr.IsValid() { - panic("invalid address or username: " + input) - } - var out string - grc20reg.GetRegistry().Iterate("", "", func(key string, value interface{}) bool { - tokenGetter := value.(grc20.TokenGetter) - token := tokenGetter() - balance := token.BalanceOf(addr) - if nonZero && balance > 0 { - out += ufmt.Sprintf("Token:%s:%d,", key, balance) - } else if !nonZero { - out += ufmt.Sprintf("Token:%s:%d,", key, balance) - } - return false - }) - - return out -} - -// getMultiTokenBalances returns a string of all the multi-tokens the user owns -func getMultiTokenBalances(input string, nonZero bool) string { - addr := getAddressForUsername(input) - if !addr.IsValid() { - panic("invalid address or username: " + input) - } - var out string - - registeredMTs.Iterate("", "", func(key string, value interface{}) bool { - info := value.(GRC1155TokenInfo) - mt := info.Collection() - balance, err := mt.BalanceOf(addr, grc1155.TokenID(info.TokenID)) - if err == nil { - if nonZero && balance > 0 { - out += ufmt.Sprintf("MultiToken:%s:%d,", key, balance) - } else if !nonZero { - out += ufmt.Sprintf("MultiToken:%s:%d,", key, balance) - } - } - return false - }) - - return out -} - -// getAddressForUsername returns an address for a given username or address -func getAddressForUsername(addrOrName string) std.Address { - addr := std.Address(addrOrName) - if addr.IsValid() { - return addr - } - - if user := users.GetUserByName(addrOrName); user != nil { - return user.Address - } - - return "" -} From 403efc5c43de89e5415750d7962984ae26a32a61 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Fri, 31 Jan 2025 02:22:17 +0100 Subject: [PATCH 60/78] fix tests --- examples/gno.land/r/demo/tokenhub/tokenhub_test.gno | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/gno.land/r/demo/tokenhub/tokenhub_test.gno b/examples/gno.land/r/demo/tokenhub/tokenhub_test.gno index ebb47640fa1..c0ee5452817 100644 --- a/examples/gno.land/r/demo/tokenhub/tokenhub_test.gno +++ b/examples/gno.land/r/demo/tokenhub/tokenhub_test.gno @@ -18,7 +18,7 @@ func TestTokenRegistration(t *testing.T) { token, _ := grc20.NewToken("Test Token", "TEST", 6) RegisterToken(token.Getter(), "test_token") - retrievedToken := GetToken("gno.land/r/demo/tokenhub.test_token") + retrievedToken := GetToken("gno.land/r/matijamarjanovic/home.test_token") urequire.True(t, retrievedToken != nil, "Should retrieve registered token") uassert.Equal(t, "Test Token", retrievedToken.GetName(), "Token name should match") @@ -70,7 +70,7 @@ func TestBalanceRetrieval(t *testing.T) { mt.SafeMint(std.CurrentRealm().Addr(), grc1155.TokenID("11"), 5) balances := GetUserTokenBalances(std.CurrentRealm().Addr().String()) - uassert.True(t, strings.Contains(balances, "Token:gno.land/r/demo/tokenhub.test_tokenn:1000"), "Should show correct GRC20 balance") + uassert.True(t, strings.Contains(balances, "Token:gno.land/r/matijamarjanovic/home.test_tokenn:1000"), "Should show correct GRC20 balance") balances = GetUserNFTBalances(std.CurrentRealm().Addr().String()) uassert.True(t, strings.Contains(balances, "NFT:gno.land/r/matijamarjanovic/home.test_nft.1"), "Should show correct NFT balance") //already minted in test register @@ -79,7 +79,7 @@ func TestBalanceRetrieval(t *testing.T) { uassert.True(t, strings.Contains(balances, "MultiToken:gno.land/r/matijamarjanovic/home.11:5"), "Should show multi-token balance") nonZeroBalances := GetUserTokenBalancesNonZero(std.CurrentRealm().Addr().String()) - uassert.True(t, strings.Contains(nonZeroBalances, "Token:gno.land/r/demo/tokenhub.test_tokenn:1000"), "Should show non-zero GRC20 balance") + uassert.True(t, strings.Contains(nonZeroBalances, "Token:gno.land/r/matijamarjanovic/home.test_tokenn:1000"), "Should show non-zero GRC20 balance") } func TestErrorCases(t *testing.T) { @@ -118,7 +118,7 @@ func TestTokenListingFunctions(t *testing.T) { "GetAllNFTs should list registered NFT") grc20List := GetAllTokens() - uassert.True(t, strings.Contains(grc20List, "Token:gno.land/r/demo/tokenhub.listing_token"), + uassert.True(t, strings.Contains(grc20List, "Token:gno.land/r/matijamarjanovic/home.listing_token"), "GetAllGRC20Tokens should list registered token") grc1155List := GetAllMultiTokens() @@ -128,7 +128,7 @@ func TestTokenListingFunctions(t *testing.T) { completeList := GetAllRegistered() uassert.True(t, strings.Contains(completeList, "NFT:gno.land/r/matijamarjanovic/home.listing_nft.1"), "GetAllTokens should list NFTs") - uassert.True(t, strings.Contains(completeList, "Token:gno.land/r/demo/tokenhub.listing_token"), + uassert.True(t, strings.Contains(completeList, "Token:gno.land/r/matijamarjanovic/home.listing_token"), "GetAllTokens should list GRC20 tokens") uassert.True(t, strings.Contains(completeList, "MultiToken:gno.land/r/matijamarjanovic/home.listing_mt"), "GetAllTokens should list multi-tokens") From 50305aad6ceb60431969a4c6566d90c5aa021543 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 4 Feb 2025 18:15:44 +0100 Subject: [PATCH 61/78] add hof registration --- examples/gno.land/r/demo/tokenhub/tokenhub.gno | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/examples/gno.land/r/demo/tokenhub/tokenhub.gno b/examples/gno.land/r/demo/tokenhub/tokenhub.gno index f5bd9196bec..5b77886350d 100644 --- a/examples/gno.land/r/demo/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/demo/tokenhub/tokenhub.gno @@ -9,6 +9,7 @@ import ( "gno.land/p/demo/grc/grc20" "gno.land/p/demo/grc/grc721" "gno.land/r/demo/grc20reg" + "gno.land/r/leon/hof" ) type GRC1155TokenInfo struct { @@ -27,6 +28,10 @@ const token = "token" // grc20 const nft = "nft" // grc721 const mt = "mt" // grc1155 +func init() { + hof.Register() +} + // RegisterToken is a function that uses gno.land/r/demo/grc20reg to register a token // It uses the slug to construct a key and then registers the token in the registry // The logic is the same as in grc20reg, but it's done here so the key path is callers pkgpath and not of this realm From 7bcf274882b2c6c77b8205c5fa61ec02ee8f275b Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Thu, 6 Feb 2025 17:02:13 +0100 Subject: [PATCH 62/78] add tests for getters --- .../r/demo/tokenhub/tokenhub_test.gno | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/examples/gno.land/r/demo/tokenhub/tokenhub_test.gno b/examples/gno.land/r/demo/tokenhub/tokenhub_test.gno index c0ee5452817..b34a3d31635 100644 --- a/examples/gno.land/r/demo/tokenhub/tokenhub_test.gno +++ b/examples/gno.land/r/demo/tokenhub/tokenhub_test.gno @@ -133,3 +133,64 @@ func TestTokenListingFunctions(t *testing.T) { uassert.True(t, strings.Contains(completeList, "MultiToken:gno.land/r/matijamarjanovic/home.listing_mt"), "GetAllTokens should list multi-tokens") } + +func TestMustGetFunctions(t *testing.T) { + std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/home")) + + token, _ := grc20.NewToken("Must Token", "MUST", 6) + RegisterToken(token.Getter(), "must_token") + + retrievedToken := MustGetToken("gno.land/r/matijamarjanovic/home.must_token") + uassert.Equal(t, "Must Token", retrievedToken.GetName(), "Token name should match") + + defer func() { + r := recover() + uassert.True(t, r != nil, "MustGetToken should panic for non-existent token") + uassert.True(t, strings.Contains(r.(string), "unknown token"), "Panic message should mention unknown token") + }() + MustGetToken("non_existent_token") + + nft := grc721.NewBasicNFT("Must NFT", "MNFT") + nft.Mint(std.CurrentRealm().Addr(), grc721.TokenID("1")) + RegisterNFT(nft.Getter(), "must_nft", "1") + + retrievedNFT := MustGetNFT("gno.land/r/matijamarjanovic/home.must_nft.1") + metadata, ok := retrievedNFT.(grc721.IGRC721CollectionMetadata) + urequire.True(t, ok, "NFT should implement IGRC721CollectionMetadata") + uassert.Equal(t, "Must NFT", metadata.Name(), "NFT name should match") + + defer func() { + r := recover() + uassert.True(t, r != nil, "MustGetNFT should panic for non-existent NFT") + uassert.True(t, strings.Contains(r.(string), "unknown NFT"), "Panic message should mention unknown NFT") + }() + MustGetNFT("non_existent_nft") + + mt := grc1155.NewBasicGRC1155Token("must-uri") + RegisterMultiToken(mt.Getter(), "must_mt") + + retrievedMT := MustGetMultiToken("gno.land/r/matijamarjanovic/home.must_mt") + _, ok = retrievedMT.(grc1155.IGRC1155) + urequire.True(t, ok, "Retrieved multi-token should implement IGRC1155") + + defer func() { + r := recover() + uassert.True(t, r != nil, "MustGetMultiToken should panic for non-existent multi-token") + uassert.True(t, strings.Contains(r.(string), "unknown multi-token"), "Panic message should mention unknown multi-token") + }() + MustGetMultiToken("non_existent_mt") +} + +func TestGetAddressForUsername(t *testing.T) { + std.TestSetRealm(std.NewCodeRealm("gno.land/r/matijamarjanovic/home")) + + validAddr := "g1ej0qca5ptsw9kfr64ey8jvfy9eacga6mpj2z0y" + addr := getAddressForUsername(validAddr) + uassert.Equal(t, validAddr, addr.String(), "Should return same address for valid address input") + + invalidInput := "invalid_input" + addr = getAddressForUsername(invalidInput) + uassert.Equal(t, "", addr.String(), "Should return empty address for invalid input") +} + + From ecd1b522a3327255c3d27774356f9fa70706b54d Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Thu, 6 Feb 2025 17:05:32 +0100 Subject: [PATCH 63/78] fix fmt --- examples/gno.land/r/demo/tokenhub/tokenhub_test.gno | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/gno.land/r/demo/tokenhub/tokenhub_test.gno b/examples/gno.land/r/demo/tokenhub/tokenhub_test.gno index b34a3d31635..83454e7eaf9 100644 --- a/examples/gno.land/r/demo/tokenhub/tokenhub_test.gno +++ b/examples/gno.land/r/demo/tokenhub/tokenhub_test.gno @@ -139,7 +139,7 @@ func TestMustGetFunctions(t *testing.T) { token, _ := grc20.NewToken("Must Token", "MUST", 6) RegisterToken(token.Getter(), "must_token") - + retrievedToken := MustGetToken("gno.land/r/matijamarjanovic/home.must_token") uassert.Equal(t, "Must Token", retrievedToken.GetName(), "Token name should match") @@ -192,5 +192,3 @@ func TestGetAddressForUsername(t *testing.T) { addr = getAddressForUsername(invalidInput) uassert.Equal(t, "", addr.String(), "Should return empty address for invalid input") } - - From 0db4ec89832c680af165b076a31b865ae4ba5a55 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Thu, 6 Feb 2025 18:23:25 +0100 Subject: [PATCH 64/78] try and fix tsts --- examples/gno.land/r/gnoland/home/home_filetest.gno | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/gno.land/r/gnoland/home/home_filetest.gno b/examples/gno.land/r/gnoland/home/home_filetest.gno index 5b5ff5740c3..0f54900de37 100644 --- a/examples/gno.land/r/gnoland/home/home_filetest.gno +++ b/examples/gno.land/r/gnoland/home/home_filetest.gno @@ -79,6 +79,7 @@ func main() { // // ## [Hall of Fame](/r/leon/hof) // +// - [gno.land/r/demo/tokenhub](/r/demo/tokenhub) // // // From 13d327c6b6aba9d1f480f297be3602ba97a53a33 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 11 Feb 2025 23:31:58 +0100 Subject: [PATCH 65/78] don't export renders. move consts to render.gno --- examples/gno.land/r/demo/tokenhub/render.gno | 22 ++++++++++++------- .../gno.land/r/demo/tokenhub/tokenhub.gno | 3 --- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/examples/gno.land/r/demo/tokenhub/render.gno b/examples/gno.land/r/demo/tokenhub/render.gno index 9b2109bfa5c..6d81d374d43 100644 --- a/examples/gno.land/r/demo/tokenhub/render.gno +++ b/examples/gno.land/r/demo/tokenhub/render.gno @@ -11,21 +11,27 @@ import ( "gno.land/r/demo/grc20reg" ) +const ( + token = "token" // grc20 + nft = "nft" // grc721 + mt = "mt" // grc1155 +) + func Render(path string) string { var out string switch { case path == "": - out = RenderHome() + out = renderHome() case strings.HasPrefix(path, token): - out = RenderToken(path) + out = renderToken(path) case strings.HasPrefix(path, nft): - out = RenderNFT(path) + out = renderNFT(path) case strings.HasPrefix(path, mt): - out = RenderMT(path) + out = renderMT(path) default: out = md.H1("404 Not Found") @@ -42,7 +48,7 @@ func Render(path string) string { return out } -func RenderHome() string { +func renderHome() string { out := md.H1("Token Hub") out += md.Paragraph("A central registry for GRC721 NFTs, GRC20 tokens, and GRC1155 multi-tokens on gno.land") @@ -56,7 +62,7 @@ func RenderHome() string { return out } -func RenderToken(path string) string { +func renderToken(path string) string { out := md.H1("GRC20 Tokens") var tokenItems []string @@ -91,7 +97,7 @@ func RenderToken(path string) string { return out } -func RenderNFT(path string) string { +func renderNFT(path string) string { out := md.H1("GRC721 NFTs") var nftItems []string @@ -131,7 +137,7 @@ func RenderNFT(path string) string { return out } -func RenderMT(path string) string { +func renderMT(path string) string { out := md.H1("GRC1155 Multi-Tokens") var mtItems []string diff --git a/examples/gno.land/r/demo/tokenhub/tokenhub.gno b/examples/gno.land/r/demo/tokenhub/tokenhub.gno index 5b77886350d..c11627a010a 100644 --- a/examples/gno.land/r/demo/tokenhub/tokenhub.gno +++ b/examples/gno.land/r/demo/tokenhub/tokenhub.gno @@ -24,9 +24,6 @@ var ( ) const pageSize = 10 -const token = "token" // grc20 -const nft = "nft" // grc721 -const mt = "mt" // grc1155 func init() { hof.Register() From b9580c804b60e2f1035390149074c3dfdbea078c Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 11 Feb 2025 23:37:23 +0100 Subject: [PATCH 66/78] add renderFooter --- examples/gno.land/r/demo/tokenhub/render.gno | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/examples/gno.land/r/demo/tokenhub/render.gno b/examples/gno.land/r/demo/tokenhub/render.gno index 6d81d374d43..c0b3bb18ae4 100644 --- a/examples/gno.land/r/demo/tokenhub/render.gno +++ b/examples/gno.land/r/demo/tokenhub/render.gno @@ -39,12 +39,6 @@ func Render(path string) string { out += md.Link("Back to home", "http://localhost:8888/r/demo/tokenhub") } - // navigation footer if not on home page - if path != "" { - out += "\n" - out += md.Link("Back to home", "http://localhost:8888/r/demo/tokenhub") - } - return out } @@ -93,7 +87,7 @@ func renderToken(path string) string { out += md.Italic("No tokens registered yet") out += "\n" } - + out += renderFooter() return out } @@ -133,7 +127,7 @@ func renderNFT(path string) string { out += md.Italic("No NFTs registered yet") out += "\n" } - + out += renderFooter() return out } @@ -167,6 +161,11 @@ func renderMT(path string) string { out += md.Italic("No multi-tokens registered yet") out += "\n" } - + out += renderFooter() return out } + func renderFooter() string { + out := "\n" + out += md.Link("Back to home", "http://localhost:8888/r/demo/tokenhub") + return out + } From 2a2a02910b4e953f52a313d51cff0dcaeb7377d7 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 11 Feb 2025 23:44:34 +0100 Subject: [PATCH 67/78] fix links --- examples/gno.land/r/demo/tokenhub/render.gno | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/gno.land/r/demo/tokenhub/render.gno b/examples/gno.land/r/demo/tokenhub/render.gno index c0b3bb18ae4..cff81e6ecab 100644 --- a/examples/gno.land/r/demo/tokenhub/render.gno +++ b/examples/gno.land/r/demo/tokenhub/render.gno @@ -36,7 +36,7 @@ func Render(path string) string { default: out = md.H1("404 Not Found") out += md.Paragraph("The requested page does not exist.") - out += md.Link("Back to home", "http://localhost:8888/r/demo/tokenhub") + out += "[Back to home](/r/demo/tokenhub)" } return out @@ -47,9 +47,9 @@ func renderHome() string { out += md.Paragraph("A central registry for GRC721 NFTs, GRC20 tokens, and GRC1155 multi-tokens on gno.land") links := []string{ - md.Link("GRC20 Tokens", "http://localhost:8888/r/demo/tokenhub:tokens"), - md.Link("GRC721 NFTs", "http://localhost:8888/r/demo/tokenhub:nfts"), - md.Link("GRC1155 Multi-Tokens", "http://localhost:8888/r/demo/tokenhub:mts"), + "[GRC20 Tokens](/r/demo/tokenhub:tokens)", + "[GRC721 NFTs](/r/demo/tokenhub:nfts)", + "[GRC1155 Multi-Tokens](/r/demo/tokenhub:mts)", } out += md.BulletList(links) From 9ee19b1124300debd7b97211125e78da0769200e Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 11 Feb 2025 23:47:58 +0100 Subject: [PATCH 68/78] fix formatting, remove new lines --- examples/gno.land/r/demo/tokenhub/render.gno | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/gno.land/r/demo/tokenhub/render.gno b/examples/gno.land/r/demo/tokenhub/render.gno index cff81e6ecab..a6b703eacc2 100644 --- a/examples/gno.land/r/demo/tokenhub/render.gno +++ b/examples/gno.land/r/demo/tokenhub/render.gno @@ -93,10 +93,9 @@ func renderToken(path string) string { func renderNFT(path string) string { out := md.H1("GRC721 NFTs") + var nftItems []string - nftPager := pager.NewPager(registeredNFTs, pageSize, false) - page := nftPager.MustGetPageByPath(path) for _, item := range page.Items { From edd676b9e78661d41211205e34269f0e223e6863 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 11 Feb 2025 23:53:53 +0100 Subject: [PATCH 69/78] simplify logic --- examples/gno.land/r/demo/tokenhub/getters.gno | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/examples/gno.land/r/demo/tokenhub/getters.gno b/examples/gno.land/r/demo/tokenhub/getters.gno index 69bbdbaa869..21e1339e47b 100644 --- a/examples/gno.land/r/demo/tokenhub/getters.gno +++ b/examples/gno.land/r/demo/tokenhub/getters.gno @@ -156,12 +156,10 @@ func getTokenBalances(input string, nonZero bool) string { grc20reg.GetRegistry().Iterate("", "", func(key string, value interface{}) bool { tokenGetter := value.(grc20.TokenGetter) token := tokenGetter() - balance := token.BalanceOf(addr) - if nonZero && balance > 0 { - out += ufmt.Sprintf("Token:%s:%d,", key, balance) - } else if !nonZero { - out += ufmt.Sprintf("Token:%s:%d,", key, balance) - } + balance := token.BalanceOf(addr) + if !nonZero || balance > 0 { + out += ufmt.Sprintf("Token:%s:%d,", key, balance) + } return false }) @@ -181,9 +179,7 @@ func getMultiTokenBalances(input string, nonZero bool) string { mt := info.Collection() balance, err := mt.BalanceOf(addr, grc1155.TokenID(info.TokenID)) if err == nil { - if nonZero && balance > 0 { - out += ufmt.Sprintf("MultiToken:%s:%d,", key, balance) - } else if !nonZero { + if !nonZero || balance > 0 { out += ufmt.Sprintf("MultiToken:%s:%d,", key, balance) } } From 3f32a329f53c1be59787489d1dd2243cc6be38f1 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 11 Feb 2025 23:57:46 +0100 Subject: [PATCH 70/78] fix formatting --- examples/gno.land/r/demo/tokenhub/getters.gno | 8 ++++---- examples/gno.land/r/demo/tokenhub/render.gno | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/gno.land/r/demo/tokenhub/getters.gno b/examples/gno.land/r/demo/tokenhub/getters.gno index 21e1339e47b..268ac818355 100644 --- a/examples/gno.land/r/demo/tokenhub/getters.gno +++ b/examples/gno.land/r/demo/tokenhub/getters.gno @@ -156,10 +156,10 @@ func getTokenBalances(input string, nonZero bool) string { grc20reg.GetRegistry().Iterate("", "", func(key string, value interface{}) bool { tokenGetter := value.(grc20.TokenGetter) token := tokenGetter() - balance := token.BalanceOf(addr) - if !nonZero || balance > 0 { - out += ufmt.Sprintf("Token:%s:%d,", key, balance) - } + balance := token.BalanceOf(addr) + if !nonZero || balance > 0 { + out += ufmt.Sprintf("Token:%s:%d,", key, balance) + } return false }) diff --git a/examples/gno.land/r/demo/tokenhub/render.gno b/examples/gno.land/r/demo/tokenhub/render.gno index a6b703eacc2..6976dbf37ae 100644 --- a/examples/gno.land/r/demo/tokenhub/render.gno +++ b/examples/gno.land/r/demo/tokenhub/render.gno @@ -93,7 +93,7 @@ func renderToken(path string) string { func renderNFT(path string) string { out := md.H1("GRC721 NFTs") - + var nftItems []string nftPager := pager.NewPager(registeredNFTs, pageSize, false) page := nftPager.MustGetPageByPath(path) @@ -163,8 +163,8 @@ func renderMT(path string) string { out += renderFooter() return out } - func renderFooter() string { +func renderFooter() string { out := "\n" out += md.Link("Back to home", "http://localhost:8888/r/demo/tokenhub") return out - } +} From 5fa00c8981274982bd3fdb55916707557325142a Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Tue, 11 Feb 2025 23:58:28 +0100 Subject: [PATCH 71/78] move tokenhub back to my namespace --- .../gno.land/r/{demo => matijamarjanovic}/tokenhub/errors.gno | 0 .../gno.land/r/{demo => matijamarjanovic}/tokenhub/getters.gno | 0 examples/gno.land/r/{demo => matijamarjanovic}/tokenhub/gno.mod | 0 .../gno.land/r/{demo => matijamarjanovic}/tokenhub/render.gno | 0 .../gno.land/r/{demo => matijamarjanovic}/tokenhub/tokenhub.gno | 0 .../r/{demo => matijamarjanovic}/tokenhub/tokenhub_test.gno | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename examples/gno.land/r/{demo => matijamarjanovic}/tokenhub/errors.gno (100%) rename examples/gno.land/r/{demo => matijamarjanovic}/tokenhub/getters.gno (100%) rename examples/gno.land/r/{demo => matijamarjanovic}/tokenhub/gno.mod (100%) rename examples/gno.land/r/{demo => matijamarjanovic}/tokenhub/render.gno (100%) rename examples/gno.land/r/{demo => matijamarjanovic}/tokenhub/tokenhub.gno (100%) rename examples/gno.land/r/{demo => matijamarjanovic}/tokenhub/tokenhub_test.gno (100%) diff --git a/examples/gno.land/r/demo/tokenhub/errors.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/errors.gno similarity index 100% rename from examples/gno.land/r/demo/tokenhub/errors.gno rename to examples/gno.land/r/matijamarjanovic/tokenhub/errors.gno diff --git a/examples/gno.land/r/demo/tokenhub/getters.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno similarity index 100% rename from examples/gno.land/r/demo/tokenhub/getters.gno rename to examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno diff --git a/examples/gno.land/r/demo/tokenhub/gno.mod b/examples/gno.land/r/matijamarjanovic/tokenhub/gno.mod similarity index 100% rename from examples/gno.land/r/demo/tokenhub/gno.mod rename to examples/gno.land/r/matijamarjanovic/tokenhub/gno.mod diff --git a/examples/gno.land/r/demo/tokenhub/render.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno similarity index 100% rename from examples/gno.land/r/demo/tokenhub/render.gno rename to examples/gno.land/r/matijamarjanovic/tokenhub/render.gno diff --git a/examples/gno.land/r/demo/tokenhub/tokenhub.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno similarity index 100% rename from examples/gno.land/r/demo/tokenhub/tokenhub.gno rename to examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno diff --git a/examples/gno.land/r/demo/tokenhub/tokenhub_test.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno similarity index 100% rename from examples/gno.land/r/demo/tokenhub/tokenhub_test.gno rename to examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno From 2675fc06c69e5cf063fb738697760990e56cce9d Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Wed, 12 Feb 2025 00:04:33 +0100 Subject: [PATCH 72/78] fix tests --- examples/gno.land/r/gnoland/home/home_filetest.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/r/gnoland/home/home_filetest.gno b/examples/gno.land/r/gnoland/home/home_filetest.gno index 0f54900de37..7324e884718 100644 --- a/examples/gno.land/r/gnoland/home/home_filetest.gno +++ b/examples/gno.land/r/gnoland/home/home_filetest.gno @@ -79,7 +79,7 @@ func main() { // // ## [Hall of Fame](/r/leon/hof) // -// - [gno.land/r/demo/tokenhub](/r/demo/tokenhub) +// - [gno.land/r/matijamarjanovic/tokenhub](/r/matijamarjanovic/tokenhub) // // // From 5918dd56e161344760d30ca6119c287a79ffb4c5 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Wed, 12 Feb 2025 00:13:27 +0100 Subject: [PATCH 73/78] fix test? --- examples/gno.land/r/gnoland/home/home_filetest.gno | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/gno.land/r/gnoland/home/home_filetest.gno b/examples/gno.land/r/gnoland/home/home_filetest.gno index 7324e884718..b15c1a1a333 100644 --- a/examples/gno.land/r/gnoland/home/home_filetest.gno +++ b/examples/gno.land/r/gnoland/home/home_filetest.gno @@ -79,8 +79,6 @@ func main() { // // ## [Hall of Fame](/r/leon/hof) // -// - [gno.land/r/matijamarjanovic/tokenhub](/r/matijamarjanovic/tokenhub) -// // // // From 7028ad3942308fefe49aac3e66e13deffbf9fbd7 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Wed, 12 Feb 2025 00:22:43 +0100 Subject: [PATCH 74/78] run make test.sync in examples --- examples/gno.land/r/gnoland/home/home_filetest.gno | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/gno.land/r/gnoland/home/home_filetest.gno b/examples/gno.land/r/gnoland/home/home_filetest.gno index b15c1a1a333..5b5ff5740c3 100644 --- a/examples/gno.land/r/gnoland/home/home_filetest.gno +++ b/examples/gno.land/r/gnoland/home/home_filetest.gno @@ -79,6 +79,7 @@ func main() { // // ## [Hall of Fame](/r/leon/hof) // +// // // // From beca66374d5d6c0ed874abf52494dd96aa0e79fd Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Wed, 12 Feb 2025 10:54:41 +0100 Subject: [PATCH 75/78] replace paths for tokenhub --- examples/gno.land/r/demo/grc20reg/grc20reg.gno | 2 +- examples/gno.land/r/matijamarjanovic/tokenhub/gno.mod | 2 +- .../gno.land/r/matijamarjanovic/tokenhub/render.gno | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/gno.land/r/demo/grc20reg/grc20reg.gno b/examples/gno.land/r/demo/grc20reg/grc20reg.gno index 193eca65b3f..70d8987df86 100644 --- a/examples/gno.land/r/demo/grc20reg/grc20reg.gno +++ b/examples/gno.land/r/demo/grc20reg/grc20reg.gno @@ -25,7 +25,7 @@ func Register(tokenGetter grc20.TokenGetter, slug string) { func RegisterWithTokenhub(tokenGetter grc20.TokenGetter, key string) { prevRealmPath := std.PrevRealm().PkgPath() - if prevRealmPath != "gno.land/r/demo/tokenhub" { + if prevRealmPath != "gno.land/r/matijamajranovic/tokenhub" { return } diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/gno.mod b/examples/gno.land/r/matijamarjanovic/tokenhub/gno.mod index bec9a924427..434d0a9bdb1 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/gno.mod +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/gno.mod @@ -1 +1 @@ -module gno.land/r/demo/tokenhub +module gno.land/r/matijamajranovic/tokenhub diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno index 6976dbf37ae..d76e3f4f41f 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno @@ -36,7 +36,7 @@ func Render(path string) string { default: out = md.H1("404 Not Found") out += md.Paragraph("The requested page does not exist.") - out += "[Back to home](/r/demo/tokenhub)" + out += "[Back to home](/r/matijamajranovic/tokenhub)" } return out @@ -47,9 +47,9 @@ func renderHome() string { out += md.Paragraph("A central registry for GRC721 NFTs, GRC20 tokens, and GRC1155 multi-tokens on gno.land") links := []string{ - "[GRC20 Tokens](/r/demo/tokenhub:tokens)", - "[GRC721 NFTs](/r/demo/tokenhub:nfts)", - "[GRC1155 Multi-Tokens](/r/demo/tokenhub:mts)", + "[GRC20 Tokens](/r/matijamajranovic/tokenhub:tokens)", + "[GRC721 NFTs](/r/matijamajranovic/tokenhub:nfts)", + "[GRC1155 Multi-Tokens](/r/matijamajranovic/tokenhub:mts)", } out += md.BulletList(links) @@ -165,6 +165,6 @@ func renderMT(path string) string { } func renderFooter() string { out := "\n" - out += md.Link("Back to home", "http://localhost:8888/r/demo/tokenhub") + out += md.Link("Back to home", "http://localhost:8888/r/matijamajranovic/tokenhub") return out } From 997eeeab6b966b26f51a54851e7b67443d12c6b3 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Sat, 15 Feb 2025 17:01:59 +0100 Subject: [PATCH 76/78] add get all tokens with info --- .../gno.land/r/matijamarjanovic/tokenhub/getters.gno | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno index 268ac818355..8e0ddeb7072 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno @@ -119,6 +119,16 @@ func GetAllMultiTokens() string { return out } +// GetAllMultiTokens returns a string of all the multi-tokens registered +func GetAllMultiTokens() string { + var out string + registeredMTs.Iterate("", "", func(key string, value interface{}) bool { + out += "MultiToken:" + key + "," + return false + }) + return out +} + // GetAllRegistered returns a string of all the registered tokens, NFTs and multi-tokens func GetAllRegistered() string { return GetAllNFTs() + GetAllTokens() + GetAllMultiTokens() From 971170e47e13983b00b60126fee421c10c9b4790 Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Sat, 15 Feb 2025 17:08:30 +0100 Subject: [PATCH 77/78] add getalltokenswithdetails --- .../gno.land/r/matijamarjanovic/tokenhub/getters.gno | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno index 8e0ddeb7072..94f78de9802 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno @@ -109,11 +109,13 @@ func GetAllTokens() string { return out } -// GetAllMultiTokens returns a string of all the multi-tokens registered -func GetAllMultiTokens() string { +// GetAllTokenWithDetails returns a string of all the tokens registered with their details +func GetAllTokenWithDetails() string { var out string - registeredMTs.Iterate("", "", func(key string, value interface{}) bool { - out += "MultiToken:" + key + "," + grc20reg.GetRegistry().Iterate("", "", func(key string, value interface{}) bool { + tokenGetter := value.(grc20.TokenGetter) + token := tokenGetter() + out += ufmt.Sprintf("Token:%s,Name:%s,Symbol:%s,Decimals:%d;", key, token.GetName(), token.GetSymbol(), token.GetDecimals()) return false }) return out From 272e065718dec2b5994d67f5f12faa0b0aaae90a Mon Sep 17 00:00:00 2001 From: matijamarjanovic Date: Sat, 15 Feb 2025 18:53:07 +0100 Subject: [PATCH 78/78] replace wrong name --- examples/gno.land/r/demo/grc20reg/grc20reg.gno | 2 +- examples/gno.land/r/matijamarjanovic/tokenhub/gno.mod | 2 +- .../gno.land/r/matijamarjanovic/tokenhub/render.gno | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/gno.land/r/demo/grc20reg/grc20reg.gno b/examples/gno.land/r/demo/grc20reg/grc20reg.gno index 70d8987df86..3795f2de426 100644 --- a/examples/gno.land/r/demo/grc20reg/grc20reg.gno +++ b/examples/gno.land/r/demo/grc20reg/grc20reg.gno @@ -25,7 +25,7 @@ func Register(tokenGetter grc20.TokenGetter, slug string) { func RegisterWithTokenhub(tokenGetter grc20.TokenGetter, key string) { prevRealmPath := std.PrevRealm().PkgPath() - if prevRealmPath != "gno.land/r/matijamajranovic/tokenhub" { + if prevRealmPath != "gno.land/r/matijamarjanovic/tokenhub" { return } diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/gno.mod b/examples/gno.land/r/matijamarjanovic/tokenhub/gno.mod index 434d0a9bdb1..19deb1b830d 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/gno.mod +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/gno.mod @@ -1 +1 @@ -module gno.land/r/matijamajranovic/tokenhub +module gno.land/r/matijamarjanovic/tokenhub diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno b/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno index d76e3f4f41f..a416602df65 100644 --- a/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno +++ b/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno @@ -36,7 +36,7 @@ func Render(path string) string { default: out = md.H1("404 Not Found") out += md.Paragraph("The requested page does not exist.") - out += "[Back to home](/r/matijamajranovic/tokenhub)" + out += "[Back to home](/r/matijamarjanovic/tokenhub)" } return out @@ -47,9 +47,9 @@ func renderHome() string { out += md.Paragraph("A central registry for GRC721 NFTs, GRC20 tokens, and GRC1155 multi-tokens on gno.land") links := []string{ - "[GRC20 Tokens](/r/matijamajranovic/tokenhub:tokens)", - "[GRC721 NFTs](/r/matijamajranovic/tokenhub:nfts)", - "[GRC1155 Multi-Tokens](/r/matijamajranovic/tokenhub:mts)", + "[GRC20 Tokens](/r/matijamarjanovic/tokenhub:tokens)", + "[GRC721 NFTs](/r/matijamarjanovic/tokenhub:nfts)", + "[GRC1155 Multi-Tokens](/r/matijamarjanovic/tokenhub:mts)", } out += md.BulletList(links) @@ -165,6 +165,6 @@ func renderMT(path string) string { } func renderFooter() string { out := "\n" - out += md.Link("Back to home", "http://localhost:8888/r/matijamajranovic/tokenhub") + out += md.Link("Back to home", "http://localhost:8888/r/matijamarjanovic/tokenhub") return out }