synapsecns/sanguine

View on GitHub
services/rfq/relayer/reldb/db.go

Summary

Maintainability
A
0 mins
Test Coverage
package reldb

import (
    "context"
    "database/sql"
    "database/sql/driver"
    "errors"
    "fmt"
    "math/big"

    "github.com/synapsecns/sanguine/ethergo/listener/db"

    "github.com/ethereum/go-ethereum/common"
    "github.com/synapsecns/sanguine/core/dbcommon"
    submitterDB "github.com/synapsecns/sanguine/ethergo/submitter/db"
    "github.com/synapsecns/sanguine/services/rfq/contracts/fastbridge"
)

// Writer is the interface for writing to the database.
type Writer interface {
    // StoreQuoteRequest stores a quote request. If one already exists, only  the status will be updated
    // TODO: find a better way to describe this in the name
    StoreQuoteRequest(ctx context.Context, request QuoteRequest) error
    // StoreRebalance stores a rebalance.
    StoreRebalance(ctx context.Context, rebalance Rebalance) error
    // UpdateQuoteRequestStatus updates the status of a quote request
    UpdateQuoteRequestStatus(ctx context.Context, id [32]byte, status QuoteRequestStatus, prevStatus *QuoteRequestStatus) error
    // UpdateRebalance updates the status of a rebalance action.
    // If the origin is supplied, it will be used to update the ID for the corresponding rebalance model.
    UpdateRebalance(ctx context.Context, rebalance Rebalance, updateID bool) error
    // UpdateLatestRebalance updates a rebalance action.
    // This is meant to be used in cases where a consistent rebalance ID cannot be determined across chains.
    UpdateLatestRebalance(ctx context.Context, rebalance Rebalance) error
    // UpdateDestTxHash updates the dest tx hash of a quote request
    UpdateDestTxHash(ctx context.Context, id [32]byte, destTxHash common.Hash) error
    // UpdateRelayNonce updates the relay nonce of a quote request
    UpdateRelayNonce(ctx context.Context, id [32]byte, nonce uint64) error
}

// Reader is the interface for reading from the database.
type Reader interface {
    // GetQuoteRequestByID gets a quote request by id. Should return ErrNoQuoteForID if not found
    GetQuoteRequestByID(ctx context.Context, id [32]byte) (*QuoteRequest, error)
    // GetQuoteRequestByOriginTxHash gets a quote request by origin tx hash. Should return ErrNoQuoteForTxHash if not found
    GetQuoteRequestByOriginTxHash(ctx context.Context, txHash common.Hash) (*QuoteRequest, error)
    // GetQuoteResultsByStatus gets quote results by status
    GetQuoteResultsByStatus(ctx context.Context, matchStatuses ...QuoteRequestStatus) (res []QuoteRequest, _ error)
    // GetPendingRebalances checks fetches all pending rebalances that involve the given chainIDs.
    GetPendingRebalances(ctx context.Context, chainIDs ...uint64) ([]*Rebalance, error)
    // GetRebalance gets a rebalance by ID. Should return ErrNoRebalanceForID if not found.
    GetRebalanceByID(ctx context.Context, rebalanceID string) (*Rebalance, error)
    // GetDBStats gets the database stats.
    GetDBStats(ctx context.Context) (*sql.DBStats, error)
    // GetStatusCounts gets the counts of quote requests by status.
    GetStatusCounts(ctx context.Context, matchStatuses ...QuoteRequestStatus) (map[QuoteRequestStatus]int, error)
}

// Service is the interface for the database service.
type Service interface {
    Reader
    // SubmitterDB returns the submitter database service.
    SubmitterDB() submitterDB.Service
    Writer
    db.ChainListenerDB
}

var (
    // ErrNoQuoteForID means the quote was not found.
    ErrNoQuoteForID = errors.New("no quote found for tx id")
    // ErrNoQuoteForTxHash means the quote was not found.
    ErrNoQuoteForTxHash = errors.New("no quote found for tx hash")
    // ErrNoRebalanceForID means the rebalance was not found.
    ErrNoRebalanceForID = errors.New("no rebalance found for id")
)

// QuoteRequest is the quote request object.
type QuoteRequest struct {
    BlockNumber         uint64
    OriginTokenDecimals uint8
    RawRequest          []byte
    DestTokenDecimals   uint8
    TransactionID       [32]byte
    Sender              common.Address
    Transaction         fastbridge.IFastBridgeBridgeTransaction
    // Status is the quote request status
    Status       QuoteRequestStatus
    OriginTxHash common.Hash
    DestTxHash   common.Hash
    // RelayNonce is the nonce for the relay transaction.
    RelayNonce uint64
}

// GetOriginIDPair gets the origin chain id and token address pair.
// for some reason, this is specified as [chainid]-[tokenaddr] in the config.
// this represents the origin pair.
func (q QuoteRequest) GetOriginIDPair() string {
    return fmt.Sprintf("%d-%s", q.Transaction.OriginChainId, q.Transaction.OriginToken.Hex())
}

// GetDestIDPair gets the destination chain id and token address pair.
// for some reason, this is specified as [chainid]-[tokenaddr] in the config.
// this represents the destination pair.
func (q QuoteRequest) GetDestIDPair() string {
    return fmt.Sprintf("%d-%s", q.Transaction.DestChainId, q.Transaction.DestToken.Hex())
}

// QuoteRequestStatus is the status of a quote request in the db.
// This is the primary mechanism for moving data through the app.
//
// TODO: consider making this an interface and exporting that.
//
// EXTREMELY IMPORTANT: DO NOT ADD NEW VALUES TO THIS ENUM UNLESS THEY ARE AT THE END.
//
//go:generate go run golang.org/x/tools/cmd/stringer -type=QuoteRequestStatus
type QuoteRequestStatus uint8

const (
    // Seen means the quote request has been seen by the relayer, but not processed or committed to.
    Seen QuoteRequestStatus = iota + 1
    // NotEnoughInventory means the relayer does not have enough inventory to process the request.
    // This can be retried at a later time.
    NotEnoughInventory
    // DeadlineExceeded means the quote request has exceeded the deadline.
    // This is a terminal state.
    DeadlineExceeded
    // WillNotProcess means the relayer will not process the request for some reason.
    // This is a terminal state.
    WillNotProcess
    // CommittedPending means the relayer has committed liquidity to the request to the chain, but it is not yet confirmed on chain.
    CommittedPending
    // CommittedConfirmed means the relayer has committed liquidity to the request to the chain, and original bridge tx has been confirmed on chain.
    CommittedConfirmed
    // RelayStarted means the relayer has called Relay() on the destination chain.
    RelayStarted
    // RelayCompleted means the relayer has called Relay() on the destination chain, and the tx has been confirmed on chain.
    RelayCompleted
    // ProvePosting means the relayer has called Prove() on the origin chain.
    ProvePosting
    // ProvePosted means the relayer has called Prove() on the origin chain, and the tx has been confirmed on chain.
    ProvePosted
    // ClaimPending means the relayer has called Claim() on the origin chain.
    ClaimPending
    // ClaimCompleted means the relayer has called Claim() on the origin chain, and the tx has been confirmed on chain.
    ClaimCompleted
    // RelayRaceLost means another relayer has relayed the tx.
    RelayRaceLost
)

// Int returns the int value of the quote request status.
func (q QuoteRequestStatus) Int() uint8 {
    return uint8(q)
}

// GormDataType implements the gorm common interface for enums.
func (q QuoteRequestStatus) GormDataType() string {
    return dbcommon.EnumDataType
}

// Scan implements the gorm common interface for enums.
func (q *QuoteRequestStatus) Scan(src any) error {
    res, err := dbcommon.EnumScan(src)
    if err != nil {
        return fmt.Errorf("could not scan %w", err)
    }
    newStatus := QuoteRequestStatus(res)
    *q = newStatus
    return nil
}

// Value implements the gorm common interface for enums.
func (q QuoteRequestStatus) Value() (driver.Value, error) {
    // nolint: wrapcheck
    return dbcommon.EnumValue(q)
}

var _ dbcommon.Enum = (*QuoteRequestStatus)(nil)

// Rebalance represents a rebalance action.
type Rebalance struct {
    RebalanceID     *string
    Origin          uint64
    Destination     uint64
    OriginAmount    *big.Int
    OriginTokenAddr common.Address
    Status          RebalanceStatus
    OriginTxHash    common.Hash
    DestTxHash      common.Hash
    TokenName       string
}

// RebalanceStatus is the status of a rebalance action in the db.
//
//go:generate go run golang.org/x/tools/cmd/stringer -type=RebalanceStatus
type RebalanceStatus uint8

const (
    // RebalanceInitiated means the rebalance transaction has been initiated.
    RebalanceInitiated RebalanceStatus = iota + 1
    // RebalancePending means the rebalance transaction has been confirmed on the origin.
    RebalancePending
    // RebalanceCompleted means the rebalance transaction has been confirmed on the destination.
    RebalanceCompleted
)

// Int returns the int value of the quote request status.
func (r RebalanceStatus) Int() uint8 {
    return uint8(r)
}

// GormDataType implements the gorm common interface for enums.
func (r RebalanceStatus) GormDataType() string {
    return dbcommon.EnumDataType
}

// Scan implements the gorm common interface for enums.
func (r *RebalanceStatus) Scan(src any) error {
    res, err := dbcommon.EnumScan(src)
    if err != nil {
        return fmt.Errorf("could not scan %w", err)
    }
    newStatus := RebalanceStatus(res)
    *r = newStatus
    return nil
}

// Value implements the gorm common interface for enums.
func (r RebalanceStatus) Value() (driver.Value, error) {
    // nolint: wrapcheck
    return dbcommon.EnumValue(r)
}

var _ dbcommon.Enum = (*RebalanceStatus)(nil)