Skip to content

Commit

Permalink
fix(rfq-api): enforce max quote age for passive quotes [SLT-463] (#3392)
Browse files Browse the repository at this point in the history
* Feat: call filterQuoteAge() in handlePassiveRFQ()

* Feat: refactor into getPassiveQuote() func

* Feat: add TestGetPassiveQuote

* Config: lower max quote age
  • Loading branch information
dwasse authored Nov 18, 2024
1 parent 825af90 commit aec6dce
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 1 deletion.
2 changes: 1 addition & 1 deletion services/rfq/api/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func (c Config) GetRelayAckTimeout() time.Duration {
return c.RelayAckTimeout
}

const defaultMaxQuoteAge = 1 * time.Hour
const defaultMaxQuoteAge = 5 * time.Minute

// GetMaxQuoteAge returns the max quote age.
func (c Config) GetMaxQuoteAge() time.Duration {
Expand Down
6 changes: 6 additions & 0 deletions services/rfq/api/rest/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@ package rest
import (
"github.com/synapsecns/sanguine/services/rfq/api/config"
"github.com/synapsecns/sanguine/services/rfq/api/db"
"github.com/synapsecns/sanguine/services/rfq/api/model"
)

// FilterQuoteAge exports filterQuoteAge for testing.
func FilterQuoteAge(cfg config.Config, dbQuotes []*db.Quote) []*db.Quote {
return filterQuoteAge(cfg, dbQuotes)
}

// GetPassiveQuote exports getPassiveQuote for testing.
func GetPassiveQuote(cfg config.Config, quotes []*db.Quote, request *model.PutRFQRequest) (*model.QuoteData, error) {
return getPassiveQuote(cfg, quotes, request)
}
12 changes: 12 additions & 0 deletions services/rfq/api/rest/rfq.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/google/uuid"
"github.com/synapsecns/sanguine/core/metrics"
"github.com/synapsecns/sanguine/services/rfq/api/config"
"github.com/synapsecns/sanguine/services/rfq/api/db"
"github.com/synapsecns/sanguine/services/rfq/api/model"
"go.opentelemetry.io/otel/attribute"
Expand Down Expand Up @@ -226,6 +227,17 @@ func (r *QuoterAPIServer) handlePassiveRFQ(ctx context.Context, request *model.P
return nil, fmt.Errorf("failed to get quotes: %w", err)
}

quote, err := getPassiveQuote(r.cfg, quotes, request)
if err != nil {
return nil, fmt.Errorf("failed to get passive quote: %w", err)
}

return quote, nil
}

func getPassiveQuote(cfg config.Config, quotes []*db.Quote, request *model.PutRFQRequest) (*model.QuoteData, error) {
quotes = filterQuoteAge(cfg, quotes)

originAmount, ok := new(big.Int).SetString(request.Data.OriginAmountExact, 10)
if !ok {
return nil, errors.New("invalid origin amount exact")
Expand Down
71 changes: 71 additions & 0 deletions services/rfq/api/rest/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import (
"encoding/json"
"fmt"
"io"
"math/big"
"net/http"
"strconv"
"time"

"github.com/shopspring/decimal"
apiClient "github.com/synapsecns/sanguine/services/rfq/api/client"
"github.com/synapsecns/sanguine/services/rfq/api/db"
"github.com/synapsecns/sanguine/services/rfq/api/rest"
Expand Down Expand Up @@ -390,6 +392,75 @@ func (c *ServerSuite) TestFilterQuoteAge() {
c.Equal(quotes[1], filteredQuotes[0])
}

func (c *ServerSuite) TestGetPassiveQuote() {
userRequestAmount := big.NewInt(1_000_000)

passiveQuotes := []*db.Quote{
{
RelayerAddr: "0x1",
OriginChainID: uint64(c.originChainID),
OriginTokenAddr: originTokenAddr,
DestChainID: uint64(c.destChainID),
DestTokenAddr: destTokenAddr,
DestAmount: decimal.NewFromBigInt(new(big.Int).Sub(userRequestAmount, big.NewInt(104)), 0),
MaxOriginAmount: decimal.NewFromBigInt(userRequestAmount, 0),
FixedFee: decimal.NewFromInt(1000),
UpdatedAt: time.Now(),
},
{
RelayerAddr: "0x2",
OriginChainID: uint64(c.originChainID),
OriginTokenAddr: originTokenAddr,
DestChainID: uint64(c.destChainID),
DestTokenAddr: destTokenAddr,
DestAmount: decimal.NewFromBigInt(new(big.Int).Sub(userRequestAmount, big.NewInt(103)), 0),
MaxOriginAmount: decimal.NewFromBigInt(userRequestAmount, 0),
FixedFee: decimal.NewFromInt(1000),
UpdatedAt: time.Now().Add(-time.Minute),
},
{
RelayerAddr: "0x3",
OriginChainID: uint64(c.originChainID),
OriginTokenAddr: originTokenAddr,
DestChainID: uint64(c.destChainID),
DestTokenAddr: destTokenAddr,
DestAmount: decimal.NewFromBigInt(new(big.Int).Sub(userRequestAmount, big.NewInt(102)), 0),
MaxOriginAmount: decimal.NewFromBigInt(userRequestAmount, 0),
FixedFee: decimal.NewFromInt(1000),
UpdatedAt: time.Now().Add(-time.Minute * 15),
},
{
RelayerAddr: "0x4",
OriginChainID: uint64(c.originChainID),
OriginTokenAddr: originTokenAddr,
DestChainID: uint64(c.destChainID),
DestTokenAddr: destTokenAddr,
DestAmount: decimal.NewFromBigInt(new(big.Int).Sub(userRequestAmount, big.NewInt(101)), 0),
MaxOriginAmount: decimal.NewFromBigInt(userRequestAmount, 0),
FixedFee: decimal.NewFromInt(1000),
UpdatedAt: time.Now().Add(-time.Hour),
},
}

userQuoteReq := &model.PutRFQRequest{
Data: model.QuoteData{
OriginChainID: c.originChainID,
OriginTokenAddr: originTokenAddr,
DestChainID: c.destChainID,
DestTokenAddr: destTokenAddr,
OriginAmountExact: userRequestAmount.String(),
ExpirationWindow: 0,
},
QuoteTypes: []string{"active", "passive"},
}

// get the best passive quote; this should be the one with highest dest amount, but within the MaxQuoteAge window
quote, err := rest.GetPassiveQuote(c.cfg, passiveQuotes, userQuoteReq)
c.Require().NoError(err)
c.Assert().Equal("998897", *quote.DestAmount)
c.Assert().Equal(passiveQuotes[1].RelayerAddr, *quote.RelayerAddress)
}

func (c *ServerSuite) TestPutAck() {
c.startQuoterAPIServer()

Expand Down

0 comments on commit aec6dce

Please sign in to comment.