diff --git a/services/rfq/relayer/pricer/fee_pricer.go b/services/rfq/relayer/pricer/fee_pricer.go index 1b6449fb7a..50df3ed859 100644 --- a/services/rfq/relayer/pricer/fee_pricer.go +++ b/services/rfq/relayer/pricer/fee_pricer.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "math/big" + "strings" "time" "github.com/jellydator/ttlcache/v3" @@ -158,7 +159,8 @@ func (f *feePricer) GetTokenID(chain uint32, addr string) (string, error) { return "", fmt.Errorf("no chain config for chain %d", chain) } for tokenID, tokenConfig := range chainConfig.Tokens { - if tokenConfig.Address == addr { + // TODO: probably a better way to do this. + if strings.ToLower(tokenConfig.Address) == strings.ToLower(addr) { return tokenID, nil } } diff --git a/services/rfq/relayer/quoter/quoter.go b/services/rfq/relayer/quoter/quoter.go index 331f9956a4..1b2dae3b65 100644 --- a/services/rfq/relayer/quoter/quoter.go +++ b/services/rfq/relayer/quoter/quoter.go @@ -155,13 +155,18 @@ func (m *Manager) GenerateQuotes(ctx context.Context, chainID int, address commo var quotes []rfqAPIClient.APIQuotePutRequest for keyTokenID, itemTokenIDs := range m.quotableTokens { for _, tokenID := range itemTokenIDs { - if tokenID == destTokenID { + // TODO: probably a better way to do this. + if strings.ToLower(tokenID) == strings.ToLower(destTokenID) { originStr := strings.Split(keyTokenID, "-")[0] origin, err := strconv.Atoi(originStr) if err != nil { return nil, fmt.Errorf("error converting origin chainID: %w", err) } - fee, err := m.feePricer.GetTotalFee(ctx, uint32(origin), uint32(chainID), destTokenID) + destToken, err := m.feePricer.GetTokenID(uint32(chainID), address.Hex()) + if err != nil { + return nil, fmt.Errorf("error getting dest token ID: %w", err) + } + fee, err := m.feePricer.GetTotalFee(ctx, uint32(origin), uint32(chainID), destToken) if err != nil { return nil, fmt.Errorf("error getting total fee: %w", err) } diff --git a/services/rfq/relayer/quoter/quoter_test.go b/services/rfq/relayer/quoter/quoter_test.go index 6ec4a50b8f..779f215114 100644 --- a/services/rfq/relayer/quoter/quoter_test.go +++ b/services/rfq/relayer/quoter/quoter_test.go @@ -2,13 +2,29 @@ package quoter_test import ( "math/big" + "strconv" "github.com/ethereum/go-ethereum/common" + rfqAPIClient "github.com/synapsecns/sanguine/services/rfq/api/client" ) func (s *QuoterSuite) TestGenerateQuotes() { - s.T().Skip("Needs to be fixed or removed") - quotesReturned, err := s.manager.GenerateQuotes(s.GetTestContext(), 1, common.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"), big.NewInt(100)) + // Generate quotes for USDC on the destination chain. + balance := big.NewInt(1000_000_000) // 1000 USDC + quotes, err := s.manager.GenerateQuotes(s.GetTestContext(), int(s.destination), common.HexToAddress("0x0b2c639c533813f4aa9d7837caf62653d097ff85"), balance) s.Require().NoError(err) - s.Require().Len(quotesReturned, 1) + + // Verify the qutoes are generated as expected. + expectedQuotes := []rfqAPIClient.APIQuotePutRequest{ + { + OriginChainID: strconv.Itoa(int(s.origin)), + OriginTokenAddr: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + DestChainID: strconv.Itoa(int(s.destination)), + DestTokenAddr: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85", + DestAmount: balance.String(), + MaxOriginAmount: balance.String(), + FixedFee: "100050000", + }, + } + s.Equal(quotes, expectedQuotes) } diff --git a/services/rfq/relayer/quoter/suite_test.go b/services/rfq/relayer/quoter/suite_test.go index ecf9de54ec..b233767304 100644 --- a/services/rfq/relayer/quoter/suite_test.go +++ b/services/rfq/relayer/quoter/suite_test.go @@ -1,17 +1,28 @@ package quoter_test import ( + "math/big" "testing" + "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" + "github.com/synapsecns/sanguine/core/metrics" "github.com/synapsecns/sanguine/core/testsuite" + clientMocks "github.com/synapsecns/sanguine/ethergo/client/mocks" + fetcherMocks "github.com/synapsecns/sanguine/ethergo/submitter/mocks" + "github.com/synapsecns/sanguine/services/rfq/relayer/pricer" "github.com/synapsecns/sanguine/services/rfq/relayer/quoter" + "github.com/synapsecns/sanguine/services/rfq/relayer/relconfig" ) // Server suite is the main API server test suite. type QuoterSuite struct { *testsuite.TestSuite - manager quoter.Manager + config relconfig.Config + manager *quoter.Manager + origin uint32 + destination uint32 } // NewServerSuite creates a end-to-end test suite. @@ -22,21 +33,73 @@ func NewQuoterSuite(tb testing.TB) *QuoterSuite { } } -func (c *QuoterSuite) SetupTest() { - c.TestSuite.SetupTest() +func (s *QuoterSuite) SetupTest() { + s.TestSuite.SetupTest() // Setup - manager := quoter.Manager{} - c.manager = manager - c.manager.SetQuotableTokens(map[string][]string{ - "42161-0xaf88d065e77c8cc2239327c5edb3a432268e5831": {"1-0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "10-0x0b2c639c533813f4aa9d7837caf62653d097ff85"}, - "1-0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48": {"42161-0xaf88d065e77c8cc2239327c5edb3a432268e5831", "10-0x0b2c639c533813f4aa9d7837caf62653d097ff85"}, - "10-0x0b2c639c533813f4aa9d7837caf62653d097ff85": {"1-0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "42161-0xaf88d065e77c8cc2239327c5edb3a432268e5831"}, - // Add more mock quotableTokens if needed - }) + s.origin = 42161 + s.destination = 137 + s.config = relconfig.Config{ + Bridges: map[int]relconfig.ChainConfig{ + int(s.origin): relconfig.ChainConfig{ + Tokens: map[string]relconfig.TokenConfig{ + "USDC": relconfig.TokenConfig{ + Address: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + PriceUSD: 1, + Decimals: 6, + }, + "ETH": relconfig.TokenConfig{ + Address: "", + PriceUSD: 2000, + Decimals: 18, + }, + }, + NativeToken: "ETH", + }, + int(s.destination): relconfig.ChainConfig{ + Tokens: map[string]relconfig.TokenConfig{ + "USDC": relconfig.TokenConfig{ + Address: "0x0b2c639c533813f4aa9d7837caf62653d097ff85", + PriceUSD: 1, + Decimals: 6, + }, + "MATIC": relconfig.TokenConfig{ + Address: "", + PriceUSD: 0.5, + Decimals: 18, + }, + }, + NativeToken: "MATIC", + }, + }, + FeePricer: relconfig.FeePricerConfig{ + GasPriceCacheTTLSeconds: 60, + TokenPriceCacheTTLSeconds: 60, + OriginGasEstimate: 500000, + DestinationGasEstimate: 1000000, + }, + QuotableTokens: map[string][]string{ + "42161-0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48": {"137-0x0b2c639c533813f4aa9d7837caf62653d097ff85", "10-0x0b2c639c533813f4aa9d7837caf62653d097ff85"}, + // "1-0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48": {"42161-0xaf88d065e77c8cc2239327c5edb3a432268e5831", "10-0x0b2c639c533813f4aa9d7837caf62653d097ff85"}, + // "10-0x0b2c639c533813f4aa9d7837caf62653d097ff85": {"1-0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "42161-0xaf88d065e77c8cc2239327c5edb3a432268e5831"}, + }, + } + + // Build a FeePricer with mock gas price. + clientFetcher := new(fetcherMocks.ClientFetcher) + client := new(clientMocks.EVM) + currentHeader := &types.Header{BaseFee: big.NewInt(100_000_000_000)} // 100 gwei + client.On(testsuite.GetFunctionName(client.HeaderByNumber), mock.Anything, mock.Anything).Return(currentHeader, nil) + clientFetcher.On(testsuite.GetFunctionName(clientFetcher.GetClient), mock.Anything, mock.Anything).Twice().Return(client, nil) + feePricer := pricer.NewFeePricer(s.config.FeePricer, s.config.Bridges, clientFetcher) + go func() { feePricer.Start(s.GetTestContext()) }() + + var err error + s.manager, err = quoter.NewQuoterManager(metrics.NewNullHandler(), s.config.QuotableTokens, nil, "", nil, feePricer) + s.NoError(err) } -func (c *QuoterSuite) SetupSuite() { - c.TestSuite.SetupSuite() +func (s *QuoterSuite) SetupSuite() { + s.TestSuite.SetupSuite() } // TestConfigSuite runs the integration test suite.