synapsecns/sanguine

View on GitHub
services/rfq/relayer/relapi/client.go

Summary

Maintainability
B
5 hrs
Test Coverage
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
}