services/rfq/relayer/relapi/server.go
// Package relapi provides RESTful API services for the RFQ relayer
package relapi
import (
"context"
"fmt"
"github.com/ipfs/go-log"
"github.com/synapsecns/sanguine/core/ginhelper"
"github.com/synapsecns/sanguine/ethergo/submitter"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/gin-gonic/gin"
"github.com/synapsecns/sanguine/core/metrics"
baseServer "github.com/synapsecns/sanguine/core/server"
"github.com/synapsecns/sanguine/ethergo/listener"
omniClient "github.com/synapsecns/sanguine/services/omnirpc/client"
"github.com/synapsecns/sanguine/services/rfq/contracts/fastbridge"
"github.com/synapsecns/sanguine/services/rfq/relayer/chain"
"github.com/synapsecns/sanguine/services/rfq/relayer/relconfig"
"github.com/synapsecns/sanguine/services/rfq/relayer/reldb"
)
// RelayerAPIServer is a struct that holds the configuration, database connection, gin engine, RPC client, metrics handler, and fast bridge contracts.
// It is used to initialize and run the API server.
type RelayerAPIServer struct {
cfg relconfig.Config
db reldb.Service
engine *gin.Engine
handler metrics.Handler
chains map[uint32]*chain.Chain
submitter submitter.TransactionSubmitter
}
// NewRelayerAPI holds the configuration, database connection, gin engine, RPC client, metrics handler, and fast bridge contracts.
// It is used to initialize and run the API server.
//
//nolint:cyclop
func NewRelayerAPI(
ctx context.Context,
cfg relconfig.Config,
handler metrics.Handler,
omniRPCClient omniClient.RPCClient,
store reldb.Service,
submitter submitter.TransactionSubmitter,
) (*RelayerAPIServer, error) {
if ctx == nil {
return nil, fmt.Errorf("context is nil")
}
if handler == nil {
return nil, fmt.Errorf("handler is nil")
}
if omniRPCClient == nil {
return nil, fmt.Errorf("omniRPCClient is nil")
}
if store == nil {
return nil, fmt.Errorf("store is nil")
}
chains := make(map[uint32]*chain.Chain)
for chainID := range cfg.Chains {
chainClient, err := omniRPCClient.GetChainClient(ctx, chainID)
if err != nil {
return nil, fmt.Errorf("could not create omnirpc client: %w", err)
}
rfqAddr, err := cfg.GetRFQAddress(chainID)
if err != nil {
return nil, fmt.Errorf("could not get rfq address: %w", err)
}
contract, err := fastbridge.NewFastBridgeRef(rfqAddr, chainClient)
if err != nil {
return nil, fmt.Errorf("could not create fast bridge contract: %w", err)
}
startBlock, err := contract.DeployBlock(&bind.CallOpts{Context: ctx})
if err != nil {
return nil, fmt.Errorf("could not get deploy block: %w", err)
}
chainListener, err := listener.NewChainListener(chainClient, store, rfqAddr, uint64(startBlock.Int64()), handler)
if err != nil {
return nil, fmt.Errorf("could not get chain listener: %w", err)
}
chains[uint32(chainID)], err = chain.NewChain(ctx, cfg, chainClient, chainListener, submitter)
if err != nil {
return nil, fmt.Errorf("could not create chain: %w", err)
}
}
return &RelayerAPIServer{
cfg: cfg,
db: store,
handler: handler,
chains: chains,
submitter: submitter,
}, nil
}
const (
getHealthRoute = "/health"
getRetryRoute = "/retry"
postWithdrawRoute = "/withdraw"
getTxHashByNonceRoute = "/tx_hash/by_nonce"
getRequestByTxID = "/request/by_tx_id"
getRequestByTxHash = "/request/by_tx_hash"
)
var logger = log.Logger("relayer-api")
// Run runs the rest api server.
func (r *RelayerAPIServer) Run(ctx context.Context) error {
engine := ginhelper.New(logger)
// default tracing middleware
engine.Use(r.handler.Gin()...)
h := NewHandler(r.handler, r.db, r.chains, r.cfg, r.submitter)
// Assign GET routes
engine.GET(getHealthRoute, h.GetHealth)
engine.GET(getRetryRoute, h.GetTxRetry)
engine.GET(getRequestByTxID, h.GetQuoteRequestByTxID)
engine.GET(getRequestByTxHash, h.GetQuoteRequestByTxHash)
engine.GET(metrics.MetricsPathDefault, gin.WrapH(r.handler.Handler()))
if r.cfg.EnableAPIWithdrawals {
engine.POST(postWithdrawRoute, h.Withdraw)
engine.GET(getTxHashByNonceRoute, h.GetTxHashByNonce)
}
r.engine = engine
connection := baseServer.Server{}
fmt.Printf("starting api at http://localhost:%s\n", r.cfg.RelayerAPIPort)
err := connection.ListenAndServe(ctx, fmt.Sprintf(":%s", r.cfg.RelayerAPIPort), r.engine)
if err != nil {
return fmt.Errorf("could not start relayer api server: %w", err)
}
return nil
}