Skip to content

Commit

Permalink
feat: add crowdfund/claim command (#252)
Browse files Browse the repository at this point in the history
  • Loading branch information
b00f authored Jan 9, 2025
1 parent 7eeea24 commit e3e6dcf
Show file tree
Hide file tree
Showing 15 changed files with 285 additions and 32 deletions.
4 changes: 4 additions & 0 deletions internal/engine/command/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ type CommandResult struct {
Successful bool
}

func (cmd *Command) RenderInternalFailure() CommandResult {
return cmd.RenderFailedTemplate("An internal error happened. Please try later")
}

func (cmd *Command) RenderFailedTemplateF(reason string, a ...any) CommandResult {
return cmd.RenderFailedTemplate(fmt.Sprintf(reason, a...))
}
Expand Down
59 changes: 55 additions & 4 deletions internal/engine/command/crowdfund/claim.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,63 @@ package crowdfund
import (
"github.com/pagu-project/pagu/internal/engine/command"
"github.com/pagu-project/pagu/internal/entity"
"github.com/pagu-project/pagu/pkg/log"
)

func (*CrowdfundCmd) claimHandler(
_ *entity.User,
func (c *CrowdfundCmd) claimHandler(
user *entity.User,
cmd *command.Command,
_ map[string]string,
args map[string]string,
) command.CommandResult {
return cmd.SuccessfulResult("TODO")
purchases, err := c.db.GetCrowdfundPurchases(user.ID)
if err != nil {
return cmd.RenderErrorTemplate(err)
}

txID := ""
for _, purchase := range purchases {
if purchase.IsClaimed() {
continue
}

isPaid, err := c.nowPayments.IsPaid(purchase.InvoiceID)
if err != nil {
log.Error("nowPayments failed", "error", err, "purchase", purchase)

return cmd.RenderInternalFailure()
}

if isPaid {
address := args[argNameClaimAddress]

txID, err = c.wallet.TransferTransaction(address, "Crowdfund campaign", purchase.PACAmount)
if err != nil {
log.Error("wallet failed", "error", err, "address", address)

return cmd.RenderErrorTemplate(err)
}

purchase.TxHash = txID
purchase.Recipient = address
err = c.db.UpdateCrowdfundPurchase(purchase)
if err != nil {
log.Error("nowPayments failed", "error", err, "purchase", purchase)

return cmd.RenderInternalFailure()
}

// Ensure we log it always
log.Warn("payment successful", "txID", txID, "address", address, "amount", purchase.PACAmount)

break
}
}

if txID == "" {
return cmd.RenderFailedTemplate("No unpaid purchase for this user")
}

txLink := c.wallet.LinkToExplorer(txID)

return cmd.RenderResultTemplate("txLink", txLink)
}
22 changes: 17 additions & 5 deletions internal/engine/command/crowdfund/crowdfund.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions internal/engine/command/crowdfund/crowdfund.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func NewCrowdfundCmd(ctx context.Context,
}

func (c *CrowdfundCmd) activeCampaign() *entity.CrowdfundCampaign {
return c.db.GetActiveCrowdfundCampaign()
return c.db.GetCrowdfundActiveCampaign()
}

func (c *CrowdfundCmd) GetCommand() *command.Command {
Expand Down Expand Up @@ -75,7 +75,7 @@ func (c *CrowdfundCmd) GetCommand() *command.Command {

purchaseChoices = append(purchaseChoices, choice)
}
c.subCmdCreate.Args[0].Choices = purchaseChoices
c.subCmdPurchase.Args[0].Choices = purchaseChoices
}

return cmd
Expand Down
12 changes: 11 additions & 1 deletion internal/engine/command/crowdfund/crowdfund.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ sub_commands:
Packages:
{{range .campaign.Packages}}
- {{.Name}}: {{.USDAmount}} USDT to {{.PACAmount}} PAC
- {{.Name}}: {{.USDAmount}} USDT to {{.PACAmount }}
{{- end}}
- name: purchase
help: Make a purchase in a crowdfunding campaign
Expand All @@ -49,3 +49,13 @@ sub_commands:
Thanks
- name: claim
help: Claim packages from a crowdfunding campaign
args:
- name: address
desc: Set your Pactus address
input_box: Text
optional: false
result_template: |
Thank you for supporting the Pactus blockchain!
You can track your transaction here: {{.txLink}}
If you have any questions or need assistance, feel free to reach out to our community.
6 changes: 3 additions & 3 deletions internal/engine/command/crowdfund/crowdfund_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,17 +71,17 @@ func (td *testData) createTestCampaign(t *testing.T, opts ...CampaignOption) *en
{
Name: td.RandString(16),
USDAmount: td.RandInt(1000),
PACAmount: td.RandInt(1000),
PACAmount: td.RandAmount(),
},
{
Name: td.RandString(16),
USDAmount: td.RandInt(1000),
PACAmount: td.RandInt(1000),
PACAmount: td.RandAmount(),
},
{
Name: td.RandString(16),
USDAmount: td.RandInt(1000),
PACAmount: td.RandInt(1000),
PACAmount: td.RandAmount(),
},
},
}
Expand Down
14 changes: 11 additions & 3 deletions internal/engine/command/crowdfund/purchase.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/pagu-project/pagu/internal/engine/command"
"github.com/pagu-project/pagu/internal/entity"
"github.com/pagu-project/pagu/pkg/log"
)

func (c *CrowdfundCmd) purchaseHandler(
Expand All @@ -32,18 +33,25 @@ func (c *CrowdfundCmd) purchaseHandler(
}
err := c.db.AddCrowdfundPurchase(purchase)
if err != nil {
return cmd.RenderErrorTemplate(err)
log.Error("database failed", "error", err, "purchase", purchase)

return cmd.RenderInternalFailure()
}

orderID := fmt.Sprintf("crowdfund/%d", purchase.ID)
invoiceID, err := c.nowPayments.CreateInvoice(pkg.USDAmount, orderID)
if err != nil {
return cmd.RenderErrorTemplate(err)
log.Error("NowPayments failed", "error", err, "orderID", orderID)

return cmd.RenderInternalFailure()
}

purchase.InvoiceID = invoiceID
err = c.db.UpdateCrowdfundPurchase(purchase)
if err != nil {
return cmd.RenderErrorTemplate(err)
log.Error("database failed", "error", err, "purchase", purchase)

return cmd.RenderInternalFailure()
}

return cmd.RenderResultTemplate(
Expand Down
14 changes: 8 additions & 6 deletions internal/entity/crowdfund.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package entity

import "github.com/pagu-project/pagu/pkg/amount"

type Package struct {
Name string `json:"name"`
USDAmount int `json:"usd_amount"`
PACAmount int `json:"pac_amount"`
Name string `json:"name"`
USDAmount int `json:"usd_amount"`
PACAmount amount.Amount `gorm:"type:double" json:"pac_amount"`
}

type CrowdfundCampaign struct {
Expand All @@ -22,9 +24,9 @@ type CrowdfundPurchase struct {
UserID uint
InvoiceID string
USDAmount int
PACAmount int
TxHash string `gorm:"type:char(64);default:null"`
Recipient string `gorm:"type:char(42);default:null"`
PACAmount amount.Amount `gorm:"type:double"`
TxHash string `gorm:"type:char(64);default:null"`
Recipient string `gorm:"type:char(42);default:null"`
}

func (p *CrowdfundPurchase) IsClaimed() bool {
Expand Down
21 changes: 16 additions & 5 deletions internal/repository/crowdfund.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,10 @@ func (db *Database) AddCrowdfundCampaign(campaign *entity.CrowdfundCampaign) err
}
}

campaign.ID = uint(tx.RowsAffected)

return nil
}

func (db *Database) GetActiveCrowdfundCampaign() *entity.CrowdfundCampaign {
func (db *Database) GetCrowdfundActiveCampaign() *entity.CrowdfundCampaign {
var campaign *entity.CrowdfundCampaign
tx := db.gormDB.Model(&entity.CrowdfundCampaign{}).
Where("active = true").
Expand Down Expand Up @@ -51,8 +49,6 @@ func (db *Database) AddCrowdfundPurchase(purchase *entity.CrowdfundPurchase) err
}
}

purchase.ID = uint(tx.RowsAffected)

return nil
}

Expand All @@ -66,3 +62,18 @@ func (db *Database) UpdateCrowdfundPurchase(purchase *entity.CrowdfundPurchase)

return nil
}

func (db *Database) GetCrowdfundPurchases(userID uint) ([]*entity.CrowdfundPurchase, error) {
var purchases []*entity.CrowdfundPurchase
tx := db.gormDB.Model(&entity.CrowdfundPurchase{}).
Where("user_id = ?", userID).
Find(&purchases)

if tx.Error != nil {
return nil, ReadError{
Message: tx.Error.Error(),
}
}

return purchases, nil
}
27 changes: 27 additions & 0 deletions pkg/amount/amount.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package amount

import (
"database/sql/driver"
"encoding/json"
"errors"
"math"
"strconv"
Expand Down Expand Up @@ -168,6 +169,8 @@ func (a *Amount) Scan(src any) error {
}

// UnmarshalYAML implements the yaml.Unmarshaler interface.
// It parses YAML data into an Amount. It first attempts to unmarshal the data as a string,
// and if that fails, as a float64. Returns an error if conversion fails.
func (a *Amount) UnmarshalYAML(unmarshal func(any) error) error {
var s string
if err := unmarshal(&s); err != nil {
Expand All @@ -182,6 +185,30 @@ func (a *Amount) UnmarshalYAML(unmarshal func(any) error) error {
return nil
}

// UnmarshalJSON implements the json.Unmarshaler interface.
// It parses JSON data into an Amount, first trying as a string, then as a float64.
// Returns an error if the data is invalid or cannot be converted.
func (a *Amount) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
s = string(data)
}

val, err := FromString(s)
if err != nil {
return err
}
*a = val

return nil
}

// MarshalJSON implements the json.Marshaler interface.
// It converts the Amount to a float64 value representing PAC.
func (a Amount) MarshalJSON() ([]byte, error) {
return json.Marshal(a.ToPAC())
}

func (a Amount) ToPactusAmount() amount.Amount {
return amount.Amount(a)
}
Loading

0 comments on commit e3e6dcf

Please sign in to comment.