Skip to content

Commit

Permalink
feat(examples): Add Nemanya's Home Realm to Examples (#3410)
Browse files Browse the repository at this point in the history
## 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
Nemanya8 and Nemanya21 authored Dec 27, 2024
1 parent ea32315 commit 0709984
Show file tree
Hide file tree
Showing 4 changed files with 345 additions and 0 deletions.
63 changes: 63 additions & 0 deletions examples/gno.land/r/nemanya/config/config.gno
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
}
1 change: 1 addition & 0 deletions examples/gno.land/r/nemanya/config/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module gno.land/r/nemanya/config
1 change: 1 addition & 0 deletions examples/gno.land/r/nemanya/home/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module gno.land/r/nemanya/home
280 changes: 280 additions & 0 deletions examples/gno.land/r/nemanya/home/home.gno
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"
}

0 comments on commit 0709984

Please sign in to comment.