Skip to content

Commit

Permalink
opbot refunder first pass
Browse files Browse the repository at this point in the history
  • Loading branch information
trajan0x committed Jul 2, 2024
1 parent 544967f commit a65c2bc
Show file tree
Hide file tree
Showing 21 changed files with 431 additions and 10 deletions.
46 changes: 44 additions & 2 deletions contrib/opbot/botmd/botmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,19 @@ package botmd

import (
"context"
"fmt"
"github.com/slack-io/slacker"
"github.com/synapsecns/sanguine/contrib/opbot/config"
"github.com/synapsecns/sanguine/contrib/opbot/signoz"
"github.com/synapsecns/sanguine/core/dbcommon"
"github.com/synapsecns/sanguine/core/metrics"
signerConfig "github.com/synapsecns/sanguine/ethergo/signer/config"
"github.com/synapsecns/sanguine/ethergo/signer/signer"
"github.com/synapsecns/sanguine/ethergo/submitter"
submitterdb "github.com/synapsecns/sanguine/ethergo/submitter/db"
cctpSql "github.com/synapsecns/sanguine/services/cctp-relayer/db/sql"
omnirpcClient "github.com/synapsecns/sanguine/services/omnirpc/client"
"golang.org/x/sync/errgroup"
)

// Bot represents the bot server.
Expand All @@ -15,6 +24,10 @@ type Bot struct {
cfg config.Config
signozClient *signoz.Client
signozEnabled bool
rpcClient omnirpcClient.RPCClient
signer signer.Signer
submitter submitter.TransactionSubmitter
db submitterdb.SubmitterDBFactory

Check failure on line 30 in contrib/opbot/botmd/botmd.go

View workflow job for this annotation

GitHub Actions / Lint (contrib/opbot)

`db` is unused (structcheck)
}

// NewBot creates a new bot server.
Expand All @@ -32,8 +45,10 @@ func NewBot(handler metrics.Handler, cfg config.Config) Bot {
bot.signozEnabled = true
}

bot.rpcClient = omnirpcClient.NewOmnirpcClient(cfg.OmniRPCURL, handler, omnirpcClient.WithCaptureReqRes())

Check warning on line 49 in contrib/opbot/botmd/botmd.go

View check run for this annotation

Codecov / codecov/patch

contrib/opbot/botmd/botmd.go#L48-L49

Added lines #L48 - L49 were not covered by tests
bot.addMiddleware(bot.tracingMiddleware(), bot.metricsMiddleware())
bot.addCommands(bot.traceCommand(), bot.rfqLookupCommand())
bot.addCommands(bot.traceCommand(), bot.rfqLookupCommand(), bot.rfqRefund())

Check warning on line 51 in contrib/opbot/botmd/botmd.go

View check run for this annotation

Codecov / codecov/patch

contrib/opbot/botmd/botmd.go#L51

Added line #L51 was not covered by tests

return bot
}
Expand All @@ -53,6 +68,33 @@ func (b *Bot) addCommands(commands ...*slacker.CommandDefinition) {
// Start starts the bot server.
// nolint: wrapcheck
func (b *Bot) Start(ctx context.Context) error {
var err error
b.signer, err = signerConfig.SignerFromConfig(ctx, b.cfg.Signer)
if err != nil {
return fmt.Errorf("failed to create signer: %w", err)
}

Check warning on line 75 in contrib/opbot/botmd/botmd.go

View check run for this annotation

Codecov / codecov/patch

contrib/opbot/botmd/botmd.go#L71-L75

Added lines #L71 - L75 were not covered by tests

dbType, err := dbcommon.DBTypeFromString(b.cfg.Database.Type)
if err != nil {
return fmt.Errorf("could not get db type: %w", err)
}

Check warning on line 80 in contrib/opbot/botmd/botmd.go

View check run for this annotation

Codecov / codecov/patch

contrib/opbot/botmd/botmd.go#L77-L80

Added lines #L77 - L80 were not covered by tests

store, err := cctpSql.Connect(ctx, dbType, b.cfg.Database.DSN, b.handler)
if err != nil {
return fmt.Errorf("could not connect to database: %w", err)
}

Check warning on line 85 in contrib/opbot/botmd/botmd.go

View check run for this annotation

Codecov / codecov/patch

contrib/opbot/botmd/botmd.go#L82-L85

Added lines #L82 - L85 were not covered by tests

b.submitter = submitter.NewTransactionSubmitter(b.handler, b.signer, b.rpcClient, store.SubmitterDB(), &b.cfg.SubmitterConfig)

g, ctx := errgroup.WithContext(ctx)
g.Go(func() error {
return b.submitter.Start(ctx)
})

Check warning on line 92 in contrib/opbot/botmd/botmd.go

View check run for this annotation

Codecov / codecov/patch

contrib/opbot/botmd/botmd.go#L87-L92

Added lines #L87 - L92 were not covered by tests

g.Go(func() error {
return b.server.Listen(ctx)
})

Check warning on line 96 in contrib/opbot/botmd/botmd.go

View check run for this annotation

Codecov / codecov/patch

contrib/opbot/botmd/botmd.go#L94-L96

Added lines #L94 - L96 were not covered by tests

// nolint: wrapcheck
return b.server.Listen(ctx)
return g.Wait()

Check warning on line 99 in contrib/opbot/botmd/botmd.go

View check run for this annotation

Codecov / codecov/patch

contrib/opbot/botmd/botmd.go#L99

Added line #L99 was not covered by tests
}
80 changes: 80 additions & 0 deletions contrib/opbot/botmd/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,21 @@ package botmd

import (
"fmt"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/hako/durafmt"
"github.com/slack-go/slack"
"github.com/slack-io/slacker"
"github.com/synapsecns/sanguine/contrib/opbot/signoz"
"github.com/synapsecns/sanguine/ethergo/chaindata"
rfqClient "github.com/synapsecns/sanguine/services/rfq/api/client"
"github.com/synapsecns/sanguine/services/rfq/contracts/fastbridge"
"github.com/synapsecns/sanguine/services/rfq/relayer/relapi"
"log"
"math/big"
"regexp"
"strconv"
"strings"
"sync"
"time"
Expand Down Expand Up @@ -239,6 +245,80 @@ func (b *Bot) rfqLookupCommand() *slacker.CommandDefinition {
}}
}

func (b *Bot) rfqRefund() *slacker.CommandDefinition {
return &slacker.CommandDefinition{
Command: "refund <tx> <origin_chainid>",
Description: "refund a quote request",
Examples: []string{"refund 0x1234"},
Handler: func(ctx *slacker.CommandContext) {
client, err := rfqClient.NewUnauthenticatedClient(b.handler, b.cfg.RFQApiURL)
if err != nil {
log.Printf("error creating rfq client: %v\n", err)
return
}

Check warning on line 258 in contrib/opbot/botmd/commands.go

View check run for this annotation

Codecov / codecov/patch

contrib/opbot/botmd/commands.go#L248-L258

Added lines #L248 - L258 were not covered by tests

contracts, err := client.GetRFQContracts(ctx.Context())
if err != nil {
log.Printf("error fetching rfq contracts: %v\n", err)
return
}

Check warning on line 264 in contrib/opbot/botmd/commands.go

View check run for this annotation

Codecov / codecov/patch

contrib/opbot/botmd/commands.go#L260-L264

Added lines #L260 - L264 were not covered by tests

tx := stripLinks(ctx.Request().Param("tx"))
originChainIDStr := ctx.Request().Param("origin_chainid")

originChainID, err := strconv.Atoi(originChainIDStr)
if err != nil {
_, err := ctx.Response().Reply("origin_chainid must be a number")
if err != nil {
log.Println(err)
}
return

Check warning on line 275 in contrib/opbot/botmd/commands.go

View check run for this annotation

Codecov / codecov/patch

contrib/opbot/botmd/commands.go#L266-L275

Added lines #L266 - L275 were not covered by tests
}

chainClient, err := b.rpcClient.GetChainClient(ctx.Context(), originChainID)
if err != nil {
log.Printf("error getting chain client: %v\n", err)
return
}

Check warning on line 282 in contrib/opbot/botmd/commands.go

View check run for this annotation

Codecov / codecov/patch

contrib/opbot/botmd/commands.go#L278-L282

Added lines #L278 - L282 were not covered by tests

contractAddress, ok := contracts.Contracts[uint32(originChainID)]
if !ok {
_, err := ctx.Response().Reply("contract address not found")
if err != nil {
log.Println(err)
}
return

Check warning on line 290 in contrib/opbot/botmd/commands.go

View check run for this annotation

Codecov / codecov/patch

contrib/opbot/botmd/commands.go#L284-L290

Added lines #L284 - L290 were not covered by tests
}

fastBridgeHandle, err := fastbridge.NewFastBridge(common.HexToAddress(contractAddress), chainClient)
if err != nil {
log.Printf("error creating fast bridge: %v\n", err)
return
}

Check warning on line 297 in contrib/opbot/botmd/commands.go

View check run for this annotation

Codecov / codecov/patch

contrib/opbot/botmd/commands.go#L293-L297

Added lines #L293 - L297 were not covered by tests

for _, relayer := range b.cfg.RelayerURLS {
relClient := relapi.NewRelayerClient(b.handler, relayer)
qr, err := relClient.GetQuoteRequestByTXID(ctx.Context(), tx)
if err != nil {
log.Printf("error fetching quote request: %v\n", err)
continue

Check warning on line 304 in contrib/opbot/botmd/commands.go

View check run for this annotation

Codecov / codecov/patch

contrib/opbot/botmd/commands.go#L299-L304

Added lines #L299 - L304 were not covered by tests
}

nonce, err := b.submitter.SubmitTransaction(ctx.Context(), big.NewInt(int64(originChainID)), func(transactor *bind.TransactOpts) (tx *types.Transaction, err error) {
return fastBridgeHandle.Refund(transactor, common.Hex2Bytes(qr.QuoteRequestRaw))

Check failure on line 308 in contrib/opbot/botmd/commands.go

View workflow job for this annotation

GitHub Actions / Lint (contrib/opbot)

error returned from external package is unwrapped: sig: func (*github.com/synapsecns/sanguine/services/rfq/contracts/fastbridge.FastBridgeTransactor).Refund(opts *github.com/ethereum/go-ethereum/accounts/abi/bind.TransactOpts, request []byte) (*github.com/ethereum/go-ethereum/core/types.Transaction, error) (wrapcheck)
})
if err != nil {
log.Printf("error submitting refund: %v\n", err)
continue

Check warning on line 312 in contrib/opbot/botmd/commands.go

View check run for this annotation

Codecov / codecov/patch

contrib/opbot/botmd/commands.go#L307-L312

Added lines #L307 - L312 were not covered by tests
}

_, err = ctx.Response().Reply(fmt.Sprintf("refund submitted with nonce %d", nonce))

Check failure on line 315 in contrib/opbot/botmd/commands.go

View workflow job for this annotation

GitHub Actions / Lint (contrib/opbot)

ineffectual assignment to err (ineffassign)
return

Check warning on line 316 in contrib/opbot/botmd/commands.go

View check run for this annotation

Codecov / codecov/patch

contrib/opbot/botmd/commands.go#L315-L316

Added lines #L315 - L316 were not covered by tests
}
},
}
}

func toExplorerSlackLink(ogHash string) string {
rfqHash := strings.ToUpper(ogHash)
// cut off 0x
Expand Down
21 changes: 21 additions & 0 deletions contrib/opbot/config/config.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
// Package config provides a simple way to read and write configuration files.
package config

import (
"github.com/synapsecns/sanguine/ethergo/signer/config"
submitterConfig "github.com/synapsecns/sanguine/ethergo/submitter/config"
)

// Config represents the configuration of the application.
type Config struct {
// SlackBotToken is the token of the slack bot.
Expand All @@ -18,4 +23,20 @@ type Config struct {
SignozBaseURL string `yaml:"signoz_base_url"`
// RelayerURLS is the list of RFQ relayer URLs.
RelayerURLS []string `yaml:"rfq_relayer_urls"`
// RFQApiURL is the URL of the RFQ API.
RFQApiURL string `yaml:"rfq_api_url"`
// OmniRPCURL is the URL of the Omni RPC.
OmniRPCURL string `yaml:"omni_rpc_url"`
// Signer is the signer config.
Signer config.SignerConfig `yaml:"signer"`
// SubmitterConfig is the submitter config.
SubmitterConfig submitterConfig.Config `yaml:"submitter_config"`
// Database is the database config.
Database DatabaseConfig `yaml:"database"`
}

// DatabaseConfig represents the configuration for the database.
type DatabaseConfig struct {
Type string `yaml:"type"`
DSN string `yaml:"dsn"` // Data Source Name
}
1 change: 0 additions & 1 deletion contrib/opbot/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,6 @@ replace (
// later versions give errors on uint64 being too high.
github.com/brianvoe/gofakeit/v6 => github.com/brianvoe/gofakeit/v6 v6.9.0
github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1
github.com/slack-go/slack => github.com/slack-go/slack v0.12.2
github.com/slack-io/slacker => github.com/slack-io/slacker v0.1.1-0.20240701203341-bd3ee211e9d2
github.com/synapsecns/sanguine/core => ./../../core
github.com/synapsecns/sanguine/ethergo => ./../../ethergo
Expand Down
4 changes: 2 additions & 2 deletions contrib/opbot/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -990,8 +990,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A=
github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
github.com/slack-go/slack v0.12.2 h1:x3OppyMyGIbbiyFhsBmpf9pwkUzMhthJMRNmNlA4LaQ=
github.com/slack-go/slack v0.12.2/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw=
github.com/slack-go/slack v0.13.0 h1:7my/pR2ubZJ9912p9FtvALYpbt0cQPAqkRy2jaSI1PQ=
github.com/slack-go/slack v0.13.0/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw=
github.com/slack-io/commander v0.0.0-20231120025847-9fd78b4b2d54 h1:aRc+G2mUb697z6bR09Roq6kP08suJulgNo00SGhAsfM=
github.com/slack-io/commander v0.0.0-20231120025847-9fd78b4b2d54/go.mod h1:aHmXZnL/ELKlfMybblXnnCl+IeHQ+gMb++DT7ujIGlA=
github.com/slack-io/proper v0.0.0-20231119200853-f78ba4fc878f h1:wiEJBKJKvMOeE9KtLV7iwtHOsNXDBZloL+FPlkZ6vnA=
Expand Down
38 changes: 38 additions & 0 deletions contrib/opbot/sql/base/base.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package base

import (
"github.com/synapsecns/sanguine/core/metrics"
submitterDB "github.com/synapsecns/sanguine/ethergo/submitter/db"
"github.com/synapsecns/sanguine/ethergo/submitter/db/txdb"
"gorm.io/gorm"
)

// Store is a store that implements an underlying gorm db.
type Store struct {
db *gorm.DB
metrics metrics.Handler
submitterStore submitterDB.Service
}

// NewStore creates a new store.
func NewStore(db *gorm.DB, metrics metrics.Handler) *Store {
txDB := txdb.NewTXStore(db, metrics)
return &Store{db: db, metrics: metrics, submitterStore: txDB}

Check warning on line 20 in contrib/opbot/sql/base/base.go

View check run for this annotation

Codecov / codecov/patch

contrib/opbot/sql/base/base.go#L18-L20

Added lines #L18 - L20 were not covered by tests
}

// DB gets the database object for mutation outside of the lib.
func (s Store) DB() *gorm.DB {
return s.db

Check warning on line 25 in contrib/opbot/sql/base/base.go

View check run for this annotation

Codecov / codecov/patch

contrib/opbot/sql/base/base.go#L24-L25

Added lines #L24 - L25 were not covered by tests
}

// SubmitterDB gets the submitter database object for mutation outside of the lib.
func (s Store) SubmitterDB() submitterDB.Service {
return s.submitterStore

Check warning on line 30 in contrib/opbot/sql/base/base.go

View check run for this annotation

Codecov / codecov/patch

contrib/opbot/sql/base/base.go#L29-L30

Added lines #L29 - L30 were not covered by tests
}

// GetAllModels gets all models to migrate.
// see: https://medium.com/@SaifAbid/slice-interfaces-8c78f8b6345d for an explanation of why we can't do this at initialization time
func GetAllModels() (allModels []interface{}) {
allModels = append(allModels, txdb.GetAllModels()...)
return allModels

Check warning on line 37 in contrib/opbot/sql/base/base.go

View check run for this annotation

Codecov / codecov/patch

contrib/opbot/sql/base/base.go#L35-L37

Added lines #L35 - L37 were not covered by tests
}
2 changes: 2 additions & 0 deletions contrib/opbot/sql/base/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Package base contains the base sql implementation
package base
2 changes: 2 additions & 0 deletions contrib/opbot/sql/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Package sql provides a common interface for starting sql-lite databases
package sql
2 changes: 2 additions & 0 deletions contrib/opbot/sql/mysql/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Package mysql contains a mysql db
package mysql
66 changes: 66 additions & 0 deletions contrib/opbot/sql/mysql/store.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package mysql

import (
"context"
"fmt"
"github.com/synapsecns/sanguine/contrib/opbot/sql/base"
submitterDB "github.com/synapsecns/sanguine/ethergo/submitter/db"
"time"

"github.com/ipfs/go-log"
common_base "github.com/synapsecns/sanguine/core/dbcommon"
"github.com/synapsecns/sanguine/core/metrics"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/schema"
)

// Logger is the mysql logger.
var logger = log.Logger("synapse-mysql")

// NewMysqlStore creates a new mysql store for a given data store.
func NewMysqlStore(ctx context.Context, dbURL string, handler metrics.Handler) (*Store, error) {
logger.Debug("create mysql store")

gdb, err := gorm.Open(mysql.Open(dbURL), &gorm.Config{
Logger: common_base.GetGormLogger(logger),
FullSaveAssociations: true,
NamingStrategy: NamingStrategy,
NowFunc: time.Now,
})

if err != nil {
return nil, fmt.Errorf("could not create mysql connection: %w", err)
}

Check warning on line 34 in contrib/opbot/sql/mysql/store.go

View check run for this annotation

Codecov / codecov/patch

contrib/opbot/sql/mysql/store.go#L22-L34

Added lines #L22 - L34 were not covered by tests

sqlDB, err := gdb.DB()
if err != nil {
return nil, fmt.Errorf("could not get sql db: %w", err)
}

Check warning on line 39 in contrib/opbot/sql/mysql/store.go

View check run for this annotation

Codecov / codecov/patch

contrib/opbot/sql/mysql/store.go#L36-L39

Added lines #L36 - L39 were not covered by tests

// fixes a timeout issue https://stackoverflow.com/a/42146536
sqlDB.SetMaxIdleConns(MaxIdleConns)
sqlDB.SetConnMaxLifetime(time.Hour)

handler.AddGormCallbacks(gdb)

err = gdb.WithContext(ctx).AutoMigrate(base.GetAllModels()...)
if err != nil {
return nil, fmt.Errorf("could not migrate on mysql: %w", err)
}

Check warning on line 50 in contrib/opbot/sql/mysql/store.go

View check run for this annotation

Codecov / codecov/patch

contrib/opbot/sql/mysql/store.go#L42-L50

Added lines #L42 - L50 were not covered by tests

return &Store{base.NewStore(gdb, handler)}, nil

Check warning on line 52 in contrib/opbot/sql/mysql/store.go

View check run for this annotation

Codecov / codecov/patch

contrib/opbot/sql/mysql/store.go#L52

Added line #L52 was not covered by tests
}

// Store is the mysql store. It extends the bsae store for mysql queries.
type Store struct {
*base.Store
}

// MaxIdleConns is exported here for testing. Tests execute too slowly with a reconnect each time.
var MaxIdleConns = 10

// NamingStrategy is for table prefixes.
var NamingStrategy = schema.NamingStrategy{}

var _ submitterDB.SubmitterDBFactory = &Store{}
2 changes: 2 additions & 0 deletions contrib/opbot/sql/sqlite/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Package sqlite implements the sqlite package
package sqlite
Loading

0 comments on commit a65c2bc

Please sign in to comment.