-
Notifications
You must be signed in to change notification settings - Fork 385
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(examples): Add Nemanya's Home Realm to Examples (#3410)
## Overview This home realm allows users to donate GNOT to support Web3 projects, with dynamic content updates, text art, and a sponsor leaderboard. The code is easy to understand and copy, making it a great example. It also serves as an informative introduction to myself for ecosystem users. ## Key Features: - **Text Art Rendering:** - Introduced a feature to display text art in codeblocks, offering a visually appealing and customizable way to present content on the home realm page. - **Content Customization:** - Admin can easily update the contents of the page, including sections like "About Me," "Sponsor Information," and social media links, giving them full control over the realm’s presentation. - **GNOT Donation System for Projects:** - Integrated a GNOT donation system, enabling users to sponsor specific Web3 projects. Donations are displayed on the project pages, showcasing the support of the community. - **Sponsor Tracking and Display:** - Added functionality to track and display the sponsors for each project, allowing donors to see the impact of their contributions. ## Realm appearance ![image](https://github.com/user-attachments/assets/843e94af-0694-475d-aea9-92aeb3b66f4f) ![image](https://github.com/user-attachments/assets/679c0bc4-1797-4da5-980e-1a3718bb538f) Co-authored-by: Nemanya21 <[email protected]>
- Loading branch information
Showing
4 changed files
with
345 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package config | ||
|
||
import ( | ||
"errors" | ||
"std" | ||
) | ||
|
||
var ( | ||
main std.Address | ||
backup std.Address | ||
|
||
ErrInvalidAddr = errors.New("Invalid address") | ||
ErrUnauthorized = errors.New("Unauthorized") | ||
) | ||
|
||
func init() { | ||
main = "g1x9qyf6f34v2g52k4q5smn5tctmj3hl2kj7l2ql" | ||
} | ||
|
||
func Address() std.Address { | ||
return main | ||
} | ||
|
||
func Backup() std.Address { | ||
return backup | ||
} | ||
|
||
func SetAddress(a std.Address) error { | ||
if !a.IsValid() { | ||
return ErrInvalidAddr | ||
} | ||
|
||
if err := checkAuthorized(); err != nil { | ||
return err | ||
} | ||
|
||
main = a | ||
return nil | ||
} | ||
|
||
func SetBackup(a std.Address) error { | ||
if !a.IsValid() { | ||
return ErrInvalidAddr | ||
} | ||
|
||
if err := checkAuthorized(); err != nil { | ||
return err | ||
} | ||
|
||
backup = a | ||
return nil | ||
} | ||
|
||
func checkAuthorized() error { | ||
caller := std.PrevRealm().Addr() | ||
isAuthorized := caller == main || caller == backup | ||
|
||
if !isAuthorized { | ||
return ErrUnauthorized | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module gno.land/r/nemanya/config |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module gno.land/r/nemanya/home |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,280 @@ | ||
package home | ||
|
||
import ( | ||
"std" | ||
"strings" | ||
|
||
"gno.land/p/demo/ufmt" | ||
"gno.land/r/nemanya/config" | ||
) | ||
|
||
type SocialLink struct { | ||
URL string | ||
Text string | ||
} | ||
|
||
type Sponsor struct { | ||
Address std.Address | ||
Amount std.Coins | ||
} | ||
|
||
type Project struct { | ||
Name string | ||
Description string | ||
URL string | ||
ImageURL string | ||
Sponsors map[std.Address]Sponsor | ||
} | ||
|
||
var ( | ||
textArt string | ||
aboutMe string | ||
sponsorInfo string | ||
socialLinks map[string]SocialLink | ||
gnoProjects map[string]Project | ||
otherProjects map[string]Project | ||
totalDonations std.Coins | ||
) | ||
|
||
func init() { | ||
textArt = renderTextArt() | ||
aboutMe = "I am a student of IT at Faculty of Sciences in Novi Sad, Serbia. My background is mainly in web and low-level programming, but since Web3 Bootcamp at Petnica this year I've been actively learning about blockchain and adjacent technologies. I am excited about contributing to the gno.land ecosystem and learning from the community.\n\n" | ||
sponsorInfo = "You can sponsor a project by sending GNOT to this address. Your sponsorship will be displayed on the project page. Thank you for supporting the development of gno.land!\n\n" | ||
|
||
socialLinks = map[string]SocialLink{ | ||
"GitHub": {URL: "https://github.com/Nemanya8", Text: "Explore my repositories and open-source contributions."}, | ||
"LinkedIn": {URL: "https://www.linkedin.com/in/nemanjamatic/", Text: "Connect with me professionally."}, | ||
"Email Me": {URL: "mailto:[email protected]", Text: "Reach out for collaboration or inquiries."}, | ||
} | ||
|
||
gnoProjects = make(map[string]Project) | ||
otherProjects = make(map[string]Project) | ||
|
||
gnoProjects["Liberty Bridge"] = Project{ | ||
Name: "Liberty Bridge", | ||
Description: "Liberty Bridge was my first Web3 project, developed as part of the Web3 Bootcamp at Petnica. This project served as a centralized bridge between Ethereum and gno.land, enabling seamless asset transfers and fostering interoperability between the two ecosystems.\n\n The primary objective of Liberty Bridge was to address the challenges of connecting decentralized networks by implementing a user-friendly solution that simplified the process for users. The project incorporated mechanisms to securely transfer assets between the Ethereum and gno.land blockchains, ensuring efficiency and reliability while maintaining a centralized framework for governance and operations.\n\n Through this project, I gained hands-on knowledge of blockchain interoperability, Web3 protocols, and the intricacies of building solutions that bridge different blockchain ecosystems.\n\n", | ||
URL: "https://gno.land", | ||
ImageURL: "https://github.com/Milosevic02/LibertyBridge/raw/main/lb_banner.png", | ||
Sponsors: make(map[std.Address]Sponsor), | ||
} | ||
|
||
otherProjects["Incognito"] = Project{ | ||
Name: "Incognito", | ||
Description: "Incognito is a Web3 platform built for Ethereum-based chains, designed to connect advertisers with users in a privacy-first and mutually beneficial way. Its modular architecture makes it easily expandable to other blockchains. Developed during the ETH Sofia Hackathon, it was recognized as a winning project for its innovation and impact.\n\n The platform allows advertisers to send personalized ads while sharing a portion of the marketing budget with users. It uses machine learning to match users based on wallet activity, ensuring precise targeting. User emails are stored securely on-chain and never shared, prioritizing privacy and transparency.\n\n With all campaign data stored on-chain, Incognito ensures decentralization and accountability. By rewarding users and empowering advertisers, it sets a new standard for fair and transparent blockchain-based advertising.", | ||
URL: "https://github.com/Milosevic02/Incognito-ETHSofia", | ||
ImageURL: "", | ||
Sponsors: make(map[std.Address]Sponsor), | ||
} | ||
} | ||
|
||
func Render(path string) string { | ||
var sb strings.Builder | ||
sb.WriteString("# Hi, I'm\n") | ||
sb.WriteString(textArt) | ||
sb.WriteString("---\n") | ||
sb.WriteString("## About me\n") | ||
sb.WriteString(aboutMe) | ||
sb.WriteString(sponsorInfo) | ||
sb.WriteString(ufmt.Sprintf("# Total Sponsor Donations: %s\n", totalDonations.String())) | ||
sb.WriteString("---\n") | ||
sb.WriteString(renderProjects(gnoProjects, "Gno Projects")) | ||
sb.WriteString("---\n") | ||
sb.WriteString(renderProjects(otherProjects, "Other Projects")) | ||
sb.WriteString("---\n") | ||
sb.WriteString(renderSocialLinks()) | ||
|
||
return sb.String() | ||
} | ||
|
||
func renderTextArt() string { | ||
var sb strings.Builder | ||
sb.WriteString("```\n") | ||
sb.WriteString(" ___ ___ ___ ___ ___ ___ ___ \n") | ||
sb.WriteString(" /\\__\\ /\\ \\ /\\__\\ /\\ \\ /\\__\\ |\\__\\ /\\ \\ \n") | ||
sb.WriteString(" /::| | /::\\ \\ /::| | /::\\ \\ /::| | |:| | /::\\ \\ \n") | ||
sb.WriteString(" /:|:| | /:/\\:\\ \\ /:|:| | /:/\\:\\ \\ /:|:| | |:| | /:/\\:\\ \\ \n") | ||
sb.WriteString(" /:/|:| |__ /::\\~\\:\\ \\ /:/|:|__|__ /::\\~\\:\\ \\ /:/|:| |__ |:|__|__ /::\\~\\:\\ \\ \n") | ||
sb.WriteString(" /:/ |:| /\\__\\ /:/\\:\\ \\:\\__\\ /:/ |::::\\__\\ /:/\\:\\ \\:\\__\\ /:/ |:| /\\__\\ /::::\\__\\ /:/\\:\\ \\:\\__\\\n") | ||
sb.WriteString(" \\/__|:|/:/ / \\:\\~\\:\\ \\/__/ \\/__/~~/:/ / \\/__\\:\\/:/ / \\/__|:|/:/ / /:/~~/~ \\/__\\:\\/:/ / \n") | ||
sb.WriteString(" |:/:/ / \\:\\ \\:\\__\\ /:/ / \\::/ / |:/:/ / /:/ / \\::/ / \n") | ||
sb.WriteString(" |::/ / \\:\\ \\/__/ /:/ / /:/ / |::/ / \\/__/ /:/ / \n") | ||
sb.WriteString(" /:/ / \\:\\__\\ /:/ / /:/ / /:/ / /:/ / \n") | ||
sb.WriteString(" \\/__/ \\/__/ \\/__/ \\/__/ \\/__/ \\/__/ \n") | ||
sb.WriteString("\n```\n") | ||
return sb.String() | ||
} | ||
|
||
func renderSocialLinks() string { | ||
var sb strings.Builder | ||
sb.WriteString("## Links\n\n") | ||
sb.WriteString("You can find me here:\n\n") | ||
sb.WriteString(ufmt.Sprintf("- [GitHub](%s) - %s\n", socialLinks["GitHub"].URL, socialLinks["GitHub"].Text)) | ||
sb.WriteString(ufmt.Sprintf("- [LinkedIn](%s) - %s\n", socialLinks["LinkedIn"].URL, socialLinks["LinkedIn"].Text)) | ||
sb.WriteString(ufmt.Sprintf("- [Email Me](%s) - %s\n", socialLinks["Email Me"].URL, socialLinks["Email Me"].Text)) | ||
sb.WriteString("\n") | ||
return sb.String() | ||
} | ||
|
||
func renderProjects(projectsMap map[string]Project, title string) string { | ||
var sb strings.Builder | ||
sb.WriteString(ufmt.Sprintf("## %s\n\n", title)) | ||
for _, project := range projectsMap { | ||
if project.ImageURL != "" { | ||
sb.WriteString(ufmt.Sprintf("![%s](%s)\n\n", project.Name, project.ImageURL)) | ||
} | ||
sb.WriteString(ufmt.Sprintf("### [%s](%s)\n\n", project.Name, project.URL)) | ||
sb.WriteString(project.Description + "\n\n") | ||
|
||
if len(project.Sponsors) > 0 { | ||
sb.WriteString(ufmt.Sprintf("#### %s Sponsors\n", project.Name)) | ||
for _, sponsor := range project.Sponsors { | ||
sb.WriteString(ufmt.Sprintf("- %s: %s\n", sponsor.Address.String(), sponsor.Amount.String())) | ||
} | ||
sb.WriteString("\n") | ||
} | ||
} | ||
return sb.String() | ||
} | ||
|
||
func UpdateLink(name, newURL string) { | ||
if !isAuthorized(std.PrevRealm().Addr()) { | ||
panic(config.ErrUnauthorized) | ||
} | ||
|
||
if _, exists := socialLinks[name]; !exists { | ||
panic("Link with the given name does not exist") | ||
} | ||
|
||
socialLinks[name] = SocialLink{ | ||
URL: newURL, | ||
Text: socialLinks[name].Text, | ||
} | ||
} | ||
|
||
func UpdateAboutMe(text string) { | ||
if !isAuthorized(std.PrevRealm().Addr()) { | ||
panic(config.ErrUnauthorized) | ||
} | ||
|
||
aboutMe = text | ||
} | ||
|
||
func AddGnoProject(name, description, url, imageURL string) { | ||
if !isAuthorized(std.PrevRealm().Addr()) { | ||
panic(config.ErrUnauthorized) | ||
} | ||
project := Project{ | ||
Name: name, | ||
Description: description, | ||
URL: url, | ||
ImageURL: imageURL, | ||
Sponsors: make(map[std.Address]Sponsor), | ||
} | ||
gnoProjects[name] = project | ||
} | ||
|
||
func DeleteGnoProject(projectName string) { | ||
if !isAuthorized(std.PrevRealm().Addr()) { | ||
panic(config.ErrUnauthorized) | ||
} | ||
|
||
if _, exists := gnoProjects[projectName]; !exists { | ||
panic("Project not found") | ||
} | ||
|
||
delete(gnoProjects, projectName) | ||
} | ||
|
||
func AddOtherProject(name, description, url, imageURL string) { | ||
if !isAuthorized(std.PrevRealm().Addr()) { | ||
panic(config.ErrUnauthorized) | ||
} | ||
project := Project{ | ||
Name: name, | ||
Description: description, | ||
URL: url, | ||
ImageURL: imageURL, | ||
Sponsors: make(map[std.Address]Sponsor), | ||
} | ||
otherProjects[name] = project | ||
} | ||
|
||
func RemoveOtherProject(projectName string) { | ||
if !isAuthorized(std.PrevRealm().Addr()) { | ||
panic(config.ErrUnauthorized) | ||
} | ||
|
||
if _, exists := otherProjects[projectName]; !exists { | ||
panic("Project not found") | ||
} | ||
|
||
delete(otherProjects, projectName) | ||
} | ||
|
||
func isAuthorized(addr std.Address) bool { | ||
return addr == config.Address() || addr == config.Backup() | ||
} | ||
|
||
func SponsorGnoProject(projectName string) { | ||
address := std.GetOrigCaller() | ||
amount := std.GetOrigSend() | ||
|
||
if amount.AmountOf("ugnot") == 0 { | ||
panic("Donation must include GNOT") | ||
} | ||
|
||
project, exists := gnoProjects[projectName] | ||
if !exists { | ||
panic("Gno project not found") | ||
} | ||
|
||
project.Sponsors[address] = Sponsor{ | ||
Address: address, | ||
Amount: project.Sponsors[address].Amount.Add(amount), | ||
} | ||
|
||
totalDonations = totalDonations.Add(amount) | ||
|
||
gnoProjects[projectName] = project | ||
} | ||
|
||
func SponsorOtherProject(projectName string) { | ||
address := std.GetOrigCaller() | ||
amount := std.GetOrigSend() | ||
|
||
if amount.AmountOf("ugnot") == 0 { | ||
panic("Donation must include GNOT") | ||
} | ||
|
||
project, exists := otherProjects[projectName] | ||
if !exists { | ||
panic("Other project not found") | ||
} | ||
|
||
project.Sponsors[address] = Sponsor{ | ||
Address: address, | ||
Amount: project.Sponsors[address].Amount.Add(amount), | ||
} | ||
|
||
totalDonations = totalDonations.Add(amount) | ||
|
||
otherProjects[projectName] = project | ||
} | ||
|
||
func Withdraw() string { | ||
if !isAuthorized(std.PrevRealm().Addr()) { | ||
panic(config.ErrUnauthorized) | ||
} | ||
|
||
banker := std.GetBanker(std.BankerTypeRealmSend) | ||
realmAddress := std.GetOrigPkgAddr() | ||
coins := banker.GetCoins(realmAddress) | ||
|
||
if len(coins) == 0 { | ||
return "No coins available to withdraw" | ||
} | ||
|
||
banker.SendCoins(realmAddress, config.Address(), coins) | ||
|
||
return "Successfully withdrew all coins to config address" | ||
} |