Skip to content

feat: add nowpayments gateway #63

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

Merged
merged 5 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ DISCORD_TOKEN=
DISCORD_GUILD_ID=
TWITTER_BEARER_TOKEN=
TWITTER_ID=
TURBOSWAP_API_TOKEN=
TURBOSWAP_URL=
AUTHORIZED_DISCORD_IDS=
NOWPAYMENTS_LISTEN_PORT=50055
NOWPAYMENTS_WEBHOOK=
NOWPAYMENTS_API_URL=https://api-sandbox.nowpayments.io
NOWPAYMENTS_API_KEY=
NOWPAYMENTS_IPN_SECRET=
NOWPAYMENTS_USERNAME=
NOWPAYMENTS_PASSWORD=
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ mock:
mockgen -source=./wallet/interface.go -destination=./wallet/mock.go -package=wallet
mockgen -source=./store/interface.go -destination=./store/mock.go -package=store
mockgen -source=./twitter_api/interface.go -destination=./twitter_api/mock.go -package=twitter_api
mockgen -source=./turboswap/interface.go -destination=./turboswap/mock.go -package=turboswap
mockgen -source=./nowpayments/interface.go -destination=./nowpayments/mock.go -package=nowpayments

### Formatting, linting, and vetting
fmt:
gofumpt -l -w .
go mod tidy

check:
golangci-lint run --build-tags "${BUILD_TAG}" --timeout=20m0s

Expand Down
37 changes: 19 additions & 18 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,21 @@ import (
"strings"

"github.com/joho/godotenv"
"github.com/kehiy/RoboPac/nowpayments"
"github.com/pactus-project/pactus/util"
)

type Config struct {
Network string
WalletAddress string
WalletPath string
WalletPassword string
NetworkNodes []string
LocalNode string
StorePath string
DiscordBotCfg DiscordBotConfig
TwitterAPICfg TwitterAPIConfig
TurboswapConfig TurboswapConfig
}

type TurboswapConfig struct {
APIToken string
URL string
Network string
WalletAddress string
WalletPath string
WalletPassword string
NetworkNodes []string
LocalNode string
StorePath string
DiscordBotCfg DiscordBotConfig
TwitterAPICfg TwitterAPIConfig
NowPaymentsConfig nowpayments.Config
}

type TwitterAPIConfig struct {
Expand Down Expand Up @@ -60,9 +56,14 @@ func Load(filePaths ...string) (*Config, error) {
BearerToken: os.Getenv("TWITTER_BEARER_TOKEN"),
TwitterID: os.Getenv("TWITTER_ID"),
},
TurboswapConfig: TurboswapConfig{
APIToken: os.Getenv("TURBOSWAP_API_TOKEN"),
URL: os.Getenv("TURBOSWAP_URL"),
NowPaymentsConfig: nowpayments.Config{
ListenPort: os.Getenv("NOWPAYMENTS_LISTEN_PORT"),
Webhook: os.Getenv("NOWPAYMENTS_WEBHOOK"),
APIToken: os.Getenv("NOWPAYMENTS_API_KEY"),
APIUrl: os.Getenv("NOWPAYMENTS_API_URL"),
IPNSecret: os.Getenv("NOWPAYMENTS_IPN_SECRET"),
Username: os.Getenv("NOWPAYMENTS_USERNAME"),
Password: os.Getenv("NOWPAYMENTS_PASSWORD"),
},
}

Expand Down
36 changes: 18 additions & 18 deletions discord/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ var commands = []*discordgo.ApplicationCommand{
Description: "TestNet reward claim status",
},
{
Name: "twitter-campaign",
Description: "Get Twitter campaign discount code",
Name: "booster-payment",
Description: "Create payment link in Validator Booster Program",
Options: []*discordgo.ApplicationCommandOption{
{
Type: discordgo.ApplicationCommandOptionString,
Expand All @@ -118,8 +118,8 @@ var commands = []*discordgo.ApplicationCommand{
},
},
{
Name: "twitter-campaign-status",
Description: "Status of Twitter campaign",
Name: "booster-claim",
Description: "Claim the stake PAC coin in Validator Booster Program",
Options: []*discordgo.ApplicationCommandOption{
{
Type: discordgo.ApplicationCommandOptionString,
Expand All @@ -130,8 +130,8 @@ var commands = []*discordgo.ApplicationCommand{
},
},
{
Name: "twitter-campaign-whitelist",
Description: "Whitelist a non-active Twitter account for Twitter campaign",
Name: "booster-whitelist",
Description: "Whitelist a non-active Twitter account in Validator Booster Program",
Options: []*discordgo.ApplicationCommandOption{
{
Type: discordgo.ApplicationCommandOptionString,
Expand All @@ -144,16 +144,16 @@ var commands = []*discordgo.ApplicationCommand{
}

var commandHandlers = map[string]func(*DiscordBot, *discordgo.Session, *discordgo.InteractionCreate){
"help": helpCommandHandler,
"claim": claimCommandHandler,
"claimer-info": claimerInfoCommandHandler,
"node-info": nodeInfoCommandHandler,
"network-health": networkHealthCommandHandler,
"network-status": networkStatusCommandHandler,
"wallet": walletCommandHandler,
"claim-status": claimStatusCommandHandler,
"reward-calc": rewardCalcCommandHandler,
"twitter-campaign": twitterCampaignCommandHandler,
"twitter-campaign-status": twitterCampaignStatusCommandHandler,
"twitter-campaign-whitelist": twitterCampaignWhitelistCommandHandler,
"help": helpCommandHandler,
"claim": claimCommandHandler,
"claimer-info": claimerInfoCommandHandler,
"node-info": nodeInfoCommandHandler,
"network-health": networkHealthCommandHandler,
"network-status": networkStatusCommandHandler,
"wallet": walletCommandHandler,
"claim-status": claimStatusCommandHandler,
"reward-calc": rewardCalcCommandHandler,
"booster-payment": boosterPaymentCommandHandler,
"booster-claim": boosterClaimCommandHandler,
"booster-whitelist": boosterWhitelistCommandHandler,
}
8 changes: 4 additions & 4 deletions discord/embeds.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ func helpEmbed(s *discordgo.Session) *discordgo.MessageEmbed {
"```/network-status``` Shows a brief info about network.\n" +
"```/network-health``` Check and shows network health status.\n" +
"```/wallet``` Shows RoboPac wallet address and balance.\n" +
"```/twitter-campaign``` Get discount code on Twitter Campaign.\n" +
"```/twitter-campaign-status``` Check the status of Twitter Campaign.\n",
"```/booster-payment``` Create payment link in Validator Booster Program.\n" +
"```/booster-claim``` Claim the stake PAC coin in Validator Booster Program.\n",
Color: PACTUS,
}
}
Expand Down Expand Up @@ -93,9 +93,9 @@ func rewardCalcEmbed(s *discordgo.Session, i *discordgo.InteractionCreate, resul
}
}

func twitterCampaignEmbed(s *discordgo.Session, i *discordgo.InteractionCreate, result string) *discordgo.MessageEmbed {
func boosterEmbed(s *discordgo.Session, i *discordgo.InteractionCreate, result string) *discordgo.MessageEmbed {
return &discordgo.MessageEmbed{
Title: "Twitter(X) Campaign 𝕏",
Title: "Pactus Validator Booster Program ✨",
Description: result,
Color: PACTUS,
}
Expand Down
18 changes: 9 additions & 9 deletions discord/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,54 +182,54 @@ func rewardCalcCommandHandler(db *DiscordBot, s *discordgo.Session, i *discordgo
db.respondEmbed(embed, s, i)
}

func twitterCampaignCommandHandler(db *DiscordBot, s *discordgo.Session, i *discordgo.InteractionCreate) {
func boosterPaymentCommandHandler(db *DiscordBot, s *discordgo.Session, i *discordgo.InteractionCreate) {
if !checkMessage(i, s, db.GuildID, i.Member.User.ID) {
return
}

twitterName := i.ApplicationCommandData().Options[0].StringValue()
valAddr := i.ApplicationCommandData().Options[1].StringValue()

result, err := db.BotEngine.Run(fmt.Sprintf("twitter-campaign %v %v %v", i.Member.User.ID, twitterName, valAddr))
result, err := db.BotEngine.Run(fmt.Sprintf("booster-payment %v %v %v", i.Member.User.ID, twitterName, valAddr))
if err != nil {
db.respondErrMsg(err, s, i)
return
}

embed := twitterCampaignEmbed(s, i, result)
embed := boosterEmbed(s, i, result)
db.respondEmbed(embed, s, i)
}

func twitterCampaignStatusCommandHandler(db *DiscordBot, s *discordgo.Session, i *discordgo.InteractionCreate) {
func boosterClaimCommandHandler(db *DiscordBot, s *discordgo.Session, i *discordgo.InteractionCreate) {
if !checkMessage(i, s, db.GuildID, i.Member.User.ID) {
return
}

twitterID := i.ApplicationCommandData().Options[0].StringValue()

result, err := db.BotEngine.Run(fmt.Sprintf("twitter-campaign-status %v", twitterID))
result, err := db.BotEngine.Run(fmt.Sprintf("booster-payment-claim %v", twitterID))
if err != nil {
db.respondErrMsg(err, s, i)
return
}

embed := twitterCampaignEmbed(s, i, result)
embed := boosterEmbed(s, i, result)
db.respondEmbed(embed, s, i)
}

func twitterCampaignWhitelistCommandHandler(db *DiscordBot, s *discordgo.Session, i *discordgo.InteractionCreate) {
func boosterWhitelistCommandHandler(db *DiscordBot, s *discordgo.Session, i *discordgo.InteractionCreate) {
if !checkMessage(i, s, db.GuildID, i.Member.User.ID) {
return
}

twitterName := i.ApplicationCommandData().Options[0].StringValue()

result, err := db.BotEngine.Run(fmt.Sprintf("twitter-campaign-whitelist %v %v", twitterName, i.Member.User.ID))
result, err := db.BotEngine.Run(fmt.Sprintf("booster-payment-whitelist %v %v", twitterName, i.Member.User.ID))
if err != nil {
db.respondErrMsg(err, s, i)
return
}

embed := twitterCampaignEmbed(s, i, result)
embed := boosterEmbed(s, i, result)
db.respondEmbed(embed, s, i)
}
71 changes: 48 additions & 23 deletions engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,28 @@ import (
"github.com/kehiy/RoboPac/client"
"github.com/kehiy/RoboPac/config"
"github.com/kehiy/RoboPac/log"
"github.com/kehiy/RoboPac/nowpayments"
"github.com/kehiy/RoboPac/store"
"github.com/kehiy/RoboPac/turboswap"
"github.com/kehiy/RoboPac/twitter_api"
"github.com/kehiy/RoboPac/utils"
"github.com/kehiy/RoboPac/wallet"
"github.com/libp2p/go-libp2p/core/peer"
gonanoid "github.com/matoous/go-nanoid/v2"
putils "github.com/pactus-project/pactus/util"
"github.com/pactus-project/pactus/util/logger"
)

var BoosterPrice = 30

type BotEngine struct {
ctx context.Context //nolint
cancel func()

wallet wallet.IWallet
store store.IStore
turboswap turboswap.ITurboSwap
clientMgr *client.Mgr
logger *log.SubLogger
wallet wallet.IWallet
store store.IStore
nowpayments nowpayments.INowpayment
clientMgr *client.Mgr
logger *log.SubLogger

twitterClient twitter_api.IClient

Expand Down Expand Up @@ -88,17 +91,17 @@ func NewBotEngine(cfg *config.Config) (IEngine, error) {
}
log.Info("twitterClient loaded successfully")

turboswap, err := turboswap.NewTurboswap(cfg.TurboswapConfig.APIToken, cfg.TurboswapConfig.URL)
nowpayments, err := nowpayments.NewNowPayments(&cfg.NowPaymentsConfig)
if err != nil {
log.Error("could not start twitter client", "err", err)
}
log.Info("turboswap loaded successfully")
log.Info("nowpayments loaded successfully")

return newBotEngine(eSl, cm, wallet, store, twitterClient, turboswap), nil
return newBotEngine(eSl, cm, wallet, store, twitterClient, nowpayments), nil
}

func newBotEngine(logger *log.SubLogger, cm *client.Mgr, w wallet.IWallet, s store.IStore,
twitterClient twitter_api.IClient, turboswap turboswap.ITurboSwap,
twitterClient twitter_api.IClient, nowpayments nowpayments.INowpayment,
) *BotEngine {
ctx, cancel := context.WithCancel(context.Background())

Expand All @@ -110,7 +113,7 @@ func newBotEngine(logger *log.SubLogger, cm *client.Mgr, w wallet.IWallet, s sto
clientMgr: cm,
store: s,
twitterClient: twitterClient,
turboswap: turboswap,
nowpayments: nowpayments,
}
}

Expand Down Expand Up @@ -312,7 +315,7 @@ func (be *BotEngine) RewardCalculate(stake int64, t string) (int64, string, int6
return reward, time, int64(utils.ChangeToCoin(bi.TotalPower)), nil
}

func (be *BotEngine) TwitterCampaign(discordID, twitterName, valAddr string) (*store.TwitterParty, error) {
func (be *BotEngine) BoosterPayment(discordID, twitterName, valAddr string) (*store.TwitterParty, error) {
be.Lock()
defer be.Unlock()

Expand Down Expand Up @@ -355,13 +358,13 @@ func (be *BotEngine) TwitterCampaign(discordID, twitterName, valAddr string) (*s
return nil, err
}

discountCode, err := gonanoid.Generate("0123456789", 6)
discountCode, err := gonanoid.Generate("0123456789", 8)
if err != nil {
return nil, err
}

totalPrice := 50
amountInPAC := 150
totalPrice := BoosterPrice
amountInPAC := int64(150)
if userInfo.Followers > 1000 {
amountInPAC = 200
}
Expand All @@ -379,33 +382,55 @@ func (be *BotEngine) TwitterCampaign(discordID, twitterName, valAddr string) (*s
CreatedAt: time.Now().Unix(),
}

err = be.turboswap.SendDiscountCode(be.ctx, party)
err = be.nowpayments.CreatePayment(party)
if err != nil {
return nil, err
}

err = be.store.AddTwitterParty(party)
err = be.store.SaveTwitterParty(party)
if err != nil {
return nil, err
}

return party, nil
}

func (be *BotEngine) TwitterCampaignStatus(twitterName string) (*store.TwitterParty, *turboswap.DiscountStatus, error) {
func (be *BotEngine) BoosterClaim(twitterName string) (*store.TwitterParty, error) {
party := be.store.FindTwitterParty(twitterName)
if party == nil {
return nil, nil, fmt.Errorf("no discount code generated for this Twitter account: `%v`", twitterName)
return nil, fmt.Errorf("no discount code generated for this Twitter account: `%v`", twitterName)
}
status, err := be.turboswap.GetStatus(be.ctx, party)
err := be.nowpayments.UpdatePayment(party)
if err != nil {
return nil, nil, err
return nil, err
}

if party.NowPaymentsFinished {
if party.TransactionID == "" {
logger.Info("sending bond transaction", "receiver", party.ValAddr, "amount", party.AmountInPAC)
memo := "Booster Program"
txID, err := be.wallet.BondTransaction(party.ValPubKey, party.ValAddr, memo, utils.CoinToChange(float64(party.AmountInPAC)))
if err != nil {
return nil, err
}

if txID == "" {
return nil, errors.New("can't send bond transaction")
}

party.TransactionID = txID

err = be.store.SaveTwitterParty(party)
if err != nil {
return nil, err
}
}
}

return party, status, nil
return party, nil
}

func (be *BotEngine) TwitterCampaignWhitelist(twitterName string, authorizedDiscordID string) error {
func (be *BotEngine) BoosterWhitelist(twitterName string, authorizedDiscordID string) error {
authorizedIDs := []string{}

if !slices.Contains(authorizedIDs, authorizedDiscordID) {
Expand Down
Loading