synapsecns/sanguine

View on GitHub
services/rfq/guard/guarddb/base/model.go

Summary

Maintainability
A
40 mins
Test Coverage
package base

import (
    "errors"
    "fmt"
    "math/big"
    "time"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/common/hexutil"
    "github.com/synapsecns/sanguine/core/dbcommon"
    "github.com/synapsecns/sanguine/services/rfq/contracts/fastbridge"
    "github.com/synapsecns/sanguine/services/rfq/guard/guarddb"
)

func init() {
    namer := dbcommon.NewNamer(GetAllModels())
    statusFieldName = namer.GetConsistentName("Status")
    transactionIDFieldName = namer.GetConsistentName("TransactionID")
}

var (
    // statusFieldName is the status field name.
    statusFieldName string
    // transactionIDFieldName is the transactions id field name.
    transactionIDFieldName string
)

// PendingProvenModel is the primary event model.
type PendingProvenModel struct {
    // CreatedAt is the creation time
    CreatedAt time.Time
    // UpdatedAt is the update time
    UpdatedAt time.Time
    // Origin is the origin chain id
    Origin uint32
    // RelayerAddress is the address of the relayer that called prove()
    RelayerAddress string
    // TransactionID is the transaction id of the event
    TransactionID string `gorm:"column:transaction_id;primaryKey"`
    // TxHash is the hash of the relay transaction on destination
    TxHash string
    // Status is the status of the event
    Status guarddb.PendingProvenStatus `gorm:"column:status;index:idx_guard_status_name"`
    // BlockNumber is the block number of the event
    BlockNumber uint64
}

// FromPendingProven converts a quote request to an object that can be stored in the db.
func FromPendingProven(proven guarddb.PendingProven) PendingProvenModel {
    return PendingProvenModel{
        Origin:         proven.Origin,
        RelayerAddress: proven.RelayerAddress.Hex(),
        TransactionID:  hexutil.Encode(proven.TransactionID[:]),
        TxHash:         proven.TxHash.Hex(),
        Status:         proven.Status,
        BlockNumber:    proven.BlockNumber,
    }
}

// ToPendingProven converts a db object to a pending proven.
func (p PendingProvenModel) ToPendingProven() (*guarddb.PendingProven, error) {
    txID, err := hexutil.Decode(p.TransactionID)
    if err != nil {
        return nil, fmt.Errorf("could not get transaction id: %w", err)
    }

    transactionID, err := sliceToArray(txID)
    if err != nil {
        return nil, fmt.Errorf("could not convert transaction id: %w", err)
    }

    return &guarddb.PendingProven{
        Origin:         p.Origin,
        RelayerAddress: common.HexToAddress(p.RelayerAddress),
        TransactionID:  transactionID,
        TxHash:         common.HexToHash(p.TxHash),
        Status:         p.Status,
        BlockNumber:    p.BlockNumber,
    }, nil
}

// BridgeRequestModel is the primary event model.
type BridgeRequestModel struct {
    // CreatedAt is the creation time
    CreatedAt time.Time
    // UpdatedAt is the update time
    UpdatedAt time.Time
    // TransactionID is the transaction id of the event
    TransactionID string `gorm:"column:transaction_id;primaryKey"`
    // OriginChainID is the origin chain for the transactions
    OriginChainID uint32
    // DestChainID is the destination chain for the tx
    DestChainID uint32
    // OriginSender is the original sender
    OriginSender string
    // DestRecipient is the recipient of the destination tx
    DestRecipient string
    // OriginToken is the origin token address
    OriginToken string
    // DestToken is the destination token address
    DestToken string
    // OriginAmount is the origin amount stored for sorting.
    // This is not the source of truth, but is approximate
    OriginAmount string
    // DestAmount is the destination amount stored for sorting.
    DestAmount string
    // Deadline is the deadline for the relay
    Deadline time.Time `gorm:"index"`
    // OriginNonce is the nonce on the origin chain in the app.
    // this is not effected by the message.sender nonce.
    OriginNonce int `gorm:"index"`
    // RawRequest is the raw request, hex encoded.
    RawRequest string
    // SendChainGas is true if the chain should send gas
    SendChainGas bool
}

// FromBridgeRequest converts a bridge request object to db model.
func FromBridgeRequest(request guarddb.BridgeRequest) BridgeRequestModel {
    return BridgeRequestModel{
        TransactionID: hexutil.Encode(request.TransactionID[:]),
        OriginChainID: request.Transaction.OriginChainId,
        DestChainID:   request.Transaction.DestChainId,
        OriginSender:  request.Transaction.OriginSender.String(),
        DestRecipient: request.Transaction.DestRecipient.String(),
        OriginToken:   request.Transaction.OriginToken.String(),
        RawRequest:    hexutil.Encode(request.RawRequest),
        SendChainGas:  request.Transaction.SendChainGas,
        DestToken:     request.Transaction.DestToken.String(),
        OriginAmount:  request.Transaction.OriginAmount.String(),
        DestAmount:    request.Transaction.DestAmount.String(),
        Deadline:      time.Unix(int64(request.Transaction.Deadline.Uint64()), 0),
        OriginNonce:   int(request.Transaction.Nonce.Uint64()),
    }
}

// ToBridgeRequest converts the bridge request db model to object.
func (b BridgeRequestModel) ToBridgeRequest() (*guarddb.BridgeRequest, error) {
    txID, err := hexutil.Decode(b.TransactionID)
    if err != nil {
        return nil, fmt.Errorf("could not get transaction id: %w", err)
    }

    req, err := hexutil.Decode(b.RawRequest)
    if err != nil {
        return nil, fmt.Errorf("could not get request: %w", err)
    }

    transactionID, err := sliceToArray(txID)
    if err != nil {
        return nil, fmt.Errorf("could not convert transaction id: %w", err)
    }

    originAmount, ok := new(big.Int).SetString(b.OriginAmount, 10)
    if !ok {
        return nil, errors.New("could not convert origin amount")
    }
    destAmount, ok := new(big.Int).SetString(b.DestAmount, 10)
    if !ok {
        return nil, errors.New("could not convert dest amount")
    }

    return &guarddb.BridgeRequest{
        TransactionID: transactionID,
        RawRequest:    req,
        Transaction: fastbridge.IFastBridgeBridgeTransaction{
            OriginChainId: b.OriginChainID,
            DestChainId:   b.DestChainID,
            OriginSender:  common.HexToAddress(b.OriginSender),
            DestRecipient: common.HexToAddress(b.DestRecipient),
            OriginToken:   common.HexToAddress(b.OriginToken),
            SendChainGas:  b.SendChainGas,
            DestToken:     common.HexToAddress(b.DestToken),
            OriginAmount:  originAmount,
            DestAmount:    destAmount,
            Deadline:      big.NewInt(b.Deadline.Unix()),
            Nonce:         big.NewInt(int64(b.OriginNonce)),
        },
    }, nil
}

func sliceToArray(slice []byte) ([32]byte, error) {
    var arr [32]byte
    if len(slice) != 32 {
        return arr, errors.New("slice is not 32 bytes long")
    }
    copy(arr[:], slice)
    return arr, nil
}