Skip to content

Commit

Permalink
feat(launchpad-grc20): add sale realm w/ creating and buying mechanism
Browse files Browse the repository at this point in the history
  • Loading branch information
MikaelVallenet committed Sep 5, 2024
1 parent 372ff3d commit 47669d5
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 20 deletions.
19 changes: 3 additions & 16 deletions gno/r/launchpad_grc20/airdrop_grc20.gno
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func NewAirdrop(tokenName, merkleRoot string, amountPerAddr uint64, startTimesta
return airdropID
}

func Redeem(airdropID uint64, proofs []Node) {
func Claim(airdropID uint64, proofs []Node) {
airdrop := mustGetAirdrop(airdropID)
caller := std.PrevRealm().Addr()

Expand Down Expand Up @@ -96,19 +96,6 @@ func (a *Airdrop) hasAlreadyClaimed(caller std.Address) bool {

func (a *Airdrop) isOnGoing() bool {
now := time.Now().Unix()

// if startTimestamp and endTimestamp are both 0, the airdrop is always ongoing
if a.startTimestamp == 0 && a.endTimestamp == 0 {
return true
}
// if startTimestamp is 0, the airdrop is ongoing until endTimestamp
if a.startTimestamp == 0 {
return now < a.endTimestamp
}

// if endTimestamp is 0, the airdrop is ongoing since startTimestamp
if a.endTimestamp == 0 {
return a.startTimestamp <= now
}
return a.startTimestamp <= now && now < a.endTimestamp
return (a.startTimestamp == 0 || a.startTimestamp <= now) &&
(a.endTimestamp == 0 || now < a.endTimestamp)
}
4 changes: 2 additions & 2 deletions gno/r/launchpad_grc20/airdrop_grc20_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"testing"
)

func TestRedeem(t *testing.T) {
func TestClaim(t *testing.T) {
leaves := []Hashable{
Leaf{[]byte("g126gx6p6d3da4ymef35ury6874j6kys044r7zlg")},
Leaf{[]byte("g1ld6uaykyugld4rnm63rcy7vju4zx23lufml3jv")},
Expand All @@ -17,7 +17,7 @@ func TestRedeem(t *testing.T) {
NewToken("MikaelVallenetCoin", "MVC", "https://mikaelvallenet.com", 18, 1000000, 1000000000, true, true)
airdropID := NewAirdrop("MikaelVallenetCoin", root, 100, 0, 0)
std.TestSetOrigCaller(std.Address("g126gx6p6d3da4ymef35ury6874j6kys044r7zlg"))
Redeem(airdropID, proofs)
Claim(airdropID, proofs)
token := mustGetToken("MikaelVallenetCoin")
balance := token.Token().BalanceOf(std.Address("g126gx6p6d3da4ymef35ury6874j6kys044r7zlg"))
if balance != 100 {
Expand Down
2 changes: 1 addition & 1 deletion gno/r/launchpad_grc20/render.gno
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ func renderAirdropDetailPage(res *mux.ResponseWriter, req *mux.Request) {
res.Write(ufmt.Sprintf("### Merkle Root: %s\n", airdrop.merkleRoot))
res.Write(ufmt.Sprintf("### Total addresses claimed: %d\n\n", airdrop.alreadyClaimed.Size()))
res.Write("## Claim Instructions\n")
res.Write("To try to claim your tokens, call the redeem function with the airdrop ID and the proof of your address.\n")
res.Write("To try to claim your tokens, call the claim function with the airdrop ID and the proof of your address.\n")
res.Write("If you are eligible, you will receive the tokens in your wallet.\n")
}

Expand Down
146 changes: 146 additions & 0 deletions gno/r/launchpad_grc20/sale_grc20.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package launchpad_grc20

import (
"std"
"time"

"gno.land/p/demo/avl"
"gno.land/p/demo/ufmt"
)

type Sale struct {
token *Token
startTimestamp int64
endTimestamp int64
pricePerToken uint64
alreadySold uint64
limitPerAddr uint64
minGoal uint64
maxGoal uint64

vault std.Address
}

var (
sales *avl.Tree // sale ID -> sale
nextSaleID uint64
)

func init() {
sales = avl.NewTree()
nextSaleID = 1
}

func NewSale(tokenName string, startTimestamp, endTimestamp int64, pricePerToken, limitPerAddr, minGoal, maxGoal uint64) uint64 {
// check if the caller is the owner of the token
token := mustGetToken(tokenName)
token.admin.AssertCallerIsOwner()

// check that the token is mintable
if !token.allowMint {
panic("token is not mintable")
}

now := time.Now().Unix()
if startTimestamp < now {
panic("start timestamp must be in the future")
}

if startTimestamp >= endTimestamp {
panic("invalid timestamps, start must be before end")
}

if minGoal > maxGoal {
panic("min goal must be less than max goal")
}

if pricePerToken == 0 {
panic("price per token must be greater than 0")
}

sale := Sale{
token: token,
startTimestamp: startTimestamp,
endTimestamp: endTimestamp,
pricePerToken: pricePerToken,
limitPerAddr: limitPerAddr,
minGoal: minGoal,
maxGoal: maxGoal,
}

saleID := nextSaleID
nextSaleID++

sales.Set(ufmt.Sprintf("%d", saleID), &sale)

return saleID
}

func Buy(saleID, amount uint64) {
buyer := std.PrevRealm().Addr()
sale := mustGetSale(saleID)
if !sale.isOnGoing() {
panic("sale is not ongoing")
}

if amount == 0 {
panic("amount must be greater than 0")
}

// TODO: HAVE TO CHECK WHAT THE BUYER ALREADY BOUGHT IN THE SALE
if amount > sale.limitPerAddr {
panic("amount exceeds limit per address")
}

if sale.alreadySold+amount > sale.maxGoal {
panic("amount exceeds max goal of the sale")
}

if sale.token.Token().BalanceOf(buyer) < amount*sale.pricePerToken {
panic("insufficient balance")
}

sale.buy(buyer, amount)
}

func Finalize() {
}

func mustGetSale(saleID uint64) *Sale {
sale, exists := sales.Get(ufmt.Sprintf("%d", saleID))
if !exists {
panic("sale not found")
}
return sale.(*Sale)
}

func (s *Sale) isOnGoing() bool {
return s.startTimestamp <= time.Now().Unix() && (s.endTimestamp == 0 || time.Now().Unix() < s.endTimestamp)
}

func (s *Sale) buy(buyer std.Address, amount uint64) {
if !s.isOnGoing() {
panic("sale is not ongoing")
}

if amount == 0 {
panic("amount must be greater than 0")
}

// TODO: HAVE TO CHECK WHAT THE BUYER ALREADY BOUGHT IN THE SALE
if amount > s.limitPerAddr {
panic("amount exceeds limit per address")
}

if s.alreadySold+amount > s.maxGoal {
panic("amount exceeds max goal of the sale")
}

if s.token.Token().BalanceOf(buyer) < amount*s.pricePerToken {
panic("insufficient balance")
}

checkErr(s.token.Token().TransferFrom(buyer, std.Address("g1ld6uaykyugld4rnm63rcy7vju4zx23lufml3jv"), amount*s.pricePerToken))

}

1 change: 0 additions & 1 deletion gno/r/launchpad_grc20/sales_grc20.gno

This file was deleted.

0 comments on commit 47669d5

Please sign in to comment.