services/rfq/relayer/relapi/client.go
package relapi
import (
"context"
"fmt"
"net/http"
"github.com/dubonzi/otelresty"
"github.com/go-http-utils/headers"
"github.com/go-resty/resty/v2"
"github.com/synapsecns/sanguine/core/metrics"
"github.com/valyala/fastjson"
)
// RelayerClient is the interface for the relayer client.
type RelayerClient interface {
Health(ctx context.Context) (ok bool, err error)
RetryTransaction(ctx context.Context, txhash string) (*GetTxRetryResponse, error)
Withdraw(ctx context.Context, req *WithdrawRequest) (*WithdrawResponse, error)
GetTxHashByNonce(ctx context.Context, req *GetTxByNonceRequest) (*TxHashByNonceResponse, error)
GetQuoteRequestByTXID(ctx context.Context, txid string) (*GetQuoteRequestResponse, error)
GetQuoteRequestByTxHash(ctx context.Context, txhash string) (*GetQuoteRequestResponse, error)
}
type relayerClient struct {
client *resty.Client
}
// NewRelayerClient creates a new RelayerClient.
func NewRelayerClient(handler metrics.Handler, url string) RelayerClient {
client := resty.New()
client.SetBaseURL(url)
client.SetHeader(headers.UserAgent, "relayer-client")
otelresty.TraceClient(client, otelresty.WithTracerProvider(handler.GetTracerProvider()))
return &relayerClient{
client: client,
}
}
// Health checks if the relayer is healthy.
func (r *relayerClient) Health(ctx context.Context) (ok bool, err error) {
resp, err := r.client.R().SetContext(ctx).Get(getHealthRoute)
if err != nil {
return false, fmt.Errorf("failed to check health: %w", err)
}
if resp.StatusCode() != http.StatusOK {
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode())
}
ok = fastjson.GetString(resp.Body(), "status") == "ok"
return ok, nil
}
func (r *relayerClient) RetryTransaction(ctx context.Context, txhash string) (*GetTxRetryResponse, error) {
var res GetTxRetryResponse
resp, err := r.client.R().SetContext(ctx).
SetQueryParam("hash", txhash).
SetResult(&res).
Get(getRetryRoute)
if err != nil {
return nil, fmt.Errorf("failed to retry transaction: %w", err)
}
if resp.StatusCode() != http.StatusOK {
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode())
}
return &res, nil
}
// WithdrawResponse is the response for the withdraw request.
type WithdrawResponse struct {
Nonce uint64 `json:"nonce"`
}
// Withdraw withdraws an ERC20 or Ether from the relayer.
func (r *relayerClient) Withdraw(ctx context.Context, req *WithdrawRequest) (*WithdrawResponse, error) {
var res WithdrawResponse
resp, err := r.client.R().SetContext(ctx).
SetResult(&res).
SetBody(req).
Post(postWithdrawRoute)
if err != nil {
return nil, fmt.Errorf("failed to withdraw transaction: %w", err)
}
if resp.StatusCode() != http.StatusOK {
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode())
}
return &res, nil
}
// TxHashByNonceResponse is the request for getting a transaction hash by nonce.
type TxHashByNonceResponse struct {
Hash string `json:"withdrawTxHash"`
}
// GetTxByNonceRequest is the request for getting a transaction hash by nonce.
func (r *relayerClient) GetTxHashByNonce(ctx context.Context, req *GetTxByNonceRequest) (*TxHashByNonceResponse, error) {
var res TxHashByNonceResponse
resp, err := r.client.R().SetContext(ctx).
SetResult(&res).
SetQueryParam("chain_id", fmt.Sprintf("%d", req.ChainID)).
SetQueryParam("nonce", fmt.Sprintf("%d", req.Nonce)).
Get(getTxHashByNonceRoute)
if err != nil {
return nil, fmt.Errorf("failed to get tx hash by nonce: %w", err)
}
if resp.StatusCode() != http.StatusOK {
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode())
}
return &res, nil
}
func (r *relayerClient) GetQuoteRequestByTXID(ctx context.Context, txid string) (*GetQuoteRequestResponse, error) {
var res GetQuoteRequestResponse
resp, err := r.client.R().SetContext(ctx).
SetQueryParam("id", txid).
SetResult(&res).
Get(getRequestByTxID)
if err != nil {
return nil, fmt.Errorf("failed to get quote request by tx id: %w", err)
}
if resp.StatusCode() != http.StatusOK {
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode())
}
return &res, nil
}
func (r *relayerClient) GetQuoteRequestByTxHash(ctx context.Context, txHash string) (*GetQuoteRequestResponse, error) {
var res GetQuoteRequestResponse
resp, err := r.client.R().SetContext(ctx).
SetQueryParam("hash", txHash).
SetResult(&res).
Get(getRequestByTxHash)
if err != nil {
return nil, fmt.Errorf("failed to get quote request by tx hash: %w", err)
}
if resp.StatusCode() != http.StatusOK {
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode())
}
return &res, nil
}