Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(examples): Gnofundme #2085

Draft
wants to merge 53 commits into
base: master
Choose a base branch
from
Draft

Conversation

MalekLahbib
Copy link
Contributor

GnoFundMe is a fundraising/crowdfunding platform built with React, TypeScript and Tailwind CSS.

It allows you to connect your Adena Wallet to identify and make actions/transactions.

I created this PR to open a space for discussion/suggestions from anyone that wants to participate and give ideas about this project, the repo of the project with the front is here.

I have this miro where I put my ideas, anyone is welcome to modify and suggest things. Feel free to participate.

Contributors' checklist...
  • Added new tests, or not needed, or not feasible
  • Provided an example (e.g. screenshot) to aid review or the PR is self-explanatory
  • Updated the official documentation or not needed
  • No breaking changes were made, or a BREAKING CHANGE: xxx message was included in the description
  • Added references to related issues and PRs
  • Provided any useful hints for running manual tests
  • Added new benchmarks to generated graphs, if any. More info here.

MalekLahbib and others added 30 commits March 21, 2024 18:23
Copy link

codecov bot commented May 13, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 60.86%. Comparing base (9897b66) to head (5ac1e45).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2085      +/-   ##
==========================================
- Coverage   60.87%   60.86%   -0.01%     
==========================================
  Files         563      563              
  Lines       75193    75193              
==========================================
- Hits        45770    45763       -7     
- Misses      26055    26062       +7     
  Partials     3368     3368              
Flag Coverage Δ
contribs/gnodev 61.46% <ø> (ø)
contribs/gnofaucet 14.46% <ø> (-0.86%) ⬇️
gno.land 67.17% <ø> (ø)
gnovm 65.63% <ø> (ø)
misc/genstd 80.54% <ø> (ø)
misc/logos 20.23% <ø> (ø)
tm2 62.07% <ø> (-0.02%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@MalekLahbib MalekLahbib changed the title feat(example) : Gnofundme feat(examples): Gnofundme May 13, 2024
Copy link
Contributor

@gfant gfant left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left some comments. If you need some help please comment on the comment and we can check it :-)

contributors []*Contributor
}

func NewCampaign(title string, description string, goal uint, begin time.Time, deadline time.Time, owner *User, beneficiary *User) *Campaign {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How will you check if the string variables aren't empty? I think it would be great if you add some conditionals to avoid this issue. Maybe a minimum of 5 chars for the title, 10 for description, a goal of more than 0, etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, I was wondering if I add my checks on the realm (can cost more gas) or in the front (react), is it secure to do it on the front?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you have a safe testing of your front, it should be ok :-)

current: 0,
begin: begin,
deadline: deadline,
owner: owner,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't be the owner the same as the caller? If not, shouldn't we add an extra requirement to avoid people requesting funds in the name of others?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can see here the different types of campaigns I found on the net. So for some campaigns, someone car create a campaign for another person or organism. But I still need to figure out how do some verification on that cases.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with you about campaigns for 3p, but you should have a mechanism to avoid people to abuse about the image of others to exploit a campaign.

examples/gno.land/p/demo/gnofundme/gnofundme.gno Outdated Show resolved Hide resolved
users = avl.NewTree()
}

func NewUser(name string, email string) string {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happen if the variables are empty? Also you can change the input part from
(name string, email string) to (name, email string)
since both are strings (but that's a matter of taste)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing here for verification, the other issue is as we're talking about web3, a user is supposed to be identified only by his address, but I may use profile pkg, who is a work in progress, to identify people. I'll need a bunch of people's opinions to take decision on how to handle this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gfant +1

You should handle the cases where the user fields are empty :)

"time"

"gno.land/p/demo/avl"
pkg "gno.land/p/demo/gnofundme"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this name pkg could be better called to avoid confusion since it's not the only package you're calling. This would help the readability of your code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the advise, I'll modify it before the final version.

// the avl.tree key will be address to simplify the fetching
users.Set(caller.String(), user)

user = pkg.NewUser("user2", "email2", beneficiary)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as previous with the other user

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same.

}

// verify that the benefeciary exists
b, ok := users.Get(beneficiary.String())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will exist because you created it in line 49, but you're doing the order in reverse. You should first check the user exists, and if it doesn't, create a user for that beneficiary. Same as with the owner.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same.

// get the user if he exists
user, ok := users.Get(userAddress.String())
if !ok {
panic("user is not registered")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why panic here but not in line 84? I think it would be better if you add panic in all the possible errors that could happen.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, didn't pay attention. (modified)

parts := strings.Split(path, "/")
switch {
case len(parts) == 2 && parts[0] == "campaign":
return renderCampaign(parts[1])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be a good practice (and a better customer experience) if you first check if that campaign exists before rendering something that couldn't even be an id for a campaign. (Imagine someone puts campaign/foo).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image
do you think what I did is good or not?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, my bad. This is correct then.

var b bytes.Buffer
c := campaign.(*pkg.Campaign)
b.WriteString(
ufmt.Sprintf(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe changing this ufmt.Sprintf into 1 line instead of 3? To avoid a long path of 42 lines doing the same and reduce it into 18 (less than the half)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you mean the new lines after "ufmt.Sprintf(", I understand. It's because I copied a code as it is from another example. But I'll make the modifications.

Copy link
Contributor

@leohhhn leohhhn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, I left a few comments as a preliminary review.

I think especially the NewUser flow is not necessary, see comments below. Let's use this PR to help you make your code better.

Please decide on design decisions, instead of "to be discussed"; do it how you think it should be, this is your project. If something is to be discussed, it can be done during the review process.

Also please remove unused code and commented out code.

"time"
)

// ctype: Campaign type, will be an int
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use iota

return c.display
}

func (c Contributor) String() string {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could also use the p/demo/json package, up to you.

Comment on lines +10 to +32
func TestNewContributor(t *testing.T) {
user := NewUser("User", "", testutils.TestAddress("user"))
amount := int64(500)
display := true
date := time.Unix(time.Now().Unix(), 0)
contributor := NewContributor(user, date, amount, display)

if contributor.GetUser() != user {
t.Errorf("Expected contributor user to be %s, got %s", user, contributor.GetUser())
}

if contributor.Date.IsZero() {
t.Error("Expected contributor date to be non-zero")
}

if contributor.Amount != amount {
t.Errorf("Expected contributor amount to be %d, got %d", amount, contributor.Amount)
}

if contributor.GetDisplay() != display {
t.Errorf("Expected contributor display to be %t, got %t", display, contributor.GetDisplay())
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that we have uassert & urequire, we should use them

Comment on lines +19 to +39
// Campaign type
Ctype int
// Campaign title
Title string
// Campaign description
Description string
// Campaign goal
Goal int64
// Current amount of money raised
Current int64
// Campaign begin date
Begin time.Time
// Campaign deadline
Deadline time.Time
// Campaign owner
Owner *User
// Campaign beneficiary (can be different from the owner)
Beneficiary *User
// Campaign contributors
contributors []*Contributor
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think most of these comments are redundant; ie the name of the variable tells you what it's about. No need to add comments unless there is ambiguity in the name.

Owner: owner,
Beneficiary: beneficiary,
contributors: make([]*Contributor, 0),
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally add newlines between codeblocks

Suggested change
}
}

Comment on lines +300 to +319
if len(c.GetContributors()) != 0 {
contributors := c.GetContributors()
for _, contributor := range contributors {
if contributor.GetDisplay() {
b.WriteString(
ufmt.Sprintf(
"### %s: %dugnot, %s\n", contributor.GetUser().GetName(), contributor.Amount, contributor.Date.String(),
), // added the date of contribution in the display, but adds "m=+1726419880.000000001" after the date =>> the problems comes from time.now(), had to modify and use time.Unix(time.Now().Unix(), 0) to solve the problem.
)
} else {
b.WriteString(
ufmt.Sprintf(
"### Anonymous: %dugnot\n", contributor.Amount,
),
)
}
}
}
return b.String()
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if there is a million contributors? I would limit this to the latest 5 - 10, with the option to fetch more somehow.

Comment on lines +20 to +34
banker := std.GetBanker(std.BankerTypeRealmSend)
mainbal := banker.GetCoins(mainaddr)
ctype := 0
title := "Campaign for Withdrawal"
description := "Description for Withdrawal"
goal := int64(1000)
begin := time.Now()
deadline := begin.Add(time.Second * 1)
ownerAddress := testutils.TestAddress("owner")
beneficiaryAddress := testutils.TestAddress("beneficiary")
std.TestSetOrigCaller(beneficiaryAddress)
NewUser("Beneficiary", "")
std.TestSetOrigCaller(ownerAddress)
NewUser("Owner", "")
NewCampaign(ctype, title, description, goal, begin.Unix(), deadline.Unix(), beneficiaryAddress)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can put most of these inside the NewCampaign function. You need to declare them only if you need to use them again (more than once) later

Comment on lines +83 to +93
// Verfiy that the dates are correct
if beginT < time.Now().Unix() {
panic("begin date must be in the future")
}
if deadlineT < time.Now().Unix() {
panic("deadline date must be in the future")
}
if deadlineT < beginT {
panic("deadline date must be after begin date")
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is redundant and can be simplified; you can check these two things:

  • if beginT is in the future
  • if deadlineT is after beginT

With transitivity, if:
now < begin && begin < deadline => now < deadline
Also, begin could be >= now


var owner, beneficiaryU *GFMpkg.User
// gets the caller address (owner of the campaign)
caller := std.GetOrigCaller()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use std.PrevRealm().Addr()

// banker.IssueCoin(std.CurrentRealm().Addr(), "ugnot", 350000000)
}

func NewUser(name string, email string) string {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't this then be Register or similar?

Not sure you need to have this whole user registration flow; you could just use the addresses as the primary point, where the campaign could have the owner address, name, email, etc, as optional.

Would be more simple that way.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🧾 package/realm Tag used for new Realms or Packages.
Projects
Status: In Progress
Status: Triage
Development

Successfully merging this pull request may close these issues.

4 participants