Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: simulate swap as part of quotes #547

Merged
merged 9 commits into from
Nov 4, 2024
Merged

feat: simulate swap as part of quotes #547

merged 9 commits into from
Nov 4, 2024

Conversation

p0mvn
Copy link
Member

@p0mvn p0mvn commented Nov 3, 2024

https://osmosis-network.slack.com/archives/C079LNKRUCB/p1730651766930679

Implementing MsgSwapExactAmountIn quote estimation logic directly into SQS.

This will allow to wire SQS integration into Polaris. Ref: https://github.com/polaris-portal/polaris/pull/1011/files

Currently, in Polaris, as we get a quote, we also want to know the pricing for it.

To avoid round trips, we can have an optional query parameter to request SQS to run simulation logic immediately after estimating a quote.

Support

  • "out given in", direct (non-split) quotes
  • controlled by the optiona simulationAddress and simulationSlippageTolerance query parameters
  • backwards compatible

Testing

  • Unit tests with mock
  • Integration test

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a PriceMsgs method for improved gas estimation and fee calculation in message simulations.
    • Added a QuoteSimulator interface and a SimulateQuote method for simulating quotes in token swaps.
    • Enhanced GetQuoteRequest to handle simulation parameters, including SimulatorAddress and SlippageToleranceMultiplier.
    • Added a mock implementation of the QuoteSimulator for testing purposes.
  • Improvements

    • Updated RouterHandler to utilize the new quote simulation functionality.
    • Added a SetQuotePriceInfo method to manage quote price data effectively.
    • Enhanced server initialization to integrate the new quote simulator.
  • Bug Fixes

    • Improved error handling in the quote simulation process to ensure proper validation and responses.

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced PriceMsgs method for improved gas estimation and fee calculation in message simulation.
    • Added QuoteSimulator interface and SimulateQuote method for simulating quotes with slippage tolerance.
    • Enhanced GetOptimalQuote method to support quote simulation based on a specified address and slippage tolerance.
    • New fields in GetQuoteRequest to manage simulation parameters.
    • Added optional price_info parameter to QuoteExactAmountInResponse for additional pricing details.
  • Bug Fixes

    • Adjusted error handling for quote simulation to ensure proper responses.
  • Documentation

    • Updated Swagger documentation to include new parameters for quote simulation.
  • Tests

    • Added tests for simulation slippage tolerance and updated existing tests to accommodate new parameters.

@p0mvn p0mvn added the A:backport/v27.x Backport to v27.x branch label Nov 3, 2024
Copy link
Contributor

coderabbitai bot commented Nov 3, 2024

Walkthrough

The pull request introduces several enhancements across multiple files, primarily focusing on the MsgSimulator interface and its interaction with transaction processing. A new method, PriceMsgs, is added to simulate message execution, returning adjusted gas and fee coin. Additionally, a QuoteSimulator interface is introduced, which includes a method for simulating quotes. Modifications to various structs and methods allow for better handling of simulation parameters, including slippage tolerance and simulator addresses. Mock implementations are also provided for testing, ensuring comprehensive coverage of the new functionalities.

Changes

File Path Change Summary
domain/cosmos/tx/msg_simulator.go Added method PriceMsgs to MsgSimulator interface and implemented it in txGasCalulator.
domain/mocks/msg_simulator_mock.go Added PriceMsgs method to MsgSimulatorMock struct to simulate pricing for messages.
domain/quote_simulator.go Introduced QuoteSimulator interface and QuotePriceInfo struct with SimulateQuote method.
domain/router.go Added SetQuotePriceInfo method to Quote interface.
quotesimulator/quote_simulator.go Implemented quoteSimulator struct with SimulateQuote method.
router/delivery/http/router_handler.go Added QuoteSimulator field to RouterHandler and updated GetOptimalQuote method for quote simulation.
router/types/get_quote_request.go Added SimulatorAddress and SlippageToleranceMultiplier fields to GetQuoteRequest struct.
router/usecase/quote_out_given_in.go Added PriceInfo field and SetQuotePriceInfo method to quoteExactAmountIn struct.
app/sidecar_query_server.go Updated NewSideCarQueryServer to create quoteSimulator instance and modified handler initialization.
domain/mocks/quote_simulator_mock.go Introduced QuoteSimulatorMock struct implementing QuoteSimulator interface.
domain/mocks/quote_mock.go Added MockQuote struct implementing Quote interface with custom function behavior.
domain/mocks/route_mock.go Added GetAmountIn and GetAmountOut methods to RouteMock struct.
router/usecase/routertesting/parsing/quote_amount_in_response_simulated.json New JSON file for simulated quote response.
tests/quote.py Updated run_quote_test method to include simulation parameters.
tests/sqs_service.py Updated get_exact_amount_in_quote method to accept simulation parameters.
tests/test_router_quote_out_given_in.py Added test_simulation_slippage_tolerance method to validate simulation slippage tolerance functionality.
docs/docs.go Added new query parameters for the /router/quote endpoint in Swagger documentation.
docs/swagger.json Updated Swagger API documentation with new parameters for quote simulation.
docs/swagger.yaml Modified Swagger definitions to include new parameters for quote simulation.
domain/mocks/passthrough_grpc_client_mock.go Updated GetChainGRPCClient method to return grpc.ClientConnInterface.
domain/passthrough/passthrough_grpc_client.go Updated GetChainGRPCClient method and client initialization with new gRPC client.

Possibly related PRs

🐰 In the land of code where rabbits play,
New features hop in, brightening the day!
With prices and quotes, all snug and tight,
Simulations abound, oh what a delight!
So let’s cheer for the changes, both big and small,
For every little tweak makes our system stand tall! 🌟


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Outside diff range and nitpick comments (10)
domain/quote_simulator.go (3)

10-18: Documentation improvements needed.

The interface documentation has some issues:

  1. There's a typo in "Retursn" in the error conditions section
  2. The contract could be more specific about error conditions and parameter constraints

Consider applying these improvements:

 // QuoteSimulator simulates a quote and returns the gas adjusted amount and the fee coin.
 type QuoteSimulator interface {
     // SimulateQuote simulates a quote and returns the gas adjusted amount and the fee coin.
     // CONTRACT:
     // - Only direct (non-split) quotes are supported.
-    // Retursn error if:
-    // - Simulator address does not have enough funds to pay for the quote.
+    // Parameters:
+    // - slippageToleranceMultiplier: must be > 0 and <= 1
+    // - simulatorAddress: must be a valid bech32 address
+    // Returns error if:
+    // - Simulator address does not have enough funds to pay for the quote
+    // - Quote is a split quote
+    // - Invalid parameters are provided
     SimulateQuote(ctx context.Context, quote Quote, slippageToleranceMultiplier osmomath.Dec, simulatorAddress string) (uint64, sdk.Coin, error)
 }

20-23: Consider adding field documentation and validation.

While the struct is well-structured, it could benefit from field documentation and validation methods.

Consider these improvements:

 type QuotePriceInfo struct {
+    // AdjustedGasUsed represents the gas amount adjusted after simulation
     AdjustedGasUsed uint64   `json:"adjusted_gas_used"`
+    // FeeCoin represents the fee amount in specific denomination
     FeeCoin         sdk.Coin `json:"fee_coin"`
 }
+
+// Validate performs basic validation of QuotePriceInfo fields
+func (q QuotePriceInfo) Validate() error {
+    if q.AdjustedGasUsed == 0 {
+        return fmt.Errorf("adjusted gas used cannot be zero")
+    }
+    if err := q.FeeCoin.Validate(); err != nil {
+        return fmt.Errorf("invalid fee coin: %w", err)
+    }
+    return nil
+}

10-18: Consider interface versioning strategy.

As this is a new interface that might evolve over time, consider adding a version suffix (e.g., QuoteSimulatorV1) or documenting the versioning strategy for future compatibility.

router/types/get_quote_request.go (1)

76-105: Simplify function structure by eliminating nested else block.

While the validation logic is correct, the function's readability can be improved by eliminating the nested else block.

Consider this refactored version:

 func validateSimulationParams(swapMethod domain.TokenSwapMethod, simulatorAddress string, slippageToleranceStr string) (osmomath.Dec, error) {
+	if simulatorAddress == "" {
+		if slippageToleranceStr != "" {
+			return osmomath.Dec{}, fmt.Errorf("slippage tolerance is not supported without simulator address")
+		}
+		return osmomath.Dec{}, nil
+	}
+
+	_, err := sdk.ValAddressFromBech32(simulatorAddress)
+	if err != nil {
+		return osmomath.Dec{}, fmt.Errorf("simulator address is not valid: %w", err)
+	}
+
+	if swapMethod == domain.TokenSwapMethodExactIn {
+		return osmomath.Dec{}, fmt.Errorf("only 'out given in' swap method is supported for simulation")
+	}
+
+	if slippageToleranceStr == "" {
+		return osmomath.Dec{}, fmt.Errorf("slippage tolerance is required for simulation")
+	}
+
+	slippageTolerance, err := osmomath.NewDecFromStr(slippageToleranceStr)
+	if err != nil {
+		return osmomath.Dec{}, fmt.Errorf("slippage tolerance is not valid: %w", err)
+	}
+
+	return slippageTolerance, nil
domain/cosmos/tx/msg_simulator.go (1)

147-165: Consider optimizing the implementation and improving error handling.

While the implementation is functional, there are several potential improvements:

  1. Consider combining the simulation and fee calculation into a single operation if the underlying APIs support it, to reduce network calls.
  2. The gas adjustment factor (1.05) is hardcoded in SimulateMsgs. Consider making it configurable.
  3. The error handling could be more specific by wrapping errors with context.
  4. Consider validating input parameters, especially for nil checks on dependencies.

Example improvement for error handling:

 func (c *txGasCalulator) PriceMsgs(ctx context.Context, txfeesClient txfeestypes.QueryClient, encodingConfig cosmosclient.TxConfig, account *authtypes.BaseAccount, chainID string, msg ...sdk.Msg) (uint64, sdk.Coin, error) {
+    if txfeesClient == nil || encodingConfig == nil || account == nil {
+        return 0, sdk.Coin{}, fmt.Errorf("invalid dependencies: txfeesClient=%v, encodingConfig=%v, account=%v", txfeesClient, encodingConfig, account)
+    }
+
     _, gasAdjusted, err := c.SimulateMsgs(
         encodingConfig,
         account,
         chainID,
         msg,
     )
     if err != nil {
-        return 0, sdk.Coin{}, err
+        return 0, sdk.Coin{}, fmt.Errorf("failed to simulate messages: %w", err)
     }

     feecoin, err := CalculateFeeCoin(ctx, txfeesClient, gasAdjusted)
     if err != nil {
-        return 0, sdk.Coin{}, err
+        return 0, sdk.Coin{}, fmt.Errorf("failed to calculate fee coin: %w", err)
     }

     return gasAdjusted, feecoin, nil
 }
domain/router.go (1)

75-76: Add documentation for the new interface method.

While the method name is descriptive, please add a comment explaining its purpose, expected behavior, and relationship to other Quote interface methods. This will help maintainers understand the design intent.

router/delivery/http/router_handler.go (1)

148-165: Consider extracting simulation logic to a separate method.

The simulation logic adds significant complexity to the GetOptimalQuote method. Consider extracting it to improve readability and maintainability.

Example refactor:

+func (a *RouterHandler) simulateQuoteIfApplicable(ctx context.Context, c echo.Context, quote domain.Quote, req types.GetQuoteRequest) error {
+    if !req.SingleRoute || req.SimulatorAddress == "" || req.SwapMethod() != domain.TokenSwapMethodExactIn {
+        return nil
+    }
+
+    value := c.Get("simulatorAddress")
+    simulatorAddress, ok := value.(string)
+    if !ok {
+        return c.JSON(http.StatusBadRequest, domain.ResponseError{Message: "invalid simulator address"})
+    }
+
+    gasUsed, feeCoin, err := a.QuoteSimulator.SimulateQuote(ctx, quote, req.SlippageToleranceMultiplier, simulatorAddress)
+    if err != nil {
+        return c.JSON(domain.GetStatusCode(err), domain.ResponseError{Message: err.Error()})
+    }
+
+    quote.SetQuotePriceInfo(&domain.QuotePriceInfo{
+        AdjustedGasUsed: gasUsed,
+        FeeCoin:         feeCoin,
+    })
+
+    return nil
+}

Then in GetOptimalQuote:

-    // Simulate quote if applicable.
-    // Note: only single routes (non-splits) are supported for simulation.
-    // Additionally, the functionality is triggerred by the user providing a simulator address.
-    // Only "out given in" swap method is supported for simulation. Thus, we also check for tokenOutDenom being set.
-    if req.SingleRoute && req.SimulatorAddress != "" && req.SwapMethod() == domain.TokenSwapMethodExactIn {
-        simulatorAddress := c.Get("simulatorAddress").(string)
-
-        gasUsed, feeCoin, err := a.QuoteSimulator.SimulateQuote(ctx, quote, req.SlippageToleranceMultiplier, simulatorAddress)
-        if err != nil {
-            return c.JSON(domain.GetStatusCode(err), domain.ResponseError{Message: err.Error()})
-        }
-
-        // Set the quote price info.
-        quote.SetQuotePriceInfo(&domain.QuotePriceInfo{
-            AdjustedGasUsed: gasUsed,
-            FeeCoin:         feeCoin,
-        })
-    }
+    if err := a.simulateQuoteIfApplicable(ctx, c, quote, req); err != nil {
+        return err
+    }
🧰 Tools
🪛 GitHub Check: Run linter

[failure] 153-153:
type assertion must be checked (forcetypeassert)

quotesimulator/quote_simulator.go (3)

43-44: Validate that poolsInRoute is not empty.

The code assumes that poolsInRoute contains at least one pool, but there's no check to ensure this. If it's empty, the subsequent loop will be skipped, which might not be the intended behavior. Add a validation to handle this case.

Apply this diff to add the validation:

 poolsInRoute := route[0].GetPools()

+if len(poolsInRoute) == 0 {
+    return 0, sdk.Coin{}, fmt.Errorf("route must contain at least one pool")
+}

 poolManagerRoute := make([]poolmanagertypes.SwapAmountInRoute, len(poolsInRoute))

65-67: Wrap error when retrieving account information for better context.

To aid in debugging and provide more informative error messages, consider wrapping the error with additional context when the account retrieval fails.

Apply this diff to wrap the error:

 if err != nil {
-    return 0, sdk.Coin{}, err
+    return 0, sdk.Coin{}, fmt.Errorf("failed to get account for simulator address: %w", err)
 }

70-72: Wrap error when pricing messages for better context.

Similarly, wrapping the error returned by PriceMsgs with context will make it easier to trace issues during execution.

Apply this diff to wrap the error:

 if err != nil {
-    return 0, sdk.Coin{}, err
+    return 0, sdk.Coin{}, fmt.Errorf("failed to price messages: %w", err)
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 662aac4 and 10eca0f.

📒 Files selected for processing (8)
  • domain/cosmos/tx/msg_simulator.go (3 hunks)
  • domain/mocks/msg_simulator_mock.go (2 hunks)
  • domain/quote_simulator.go (1 hunks)
  • domain/router.go (1 hunks)
  • quotesimulator/quote_simulator.go (1 hunks)
  • router/delivery/http/router_handler.go (3 hunks)
  • router/types/get_quote_request.go (3 hunks)
  • router/usecase/quote_out_given_in.go (2 hunks)
🧰 Additional context used
🪛 GitHub Check: Run linter
router/delivery/http/router_handler.go

[failure] 153-153:
type assertion must be checked (forcetypeassert)

router/usecase/quote_out_given_in.go

[failure] 46-46:
structtag: struct field priceInfo has json tag but is not exported (govet)

🔇 Additional comments (9)
domain/quote_simulator.go (1)

1-8: LGTM! Clean package structure and imports.

The package declaration and imports are well-organized and follow Go best practices.

domain/mocks/msg_simulator_mock.go (1)

34-41: LGTM: Function pointer declaration follows mock patterns.

The PriceMsgsFn function pointer is well-defined with appropriate parameter and return types for gas estimation and fee calculation.

router/types/get_quote_request.go (1)

15-23: LGTM! Well-structured struct definition.

The new fields SimulatorAddress and SlippageToleranceMultiplier are properly typed and follow Go naming conventions.

router/usecase/quote_out_given_in.go (1)

157-160: Verify Quote interface implementation.

Let's ensure the Quote interface is properly implemented and all related code is using the correct field name.

✅ Verification successful

Let me gather more information about the Quote interface definition and its implementations.


Quote interface implementation is correct and consistent

The verification confirms:

  • The Quote interface correctly defines SetQuotePriceInfo method in domain/router.go
  • The quoteExactAmountIn struct properly implements the interface with matching method signature
  • The field priceInfo is consistently used in the struct and its setter
  • The implementation is being used correctly in router_handler.go
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify Quote interface implementation and field usage

# Test 1: Check Quote interface definition
echo "Checking Quote interface definition..."
ast-grep --pattern 'type Quote interface {
  $$$
  SetQuotePriceInfo($_) $_
  $$$
}'

# Test 2: Check usage of PriceInfo field
echo "Checking PriceInfo field usage..."
rg -A 2 'PriceInfo.*domain\.QuotePriceInfo'

# Test 3: Check for any remaining lowercase priceInfo usage
echo "Checking for lowercase priceInfo usage..."
rg -A 2 'priceInfo.*domain\.QuotePriceInfo'

Length of output: 1052


Script:

#!/bin/bash
# Check Quote interface definition and all its methods
echo "Checking Quote interface definition..."
ast-grep --pattern 'type Quote interface {
  $$$
}'

# Check all implementations of SetQuotePriceInfo
echo "Checking SetQuotePriceInfo implementations..."
ast-grep --pattern 'func ($_ $_) SetQuotePriceInfo($_ *domain.QuotePriceInfo) {
  $$$
}'

# Check if priceInfo field is consistently used across all quote types
echo "Checking priceInfo field declarations..."
ast-grep --pattern 'type $_ struct {
  $$$
  priceInfo *domain.QuotePriceInfo
  $$$
}'

Length of output: 2858

domain/cosmos/tx/msg_simulator.go (3)

41-51: LGTM! Well-documented interface addition.

The new PriceMsgs method is a good addition that consolidates gas estimation and fee calculation into a single call, which aligns well with the quote simulation requirements.


93-99: LGTM! Good refactoring of gas and fee calculation.

The changes improve code organization by using the consolidated PriceMsgs method, maintaining proper error handling and setting gas limit before fee amount.


147-165: Verify integration with quote simulation system.

Since this code is part of the swap simulation feature, please ensure:

  1. The gas estimation accounts for all operations in the swap
  2. The fee calculation considers the correct denomination for the chain
  3. The error cases are properly handled by the quote simulation system
✅ Verification successful

Integration with quote simulation system verified successfully

The code is properly integrated with the quote simulation system:

  1. Gas estimation is handled correctly:

    • PriceMsgs is used by quoteSimulator.SimulateQuote to calculate gas and fees
    • It internally uses SimulateMsgs to get accurate gas estimation for all operations
  2. Fee calculation is properly implemented:

    • CalculateFeeCoin is called with the adjusted gas amount
    • The fee calculation uses the chain-specific txfeesClient
  3. Error handling is comprehensive:

    • Errors from both simulation and fee calculation are properly propagated
    • The quote simulator properly handles these errors in its implementation
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for quote simulation code that uses this new PriceMsgs method
rg -A 5 "PriceMsgs.*QuoteSimulator"

# Look for any existing swap simulation tests
fd -e go -x rg -l "TestSwap.*Simulation"

Length of output: 80


Script:

#!/bin/bash
# Let's search for any usage of PriceMsgs method
rg -A 5 "PriceMsgs\("

# Look for quote simulation related code
rg -A 10 "QuoteSimulator"

# Search for swap simulation tests with a broader pattern
rg -l "Test.*Swap.*Simulation"

# Look for the interface definition of MsgSimulator
ast-grep --pattern 'type MsgSimulator interface {
  $$$
}'

Length of output: 9506

router/delivery/http/router_handler.go (2)

22-25: LGTM! Clean struct field addition.

The new QuoteSimulator field is well-placed and properly typed.


39-44: LGTM! Constructor updated consistently.

The QuoteSimulator parameter is properly integrated into the constructor, maintaining the existing pattern.

domain/mocks/msg_simulator_mock.go Show resolved Hide resolved
router/types/get_quote_request.go Outdated Show resolved Hide resolved
router/types/get_quote_request.go Show resolved Hide resolved
router/usecase/quote_out_given_in.go Outdated Show resolved Hide resolved
router/usecase/quote_out_given_in.go Outdated Show resolved Hide resolved
domain/router.go Show resolved Hide resolved
router/delivery/http/router_handler.go Outdated Show resolved Hide resolved
quotesimulator/quote_simulator.go Outdated Show resolved Hide resolved
quotesimulator/quote_simulator.go Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (4)
domain/mocks/quote_simulator_mock.go (2)

11-13: Add godoc comments for the struct and its field.

While the implementation is correct, adding documentation would improve code maintainability and help other developers understand the purpose of this mock.

+// QuoteSimulatorMock provides a mock implementation of domain.QuoteSimulator interface for testing.
 type QuoteSimulatorMock struct {
+    // SimulateQuoteFn allows tests to define custom behavior for SimulateQuote method.
     SimulateQuoteFn func(ctx context.Context, quote domain.Quote, slippageToleranceMultiplier math.LegacyDec, simulatorAddress string) (uint64, types.Coin, error)
 }

15-21: Consider enhancing error handling and validation.

While the implementation is functional, consider these improvements:

  1. Add parameter validation for non-nil context and valid slippage tolerance.
  2. Make the panic message more descriptive for debugging.
 func (q *QuoteSimulatorMock) SimulateQuote(ctx context.Context, quote domain.Quote, slippageToleranceMultiplier math.LegacyDec, simulatorAddress string) (uint64, types.Coin, error) {
+    if ctx == nil {
+        panic("context cannot be nil")
+    }
+    if slippageToleranceMultiplier.IsNegative() {
+        panic("slippage tolerance multiplier cannot be negative")
+    }
     if q.SimulateQuoteFn != nil {
         return q.SimulateQuoteFn(ctx, quote, slippageToleranceMultiplier, simulatorAddress)
     }
-    panic("SimulateQuoteFn not implemented")
+    panic("QuoteSimulatorMock: SimulateQuoteFn not implemented")
 }
router/types/get_quote_request.go (2)

65-71: Improve error handling for simulation parameters.

Consider wrapping the error from validateSimulationParams with additional context to help with debugging:

-	slippageToleranceDec, err := validateSimulationParams(r.SwapMethod(), simulatorAddress, slippageToleranceStr)
-	if err != nil {
-		return err
-	}
+	slippageToleranceDec, err := validateSimulationParams(r.SwapMethod(), simulatorAddress, slippageToleranceStr)
+	if err != nil {
+		return fmt.Errorf("failed to validate simulation parameters: %w", err)
+	}

101-108: Simplify the else block logic.

The else block can be simplified by returning early, which improves readability:

-	} else {
-		if slippageToleranceStr != "" {
-			return osmomath.Dec{}, fmt.Errorf("slippage tolerance is not supported without simulator address")
-		}
-	}
-
-	return osmomath.Dec{}, nil
+	}
+	
+	if slippageToleranceStr != "" {
+		return osmomath.Dec{}, fmt.Errorf("slippage tolerance is not supported without simulator address")
+	}
+	
+	return osmomath.Dec{}, nil
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 10eca0f and 331f7e3.

⛔ Files ignored due to path filters (3)
  • router/delivery/http/router_handler_test.go is excluded by !**/*_test.go
  • router/types/export_test.go is excluded by !**/*_test.go
  • router/types/get_quote_request_test.go is excluded by !**/*_test.go
📒 Files selected for processing (10)
  • app/sidecar_query_server.go (3 hunks)
  • domain/cosmos/tx/msg_simulator.go (3 hunks)
  • domain/mocks/msg_simulator_mock.go (2 hunks)
  • domain/mocks/quote_simulator_mock.go (1 hunks)
  • domain/quote_simulator.go (1 hunks)
  • domain/router.go (1 hunks)
  • quotesimulator/quote_simulator.go (1 hunks)
  • router/delivery/http/router_handler.go (3 hunks)
  • router/types/get_quote_request.go (3 hunks)
  • router/usecase/quote_out_given_in.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • domain/cosmos/tx/msg_simulator.go
  • domain/quote_simulator.go
  • domain/router.go
  • quotesimulator/quote_simulator.go
  • router/delivery/http/router_handler.go
  • router/usecase/quote_out_given_in.go
🔇 Additional comments (8)
domain/mocks/quote_simulator_mock.go (3)

1-9: LGTM! Package and imports are well-organized.

The package declaration and imports follow Go best practices and contain all necessary dependencies.


23-23: LGTM! Proper interface compliance check.

The compile-time interface compliance check is correctly implemented using Go's type assertion pattern.


11-23: Verify interface completeness.

Let's ensure this mock implements all methods of the QuoteSimulator interface correctly.

✅ Verification successful

Based on the results, I can now generate the final response since I have found both the interface definition and its usage in the codebase:

Interface implementation is correct and complete

The mock correctly implements the only method required by the QuoteSimulator interface:

  • SimulateQuote(ctx context.Context, quote Quote, slippageToleranceMultiplier osmomath.Dec, simulatorAddress string) (uint64, sdk.Coin, error)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that QuoteSimulator interface is fully implemented
# Expected: Find the interface definition and compare with mock implementation

# Search for QuoteSimulator interface definition
echo "Searching for QuoteSimulator interface definition..."
ast-grep --pattern 'type QuoteSimulator interface {
  $$$
}'

# Search for any other methods that might be required
echo "Searching for any method calls on QuoteSimulator..."
rg -A 2 'QuoteSimulator\.' 

Length of output: 1701

domain/mocks/msg_simulator_mock.go (2)

33-41: LGTM: Function pointer field is well-defined.

The PriceMsgsFn field follows the established mock pattern and has appropriate parameter types for gas and fee calculation.


72-82: Type inconsistency issue was previously identified.

The message parameter type inconsistency between the method signature and the function pointer was already noted in a previous review.

Implementation pattern is correct.

The implementation follows the standard mock pattern with appropriate nil checks and panic behavior.

router/types/get_quote_request.go (1)

15-23: LGTM: Struct changes are well-designed.

The new fields SimulatorAddress and SlippageToleranceMultiplier are appropriately typed and follow Go naming conventions.

app/sidecar_query_server.go (2)

23-32: LGTM: Import statements are well-organized.

The new imports are properly organized and necessary for implementing the quote simulation feature.


218-228: Verify quote simulator integration with router handler.

The quote simulator is properly initialized with all required dependencies and correctly integrated with the router handler. However, let's verify the integration points.

Run the following script to verify the integration:

✅ Verification successful

Quote simulator integration is properly implemented and verified

The verification confirms that:

  • QuoteSimulator is correctly integrated in the RouterHandler with proper initialization and dependency injection
  • The interface is well-defined with a clear contract for SimulateQuote method
  • The quote simulation is properly used in GetOptimalQuote endpoint with appropriate error handling
  • The integration is covered by unit tests that verify the simulation functionality
  • The simulation is only triggered under specific conditions:
    • Single route (non-split) quotes
    • Exact-in swap method
    • When simulator address is provided
    • With proper slippage tolerance handling

The implementation matches the intended design and is working as expected.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify quote simulator integration with router handler
# Test 1: Check if RouterHandler correctly uses the quote simulator
ast-grep --pattern 'func NewRouterHandler($_, $_, $_, quoteSimulator $_)'

# Test 2: Check if quote simulator is used in route handling
rg -A 5 'quoteSimulator\.' 

Length of output: 104


Script:

#!/bin/bash
# Let's try a more specific search for the router handler implementation
# Search for NewRouterHandler function definition
ast-grep --pattern 'func NewRouterHandler($$$)'

# Search for files containing RouterHandler
fd -e go RouterHandler --exec cat {}

# Search for quote simulator usage patterns
rg -l "quotesimulator\." 

# Search for any files containing both router and quote simulator references
rg -l "RouterHandler|quotesimulator"

Length of output: 1874


Script:

#!/bin/bash
# Let's check how QuoteSimulator is used in the router handler
rg -A 5 "QuoteSimulator\." router/delivery/http/router_handler.go

# Check the interface definition to verify required methods
rg "type QuoteSimulator interface" -A 10

# Check the actual usage in GetOptimalQuote and other relevant methods
ast-grep --pattern 'func ($_ *RouterHandler) GetOptimalQuote($$$)'

# Check test coverage
cat router/delivery/http/router_handler_test.go

Length of output: 22222

app/sidecar_query_server.go Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (5)
domain/mocks/quote_mock.go (1)

12-15: Add documentation for the mock struct and its fields.

Consider adding documentation to explain the purpose of the mock and how to use its configurable functions:

+// MockQuote implements domain.Quote interface for testing purposes.
 type MockQuote struct {
+    // GetAmountInFunc allows customizing the GetAmountIn behavior
     GetAmountInFunc func() types.Coin
+    // GetRouteFunc allows customizing the GetRoute behavior
     GetRouteFunc    func() []domain.SplitRoute
 }
domain/mocks/route_mock.go (1)

88-102: LGTM with minor formatting suggestion.

The implementations of GetAmountIn and GetAmountOut correctly follow the established mock pattern. However, there's inconsistent spacing around the panic statements.

Consider standardizing the spacing around panic statements:

 func (r *RouteMock) GetAmountIn() math.Int {
 	if r.GetAmountInFunc != nil {
 		return r.GetAmountInFunc()
 	}
-
 	panic("unimplemented")
 }

 func (r *RouteMock) GetAmountOut() math.Int {
 	if r.GetAmountOutFunc != nil {
 		return r.GetAmountOutFunc()
 	}
-
 	panic("unimplemented")
 }
router/delivery/http/router_handler.go (1)

154-157: Consider adding metrics for simulation failures.

The error handling for simulation failures could benefit from metrics to track failure rates and types.

Consider adding metrics:

+const (
+    metricQuoteSimulationFailure = "quote_simulation_failure"
+)

 if err != nil {
+    // Record simulation failure metric
+    metrics.IncrCounter(metricQuoteSimulationFailure, 1)
     return c.JSON(domain.GetStatusCode(err), domain.ResponseError{Message: err.Error()})
 }
quotesimulator/quote_simulator.go (2)

27-35: Add documentation comment for NewQuoteSimulator.

Consider adding a GoDoc comment for the NewQuoteSimulator function to enhance code readability and provide clarity on its purpose and usage.


37-80: Add method documentation for SimulateQuote.

Including a detailed GoDoc comment for SimulateQuote will improve maintainability and help other developers understand its functionality, parameters, and return values.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 331f7e3 and 7c42583.

⛔ Files ignored due to path filters (5)
  • domain/cosmos/tx/msg_simulator_test.go is excluded by !**/*_test.go
  • quotesimulator/quote_simulator_test.go is excluded by !**/*_test.go
  • router/delivery/http/router_handler_test.go is excluded by !**/*_test.go
  • router/types/export_test.go is excluded by !**/*_test.go
  • router/types/get_quote_request_test.go is excluded by !**/*_test.go
📒 Files selected for processing (13)
  • app/sidecar_query_server.go (3 hunks)
  • domain/cosmos/tx/msg_simulator.go (3 hunks)
  • domain/mocks/msg_simulator_mock.go (2 hunks)
  • domain/mocks/quote_mock.go (1 hunks)
  • domain/mocks/quote_simulator_mock.go (1 hunks)
  • domain/mocks/route_mock.go (2 hunks)
  • domain/quote_simulator.go (1 hunks)
  • domain/router.go (1 hunks)
  • quotesimulator/quote_simulator.go (1 hunks)
  • router/delivery/http/router_handler.go (3 hunks)
  • router/types/get_quote_request.go (3 hunks)
  • router/usecase/quote_out_given_in.go (2 hunks)
  • router/usecase/routertesting/parsing/quote_amount_in_response_simulated.json (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • router/usecase/routertesting/parsing/quote_amount_in_response_simulated.json
🚧 Files skipped from review as they are similar to previous changes (5)
  • domain/mocks/quote_simulator_mock.go
  • domain/quote_simulator.go
  • domain/router.go
  • router/types/get_quote_request.go
  • router/usecase/quote_out_given_in.go
🔇 Additional comments (17)
domain/mocks/quote_mock.go (3)

1-10: LGTM! Package declaration and imports are clean and well-organized.


70-70: LGTM! Interface compliance check is properly implemented.


1-70: Verify interface method coverage.

Let's ensure that all methods from the domain.Quote interface are implemented in this mock.

✅ Verification successful

Let me try a different search pattern to find the implemented methods since the previous one didn't yield results.


Let me try one final verification for the interface compliance check with a corrected regex.


Let me try one last time with a simpler pattern to find the interface compliance check.


Based on the results gathered, I can now provide the final response:

Mock implementation correctly covers all interface methods

Comparing the interface definition with the mock implementation:

  • All 9 methods from the Quote interface are implemented: GetAmountIn, GetAmountOut, GetRoute, GetEffectiveFee, GetPriceImpact, GetInBaseOutQuoteSpotPrice, PrepareResult, SetQuotePriceInfo, and String
  • The mock includes function fields for customizing behavior (GetAmountInFunc, GetRouteFunc)
  • Unimplemented methods properly panic with "unimplemented" message
  • Interface compliance is enforced with the type assertion var _ domain.Quote = &MockQuote{}
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that all domain.Quote interface methods are implemented in MockQuote

# Search for the Quote interface definition
echo "Quote interface definition:"
ast-grep --pattern 'type Quote interface {
  $$$
}'

# Search for implemented methods in MockQuote
echo -e "\nImplemented methods in MockQuote:"
ast-grep --pattern 'func \(m \*MockQuote\) $_\(' 

Length of output: 1456


Script:

#!/bin/bash
# Description: Verify that all domain.Quote interface methods are implemented in MockQuote

# Search for the Quote interface definition (already found above)
echo "Implemented methods in MockQuote (using ripgrep):"
rg "func \(m \*MockQuote\)" -A 1

# Also verify the interface compliance check
echo -e "\nInterface compliance check:"
rg "var _.+ domain\.Quote.+= &MockQuote{}"

Length of output: 1731


Script:

#!/bin/bash
# Search for the interface compliance check with fixed regex
echo "Interface compliance check:"
rg "var _.* domain\.Quote.* = &MockQuote{}"

Length of output: 268


Script:

#!/bin/bash
# Search for the interface compliance check with simpler pattern
echo "Interface compliance check:"
rg "var _ domain.Quote = &MockQuote{}"

Length of output: 253

domain/mocks/msg_simulator_mock.go (2)

34-41: LGTM! Function field declaration is well-structured.

The PriceMsgsFn field declaration is correct with appropriate parameter types and return values.


72-82: Type inconsistency issue still present.

The previously identified issue regarding message parameter type inconsistency remains valid. The method should use sdk.Msg instead of the generic interface constraint to maintain consistency with other methods in the mock.

domain/mocks/route_mock.go (2)

20-22: LGTM: New function pointers follow established patterns.

The new function pointer fields GetAmountInFunc and GetAmountOutFunc maintain consistency with the existing mock implementation pattern and use appropriate types for amount calculations.


103-105: Verify SplitRoute interface implementation completeness.

The mock now implements both domain.Route and domain.SplitRoute interfaces. Let's ensure all required methods are implemented.

✅ Verification successful

Let me gather more information about the implemented methods in RouteMock specifically.


Interface implementations are complete and correct

The RouteMock correctly implements all required methods:

  • All 7 methods from the Route interface: ContainsGeneralizedCosmWasmPool, GetPools, CalculateTokenOutByTokenIn, GetTokenOutDenom, GetTokenInDenom, PrepareResultPools, and String
  • Both methods from the SplitRoute interface: GetAmountIn and GetAmountOut (note that SplitRoute embeds Route, so it inherits all Route methods)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that all required methods of SplitRoute interface are implemented

# Test: Search for the SplitRoute interface definition
echo "Searching for SplitRoute interface definition..."
ast-grep --pattern 'type SplitRoute interface {
  $$$
}'

# Test: Compare with implemented methods
echo "Listing all implemented methods in RouteMock..."
ast-grep --pattern 'func (r *RouteMock) $_($_) $_ {
  $$$
}'

Length of output: 527


Script:

#!/bin/bash
# Search for the Route interface definition first
echo "Searching for Route interface definition..."
ast-grep --pattern 'type Route interface {
  $$$
}'

# Search for all method implementations in RouteMock with ripgrep for better context
echo "Searching for RouteMock method implementations..."
rg "func \(r \*RouteMock\)" -A 1

Length of output: 3936

app/sidecar_query_server.go (2)

219-227: Add error handling for quote simulator initialization.

The past review comment about adding error handling for quote simulator initialization is still valid. This ensures proper error propagation during server startup.


228-228: Verify router handler implementation.

The router handler is now initialized with the quote simulator. Please ensure that the RouterHandler implementation properly utilizes this parameter for quote simulations.

router/delivery/http/router_handler.go (4)

22-25: LGTM: Clean struct field addition.

The QuoteSimulator field is properly integrated into the RouterHandler struct, maintaining a logical field ordering.


39-44: LGTM: Clean constructor update.

The constructor properly initializes the new QuoteSimulator field while maintaining the existing initialization pattern.


152-152: ⚠️ Potential issue

Fix unsafe variable assignment.

Direct assignment of req.SimulatorAddress could be unsafe if the field is not properly validated.

This issue was previously flagged in past reviews. The same fix applies here:

-simulatorAddress := req.SimulatorAddress
+// Validate simulator address if provided
+if req.SimulatorAddress != "" {
+    if err := domain.ValidateAddress(req.SimulatorAddress); err != nil {
+        return c.JSON(http.StatusBadRequest, domain.ResponseError{Message: "invalid simulator address"})
+    }
+}
+simulatorAddress := req.SimulatorAddress

159-163: Consider validating simulation results.

The quote price info is set without validating the simulation results.

Let's verify if there are any validation requirements for gasUsed and feeCoin:

quotesimulator/quote_simulator.go (1)

1-82: Overall implementation is solid.

The code is well-structured and follows best practices.

domain/cosmos/tx/msg_simulator.go (3)

41-51: Addition of PriceMsgs method to MsgSimulator interface is well-defined

The new method PriceMsgs enhances the MsgSimulator interface by consolidating gas estimation and fee calculation, which improves functionality and code clarity.


93-93: Refactored BuildTx method improves efficiency

By using PriceMsgs to retrieve both the adjusted gas and fee coin, the BuildTx method is streamlined, enhancing code maintainability and reducing redundancy.

Also applies to: 98-99


147-166: Implementation of PriceMsgs method correctly reuses existing logic

The PriceMsgs function correctly simulates messages to calculate adjusted gas and fee coin, utilizing SimulateMsgs and CalculateFeeCoin with proper error handling.

domain/mocks/quote_mock.go Outdated Show resolved Hide resolved
router/delivery/http/router_handler.go Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (11)
tests/quote_response.py (2)

44-46: Add type hints and validation for price_info.

Consider adding type hints and validation to ensure price_info has the expected structure. This will help catch issues earlier and improve code maintainability.

-    def __init__(self, amount_in, amount_out, route, effective_fee, price_impact, in_base_out_quote_spot_price, price_info):
+    def __init__(self, amount_in, amount_out, route, effective_fee, price_impact, in_base_out_quote_spot_price, price_info: Optional[Dict[str, Any]] = None):
         self.amount_in = Coin(**amount_in)
         self.amount_out = int(amount_out)
         self.route = [Route(**r) for r in route]
         self.effective_fee = Decimal(effective_fee)
         self.price_impact = Decimal(price_impact)
         self.in_base_out_quote_spot_price = Decimal(in_base_out_quote_spot_price)
 
         if price_info:
+            # Add validation for required fields
+            if not isinstance(price_info, dict):
+                raise ValueError("price_info must be a dictionary")
             self.price_info = price_info

Line range hint 1-1: Improve decimal module import.

The current import style could lead to namespace pollution. Consider using a more specific import.

-from decimal import *
+from decimal import Decimal
🧰 Tools
🪛 Ruff

40-40: Decimal may be undefined, or defined from star imports

(F405)


41-41: Decimal may be undefined, or defined from star imports

(F405)


42-42: Decimal may be undefined, or defined from star imports

(F405)

tests/sqs_service.py (3)

98-98: Update docstring to document new parameters.

The method signature has been updated with simulation-related parameters, but the docstring doesn't describe them. Consider updating it to include:

  • simulator_address: Description of its purpose and format
  • simulation_slippage_tolerance: Description of its purpose and expected value range

111-112: Consider conditional parameter inclusion.

Empty simulation parameters are currently always included in the request URL. Consider adding them only when they have non-empty values to keep the URLs clean and consistent with the API's behavior.

         params = {
             "tokenIn": denom_in,
             "tokenOutDenom": denom_out,
             "humanDenoms": human_denoms,
-            "singleRoute": singleRoute,
-            "simulatorAddress": simulator_address,
-            "simulationSlippageTolerance": simulation_slippage_tolerance,
+            "singleRoute": singleRoute
         }
+        if simulator_address:
+            params["simulatorAddress"] = simulator_address
+        if simulation_slippage_tolerance:
+            params["simulationSlippageTolerance"] = simulation_slippage_tolerance

Test coverage needed for simulation parameters

The verification shows that while the get_exact_amount_in_quote method and run_quote_test helper have been updated to support simulation parameters, there are no actual test cases exercising these new parameters. The codebase needs:

  • Test cases that verify behavior with simulator_address parameter
  • Test cases that verify behavior with simulation_slippage_tolerance parameter
  • Test cases that verify the interaction between both parameters
🔗 Analysis chain

Line range hint 98-112: Verify test coverage for simulation parameters.

The changes add simulation-related parameters to the quote API. Let's verify that these parameters are properly tested.


Let me check the test coverage in tests/quote.py to verify if the simulation parameters are properly tested.


Let me check the test implementation to see how these parameters are being used in the actual test cases.


Let me check if there are any actual test cases that use these simulation parameters.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for test files using get_exact_amount_in_quote with simulation parameters
echo "Searching for test files using simulation parameters..."
rg -l "get_exact_amount_in_quote.*simulator_address.*simulation_slippage_tolerance" tests/

# Search for test files using get_exact_amount_in_quote that might need updates
echo "Searching for other test files that might need updates..."
rg -l "get_exact_amount_in_quote" tests/ | grep -v "sqs_service.py"

Length of output: 455


Script:

#!/bin/bash
# Check the test coverage for simulation parameters in quote.py
echo "Checking test coverage in quote.py..."
rg -A 10 "get_exact_amount_in_quote" tests/quote.py

Length of output: 834


Script:

#!/bin/bash
# Search for test cases using these parameters
echo "Searching for test cases using simulation parameters..."
rg -B 5 -A 15 "simulator_address.*simulation_slippage_tolerance" tests/quote.py

Length of output: 1941


Script:

#!/bin/bash
# Search for test cases that call run_quote_test with simulation parameters
echo "Searching for test cases using run_quote_test..."
rg -B 5 "run_quote_test.*simulator.*slippage" tests/quote.py

# Also search for test function definitions that might test simulation
echo "Searching for test functions related to simulation..."
rg "def test.*simul" tests/quote.py

Length of output: 732

tests/test_router_quote_out_given_in.py (3)

291-308: Enhance test coverage for simulation slippage tolerance.

While the test provides basic validation, consider enhancing it with:

  1. Multiple test cases using @pytest.mark.parametrize to test different slippage tolerance values
  2. Validation of the actual slippage behavior in the simulation
  3. Error cases (e.g., negative slippage, zero slippage)
  4. Documentation of why specific values were chosen (1000000uosmo, 1.2 slippage)

Example enhancement:

@pytest.mark.parametrize("slippage_tolerance,expected_result", [
    (1.2, True),    # Normal case
    (0, False),     # Zero slippage
    (-0.1, False),  # Negative slippage
    (100, True),    # High slippage
])
def test_simulation_slippage_tolerance(self, environment_url, slippage_tolerance, expected_result):
    """
    Test simulation slippage tolerance with various scenarios:
    - Normal case: 1.2% slippage tolerance
    - Edge case: 0% slippage tolerance
    - Invalid case: Negative slippage tolerance
    - Extreme case: Very high slippage tolerance
    
    Amount chosen (1000000uosmo) represents a significant trade to ensure
    observable price impact.
    """
    token_in_coin = "1000000uosmo"  # ~$1000 USD at typical OSMO prices
    denom_out = "uion"
    fillbot_address = "osmo10s3vlv40h64qs2p98yal9w0tpm4r30uyg6ceux"
    
    quote = ExactAmountInQuote.run_quote_test(
        environment_url, 
        token_in_coin, 
        denom_out, 
        False, 
        False, 
        EXPECTED_LATENCY_UPPER_BOUND_MS,
        fillbot_address,
        slippage_tolerance
    )
    
    assert quote is not None
    assert quote.price_info is not None
    # Add assertions to validate slippage behavior
    assert (quote.price_impact <= slippage_tolerance) == expected_result
🧰 Tools
🪛 Ruff

304-304: ExactAmountInQuote may be undefined, or defined from star imports

(F405)


304-304: Consider explicit imports instead of star imports.

The static analysis tool indicates that ExactAmountInQuote may be imported via star import. For better code maintainability and clarity, consider making the import explicit.

- from quote import *
+ from quote import ExactAmountInQuote
🧰 Tools
🪛 Ruff

304-304: ExactAmountInQuote may be undefined, or defined from star imports

(F405)


292-294: Align docstring with existing test patterns.

Consider enhancing the docstring to match the detail level of other test methods in the file. Include:

  • Test scenarios and edge cases covered
  • Explanation of chosen test values
  • Expected behavior and validation criteria

Example:

"""
This test validates that the simulation slippage tolerance is working as expected.

Test scenarios:
- Validates quote simulation with specified slippage tolerance
- Ensures price info is correctly populated
- Verifies simulation with fillbot address

Parameters:
- token_in_coin: 1000000uosmo (~$1000 USD) chosen to ensure observable price impact
- slippage_tolerance: 1.2% represents a typical trading scenario
- fillbot_address: Known address with sufficient balance for simulation

Expected behavior:
- Quote simulation succeeds with given slippage tolerance
- Price info reflects the simulation results
- Transaction would be executable within slippage bounds
"""
tests/quote.py (3)

120-120: Consider explicit import of QuoteExactAmountInResponse.

While the method signature changes look good, the return type QuoteExactAmountInResponse is imported via star import. Consider making this import explicit for better code maintainability.

-from quote_response import *
+from quote_response import QuoteExactAmountInResponse
🧰 Tools
🪛 Ruff

120-120: QuoteExactAmountInResponse may be undefined, or defined from star imports

(F405)


133-133: Replace lambda with def function for better readability.

The lambda function should be rewritten as a regular function definition for improved code clarity and maintainability.

-        service_call = lambda: conftest.SERVICE_MAP[environment_url].get_exact_amount_in_quote(token_in, token_out, human_denoms, single_route, simulator_address, simulation_slippage_tolerance)        
+        def service_call():
+            return conftest.SERVICE_MAP[environment_url].get_exact_amount_in_quote(
+                token_in,
+                token_out,
+                human_denoms,
+                single_route,
+                simulator_address,
+                simulation_slippage_tolerance
+            )
🧰 Tools
🪛 Ruff

133-133: Do not assign a lambda expression, use a def

Rewrite service_call as a def

(E731)


Test coverage for simulation parameters needs improvement

The current test coverage for simulation parameters is insufficient:

  • Only one happy path test case exists with a fixed slippage tolerance of 1.2
  • No validation of simulator_address format or edge cases
  • No error scenarios tested (empty address, invalid slippage values)
  • No assertions specific to simulation behavior

Key areas needing test coverage:

  • Test with invalid simulator addresses
  • Test with boundary values for slippage tolerance (0, negative, very large values)
  • Test with empty/invalid slippage tolerance values
  • Verify simulation-specific response fields and behavior
🔗 Analysis chain

Line range hint 120-133: Add test cases for simulation parameters.

The new parameters simulator_address and simulation_slippage_tolerance should have dedicated test cases to verify:

  • Valid values processing
  • Edge cases (empty strings, invalid values)
  • Error scenarios

Would you like me to help generate comprehensive test cases for these parameters?

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if there are any test cases for the new parameters
rg -l "simulation_slippage_tolerance.*=.*['\"].*['\"]" tests/

Length of output: 103


Script:

#!/bin/bash
# Check the actual test cases in the files
rg -B 5 -A 5 "simulation_slippage_tolerance.*=.*['\"].*['\"]" tests/

Length of output: 1518


Script:

#!/bin/bash
# Look for test functions that might be testing these parameters
rg -B 5 -A 5 "def test.*simul" tests/

Length of output: 990


Script:

#!/bin/bash
# Check if there are any assertions related to these parameters
rg "assert.*simul" tests/

Length of output: 27


Script:

#!/bin/bash
# Let's see the complete implementation of the simulation slippage tolerance test
rg -B 10 -A 20 "def test_simulation_slippage_tolerance" tests/

Length of output: 2621

🧰 Tools
🪛 Ruff

120-120: QuoteExactAmountInResponse may be undefined, or defined from star imports

(F405)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 7c42583 and 3d9e378.

📒 Files selected for processing (4)
  • tests/quote.py (2 hunks)
  • tests/quote_response.py (1 hunks)
  • tests/sqs_service.py (2 hunks)
  • tests/test_router_quote_out_given_in.py (1 hunks)
🧰 Additional context used
🪛 Ruff
tests/quote.py

120-120: QuoteExactAmountInResponse may be undefined, or defined from star imports

(F405)


133-133: Do not assign a lambda expression, use a def

Rewrite service_call as a def

(E731)

tests/quote_response.py

40-40: Decimal may be undefined, or defined from star imports

(F405)


41-41: Decimal may be undefined, or defined from star imports

(F405)


42-42: Decimal may be undefined, or defined from star imports

(F405)

tests/test_router_quote_out_given_in.py

304-304: ExactAmountInQuote may be undefined, or defined from star imports

(F405)

🔇 Additional comments (1)
tests/quote_response.py (1)

36-36: Verify backward compatibility of constructor changes.

The addition of the price_info parameter without a default value could break existing test cases.

✅ Verification successful

No compatibility concerns with constructor changes

The code inspection shows that all instances of QuoteExactAmountInResponse are instantiated using **response or **res pattern, which unpacks the API response directly. This pattern automatically handles new fields in the response without requiring code changes, making it backward compatible. The API will provide the price_info field, and the unpacking will handle it seamlessly.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for existing instantiations of QuoteExactAmountInResponse to verify they've been updated
rg "QuoteExactAmountInResponse\(" --type python

Length of output: 80


Script:

#!/bin/bash
# Search for existing instantiations without file type flag since rg doesn't recognize python
rg "QuoteExactAmountInResponse\(" -A 2

Length of output: 735


Script:

#!/bin/bash
# Check the response dictionary construction to see if price_info is included
rg -B 5 "QuoteExactAmountInResponse\(\*\*" 

Length of output: 2290


Script:

#!/bin/bash
# Check the response JSON structure from the API to verify price_info field
rg -B 10 '"price_info".*:' tests/

Length of output: 35

type QuoteSimulator interface {
// SimulateQuote simulates a quote and returns the gas adjusted amount and the fee coin.
// CONTRACT:
// - Only direct (non-split) quotes are supported.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why only non-split routes are supported? Nit: typo in Retursn

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Splits require a separate message and increase the complexity drastically.

My ultimate goal is to integrate SQS into Polaris at the lowest cost, and then we can iterate on improvements, including adding splits

slippageBound := tokenOutAmt.ToLegacyDec().Mul(slippageToleranceMultiplier).TruncateInt()

// Create the swap message
swapMsg := &poolmanagertypes.MsgSwapExactAmountIn{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can it be that result can vary depending of swap method ( poolmanagertypes.MsgSwapExactAmountIn vs poolmanagertypes.MsgSwapExactAmountOut ) for some cases such as imbalanced pools, etc?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"in given out" is not supported in simulation for the same reason I mentioned here:
#547 (comment)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Outside diff range and nitpick comments (10)
router/types/get_quote_request.go (1)

73-109: Consider structural improvements for better maintainability.

A few suggestions to enhance the code:

  1. Define constants for magic values
  2. Simplify the else block by early return
  3. Add more specific error messages

Consider these improvements:

+const (
+    // MaxSlippageTolerance represents the maximum allowed slippage (50%)
+    MaxSlippageTolerance = "0.5"
+)

 func validateSimulationParams(swapMethod domain.TokenSwapMethod, simulatorAddress string, slippageToleranceStr string) (osmomath.Dec, error) {
+    // Return early if no simulation is requested
+    if simulatorAddress == "" {
+        if slippageToleranceStr != "" {
+            return osmomath.Dec{}, fmt.Errorf("slippage tolerance cannot be set without simulator address")
+        }
+        return osmomath.Dec{}, nil
+    }
+
     // Rest of the function...
-    } else {
-        if slippageToleranceStr != "" {
-            return osmomath.Dec{}, fmt.Errorf("slippage tolerance is not supported without simulator address")
-        }
-    }
-
-    return osmomath.Dec{}, nil
 }
domain/passthrough/passthrough_grpc_client.go (1)

Line range hint 204-231: Consider architectural improvements for better maintainability.

  1. The paginateRequest helper could be moved to a shared utility package as it's a generic pagination implementation that could be reused across other services.

  2. Consider adding context timeout management to prevent long-running queries from blocking resources.

Example implementation for context timeout:

func paginateRequest(ctx context.Context, fetchCoinsFn func(context.Context, *query.PageRequest) (*query.PageResponse, sdk.Coins, error)) (sdk.Coins, error) {
    // Add timeout to context
    ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
    defer cancel()
    
    // ... rest of the implementation
}
tests/test_router_quote_out_given_in.py (1)

Line range hint 4-13: Consider replacing star imports with explicit imports.

While star imports work in test files, explicit imports would make the code more maintainable by clearly showing which symbols are used from each module.

-from sqs_service import *
-from quote import *
-from quote_response import *
-from rand_util import *
-from e2e_math import *
-from decimal import *
-from constants import *
-from chain_service import *
-from util import *
-from route import *
+from sqs_service import SERVICE_MAP
+from quote import ExactAmountInQuote, ExactAmountOutQuote, Quote
+from quote_response import QuoteExactAmountInResponse
+from rand_util import id_from_coin, id_from_swap_pair
+from e2e_math import relative_error
+from decimal import Decimal
+from constants import USDC, UOSMO, UOSMO_USDC_POOL_ID
+from chain_service import Coin
+from util import skip_imbalanced_pool_test_if_imbalanced
+from route import Route
🧰 Tools
🪛 Ruff

286-286: relative_error may be undefined, or defined from star imports

(F405)


307-307: ExactAmountInQuote may be undefined, or defined from star imports

(F405)

docs/swagger.yaml (2)

145-146: Enhance the math.Int definition with detailed schema properties.

The current definition only specifies it as an object type without any properties. Consider adding detailed schema properties to better document the structure and constraints of math.Int.

   math.Int:
     type: object
+    properties:
+      value:
+        type: string
+        description: String representation of the integer value
+    description: Represents an arbitrary-precision integer

410-419: Consider using number type for slippageToleranceMultiplier.

The slippageToleranceMultiplier parameter represents a numerical value but is currently defined as a string type. For better type safety and validation, consider changing it to a number type with appropriate constraints.

       - description: Slippage tolerance multiplier for the simulation. If simulatorAddress
           is provided, this must be provided.
         in: query
         name: slippageToleranceMultiplier
-        type: string
+        type: number
+        format: double
+        minimum: 0
+        maximum: 1
tests/quote.py (1)

120-120: LGTM! Consider explicit imports.

The method signature changes appropriately support simulation parameters. However, consider replacing star imports with explicit imports to improve code maintainability and avoid potential naming conflicts.

Replace:

from quote_response import *

with:

from quote_response import QuoteExactAmountInResponse
🧰 Tools
🪛 Ruff

120-120: QuoteExactAmountInResponse may be undefined, or defined from star imports

(F405)

docs/swagger.json (1)

520-520: Consider enhancing the math.Int definition.

While the basic object definition is sufficient, consider adding a description field to clarify that this represents a big integer type used for token amounts.

Apply this enhancement:

         "math.Int": {
-            "type": "object"
+            "type": "object",
+            "description": "Represents a big integer type used for token amounts"
         },

Also applies to: 669-671

quotesimulator/quote_simulator.go (3)

60-65: Abstract swap message creation to support different swap types.

Currently, the swap message is hardcoded to MsgSwapExactAmountIn. Abstracting the message creation would allow support for MsgSwapExactAmountOut and enhance the flexibility of the simulator.

Consider refactoring the swap message creation like this:

-var swapMsg := &poolmanagertypes.MsgSwapExactAmountIn{
-	Sender:            simulatorAddress,
-	Routes:            poolManagerRoute,
-	TokenIn:           quote.GetAmountIn(),
-	TokenOutMinAmount: slippageBound,
-}
+var swapMsg sdk.Msg
+if quote.IsExactIn() {
+	swapMsg = &poolmanagertypes.MsgSwapExactAmountIn{
+		Sender:            simulatorAddress,
+		Routes:            poolManagerRoute,
+		TokenIn:           quote.GetAmountIn(),
+		TokenOutMinAmount: slippageBound,
+	}
+} else {
+	swapMsg = &poolmanagertypes.MsgSwapExactAmountOut{
+		Sender:           simulatorAddress,
+		Routes:           poolManagerRoute,
+		TokenOut:         quote.GetAmountOut(),
+		TokenInMaxAmount: slippageBound,
+	}
+}

38-80: Enhance error messages for better troubleshooting.

Some error messages could be more descriptive to aid in debugging, especially in a multi-component system where errors may propagate.

Consider updating error messages like so:

 if len(route) != 1 {
-	return 0, sdk.Coin{}, fmt.Errorf("route length must be 1, got %d", len(route))
+	return 0, sdk.Coin{}, fmt.Errorf("invalid route length in quote simulation: expected 1, got %d", len(route))
 }

...

 if err != nil {
-	return 0, sdk.Coin{}, err
+	return 0, sdk.Coin{}, fmt.Errorf("failed to get account for simulator address %s: %w", simulatorAddress, err)
 }

1-82: Add unit tests for quoteSimulator methods to ensure reliability.

Implementing unit tests for SimulateQuote will help catch potential issues early and ensure that future changes do not break existing functionality.

Would you like assistance in creating a suite of unit tests for this implementation?

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 3d9e378 and 96cf62b.

⛔ Files ignored due to path filters (1)
  • quotesimulator/quote_simulator_test.go is excluded by !**/*_test.go
📒 Files selected for processing (11)
  • docs/docs.go (3 hunks)
  • docs/swagger.json (3 hunks)
  • docs/swagger.yaml (3 hunks)
  • domain/mocks/passthrough_grpc_client_mock.go (1 hunks)
  • domain/mocks/quote_mock.go (1 hunks)
  • domain/passthrough/passthrough_grpc_client.go (5 hunks)
  • quotesimulator/quote_simulator.go (1 hunks)
  • router/delivery/http/router_handler.go (5 hunks)
  • router/types/get_quote_request.go (3 hunks)
  • tests/quote.py (2 hunks)
  • tests/test_router_quote_out_given_in.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • domain/mocks/quote_mock.go
  • router/delivery/http/router_handler.go
🧰 Additional context used
🪛 Ruff
tests/quote.py

120-120: QuoteExactAmountInResponse may be undefined, or defined from star imports

(F405)


133-133: Do not assign a lambda expression, use a def

Rewrite service_call as a def

(E731)


138-138: QuoteExactAmountInResponse may be undefined, or defined from star imports

(F405)

tests/test_router_quote_out_given_in.py

307-307: ExactAmountInQuote may be undefined, or defined from star imports

(F405)

🔇 Additional comments (13)
domain/mocks/passthrough_grpc_client_mock.go (1)

23-23: LGTM: Good interface-based design!

The change from concrete type *grpc.ClientConn to interface type grpc.ClientConnInterface is a good practice as it:

  • Makes the mock more flexible and loosely coupled
  • Follows interface-based design principles
  • Aligns with the actual implementation
router/types/get_quote_request.go (2)

15-23: LGTM! Well-structured struct definition.

The new fields are appropriately typed and follow Go naming conventions.


59-69: LGTM! Clean parameter handling.

The implementation properly retrieves and validates simulation parameters before setting them to the struct fields.

domain/passthrough/passthrough_grpc_client.go (4)

42-42: LGTM! Good interface design.

The change to grpc.ClientConnInterface improves abstraction and testability by depending on interfaces rather than concrete implementations.


59-59: LGTM! Consistent interface usage.

The struct field type change maintains consistency with the interface changes.


198-200: LGTM! Clean implementation.

The getter implementation is straightforward and correctly returns the interface.


14-14: Verify observability coverage with polarisgrpc.

The switch from OpenTelemetry to polarisgrpc might affect observability. Please ensure that polarisgrpc provides equivalent instrumentation capabilities for monitoring and tracing.

✅ Verification successful

Let me check the actual implementation to verify if the instrumentation is properly handled.


OpenTelemetry instrumentation is properly maintained

The polarisgrpc package maintains OpenTelemetry instrumentation through otelgrpc.NewClientHandler(). The implementation in delivery/grpc/grpc.go shows that the gRPC client is configured with the OpenTelemetry stats handler, ensuring observability is not compromised.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if polarisgrpc package includes instrumentation
rg -A 5 "instrumentation|telemetry|metrics|tracing" "delivery/grpc"

Length of output: 721


Script:

#!/bin/bash
# Check the NewClient implementation in polarisgrpc
ast-grep --pattern 'func NewClient($$$) $_ {
  $$$
}'  delivery/grpc/grpc.go

Length of output: 1153

tests/test_router_quote_out_given_in.py (1)

301-304: Verify fillbot configuration and simulation parameters.

The test assumes the fillbot address has sufficient OSMO tokens. Let's verify this assumption and check how simulation parameters are handled.

✅ Verification successful

Based on the results, I can generate the final response as we have sufficient information about the fillbot address usage and simulation parameters.

Fillbot address and simulation parameters are properly configured for testing

The fillbot address is only used in test scenarios and is properly passed through the stack:

  • The address is only referenced in tests/test_router_quote_out_given_in.py
  • The simulation parameters are correctly propagated through the service layer in tests/sqs_service.py
  • The slippage tolerance value of 0.8 is used specifically for testing purposes and doesn't conflict with any other configurations
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify fillbot configuration and simulation parameter handling

# Test 1: Check if fillbot address is configured in environment files
echo "Checking fillbot configuration..."
rg -l "osmo10s3vlv40h64qs2p98yal9w0tpm4r30uyg6ceux"

# Test 2: Check how simulation parameters are handled in the codebase
echo "Checking simulation parameter handling..."
rg -A 5 "simulation_slippage_tolerance"

# Test 3: Check for any hardcoded slippage values that might conflict
echo "Checking for hardcoded slippage values..."
rg -A 2 "slippage.*=.*0\.8"

Length of output: 3685

tests/quote.py (2)

133-133: LGTM! Service call correctly handles simulation parameters.

The lambda function appropriately forwards the simulation parameters to the quote service.

🧰 Tools
🪛 Ruff

133-133: Do not assign a lambda expression, use a def

Rewrite service_call as a def

(E731)


138-144: LGTM! Clean handling of optional price info.

The response handling properly manages the optional price info field, maintaining backward compatibility while supporting the new simulation features.

🧰 Tools
🪛 Ruff

138-138: QuoteExactAmountInResponse may be undefined, or defined from star imports

(F405)

docs/swagger.json (1)

293-304: LGTM! Well-documented query parameters for quote simulation.

The new parameters are clearly documented with appropriate descriptions and dependency rules.

docs/docs.go (2)

302-313: LGTM! Well-documented query parameters for quote simulation.

The new query parameters are clearly documented with their dependencies and requirements properly specified.


529-529: LGTM! Appropriate schema update for Cosmos SDK's math.Int type.

The schema reference has been correctly updated to use math.Int, and the new definition has been added with the appropriate type.

Also applies to: 678-680

Comment on lines +23 to 25
func (p *PassthroughGRPCClientMock) GetChainGRPCClient() grpc.ClientConnInterface {
panic("unimplemented")
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Implement the mock method instead of panicking.

The current implementation panics with "unimplemented", which could cause tests to fail unexpectedly. Consider implementing a proper mock that returns a mock gRPC connection.

Here's a suggested implementation:

 func (p *PassthroughGRPCClientMock) GetChainGRPCClient() grpc.ClientConnInterface {
-    panic("unimplemented")
+    // Return a no-op implementation that satisfies the interface
+    return &grpc.ClientConn{}
 }

Alternatively, you could add a callback field to allow tests to provide their own implementation:

 type PassthroughGRPCClientMock struct {
+    MockGetChainGRPCClientCb func() grpc.ClientConnInterface
     MockAllBalancesCb        func(ctx context.Context, address string) (sdk.Coins, error)
     // ... other fields ...
 }

 func (p *PassthroughGRPCClientMock) GetChainGRPCClient() grpc.ClientConnInterface {
-    panic("unimplemented")
+    if p.MockGetChainGRPCClientCb != nil {
+        return p.MockGetChainGRPCClientCb()
+    }
+    return &grpc.ClientConn{}
 }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +97 to +99
if slippageTolerance.LTE(osmomath.ZeroDec()) {
return osmomath.Dec{}, fmt.Errorf("slippage tolerance must be greater than 0")
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add upper bound for slippage tolerance.

Currently, the code only checks if slippage tolerance is greater than 0, which could allow extremely high values that might be dangerous in production. Consider adding an upper bound check.

Apply this diff:

-		if slippageTolerance.LTE(osmomath.ZeroDec()) {
-			return osmomath.Dec{}, fmt.Errorf("slippage tolerance must be greater than 0")
+		// Common practice is to limit slippage to 50% max
+		maxSlippage := osmomath.NewDec(50).Quo(osmomath.NewDec(100))
+		if slippageTolerance.LTE(osmomath.ZeroDec()) || slippageTolerance.GT(maxSlippage) {
+			return osmomath.Dec{}, fmt.Errorf("slippage tolerance must be between 0 and 0.5 (50%%)")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if slippageTolerance.LTE(osmomath.ZeroDec()) {
return osmomath.Dec{}, fmt.Errorf("slippage tolerance must be greater than 0")
}
// Common practice is to limit slippage to 50% max
maxSlippage := osmomath.NewDec(50).Quo(osmomath.NewDec(100))
if slippageTolerance.LTE(osmomath.ZeroDec()) || slippageTolerance.GT(maxSlippage) {
return osmomath.Dec{}, fmt.Errorf("slippage tolerance must be between 0 and 0.5 (50%%)")
}

grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithStatsHandler(otelgrpc.NewClientHandler()),
)
grpcClient, err := polarisgrpc.NewClient(grpcURI)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Security concern: Insecure gRPC connections detected

The verification reveals that the current implementation uses insecure credentials (insecure.NewCredentials()) without proper TLS configuration. This is consistent across multiple gRPC client implementations in the codebase. While connection timeouts are handled in various parts of the system, the simplified client creation with polarisgrpc.NewClient needs to be updated to:

  • Replace insecure credentials with proper TLS configuration
  • Explicitly configure connection timeouts
  • Consider adding retry policies for improved reliability

Locations requiring attention:

  • delivery/grpc/grpc.go: Currently using insecure.NewCredentials()
  • app/sidecar_query_server.go: Also using insecure transport
  • pools/usecase/pools_usecase.go: Same security concern
🔗 Analysis chain

Verify security and reliability features of polarisgrpc.

The simplified client creation removes explicit transport credentials. Please ensure that polarisgrpc.NewClient properly handles:

  1. Secure connections (TLS)
  2. Connection timeouts
  3. Retry policies
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check polarisgrpc client configuration
rg -A 10 "NewClient.*func" "delivery/grpc"

# Check for security configurations
rg "TLS|Certificate|Credential" "delivery/grpc"

# Check for timeout and retry configurations
rg "timeout|retry|backoff" "delivery/grpc"

Length of output: 756


Script:

#!/bin/bash
# Let's check the actual implementation of NewClient in polarisgrpc
fd "polaris.*grpc" --type f --exec cat {}

# Also check for any configuration or options being set
rg -A 5 "WithTransportCredentials|WithTimeout|WithRetry" 

Length of output: 2390

Comment on lines +291 to +311
def test_simulation_slippage_tolerance(self, environment_url):
"""
This test validates that the simulation slippage tolerance is working as expected.
"""
token_in_coin = "1000000uosmo"
denom_out = "uion"

expected_status_code = 200

# Fillbot address and slippage tolerance
# We choose fillbot address because we expect it to have at least one OSMO.
fillbot_address = "osmo10s3vlv40h64qs2p98yal9w0tpm4r30uyg6ceux"
# Note: relaxed
simulation_slippage_tolerance = 0.8

# Run the quote test
quote = ExactAmountInQuote.run_quote_test(environment_url, token_in_coin, denom_out, False, True, EXPECTED_LATENCY_UPPER_BOUND_MS, expected_status_code, fillbot_address, simulation_slippage_tolerance)

# Validate that the price info is set to something
assert quote is not None
assert quote.price_info is not None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance test coverage and document high slippage tolerance.

The test has several areas for improvement:

  1. The slippage tolerance of 80% is unusually high and should be documented with a clear explanation of why such a relaxed value is used for testing.
  2. The test lacks comprehensive assertions - it only checks if the quote and price_info are not None.
  3. There's inconsistent indentation at line 306.

Consider enhancing the test with these changes:

 def test_simulation_slippage_tolerance(self, environment_url):
     """
     This test validates that the simulation slippage tolerance is working as expected.
+    
+    Note: The high slippage tolerance of 80% is used only for testing purposes
+    to ensure the simulation works even in extreme conditions. In production,
+    much lower values should be used.
     """
     token_in_coin = "1000000uosmo"
     denom_out = "uion"

     expected_status_code = 200

     # Fillbot address and slippage tolerance
     # We choose fillbot address because we expect it to have at least one OSMO.
     fillbot_address = "osmo10s3vlv40h64qs2p98yal9w0tpm4r30uyg6ceux"
-    # Note: relaxed
     simulation_slippage_tolerance = 0.8

-            # Run the quote test
+    # Run the quote test
     quote = ExactAmountInQuote.run_quote_test(
         environment_url,
         token_in_coin,
         denom_out,
         False,
         True,
         EXPECTED_LATENCY_UPPER_BOUND_MS,
         expected_status_code,
         fillbot_address,
         simulation_slippage_tolerance
     )

     # Validate that the price info is set to something
     assert quote is not None
     assert quote.price_info is not None
+    # Additional assertions to validate simulation behavior
+    assert quote.amount_in.denom == "uosmo"
+    assert quote.amount_in.amount == 1000000
+    assert quote.amount_out > 0
+    assert "simulation_slippage_tolerance" in quote.price_info
+    assert float(quote.price_info["simulation_slippage_tolerance"]) == simulation_slippage_tolerance
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def test_simulation_slippage_tolerance(self, environment_url):
"""
This test validates that the simulation slippage tolerance is working as expected.
"""
token_in_coin = "1000000uosmo"
denom_out = "uion"
expected_status_code = 200
# Fillbot address and slippage tolerance
# We choose fillbot address because we expect it to have at least one OSMO.
fillbot_address = "osmo10s3vlv40h64qs2p98yal9w0tpm4r30uyg6ceux"
# Note: relaxed
simulation_slippage_tolerance = 0.8
# Run the quote test
quote = ExactAmountInQuote.run_quote_test(environment_url, token_in_coin, denom_out, False, True, EXPECTED_LATENCY_UPPER_BOUND_MS, expected_status_code, fillbot_address, simulation_slippage_tolerance)
# Validate that the price info is set to something
assert quote is not None
assert quote.price_info is not None
def test_simulation_slippage_tolerance(self, environment_url):
"""
This test validates that the simulation slippage tolerance is working as expected.
Note: The high slippage tolerance of 80% is used only for testing purposes
to ensure the simulation works even in extreme conditions. In production,
much lower values should be used.
"""
token_in_coin = "1000000uosmo"
denom_out = "uion"
expected_status_code = 200
# Fillbot address and slippage tolerance
# We choose fillbot address because we expect it to have at least one OSMO.
fillbot_address = "osmo10s3vlv40h64qs2p98yal9w0tpm4r30uyg6ceux"
simulation_slippage_tolerance = 0.8
# Run the quote test
quote = ExactAmountInQuote.run_quote_test(
environment_url,
token_in_coin,
denom_out,
False,
True,
EXPECTED_LATENCY_UPPER_BOUND_MS,
expected_status_code,
fillbot_address,
simulation_slippage_tolerance
)
# Validate that the price info is set to something
assert quote is not None
assert quote.price_info is not None
# Additional assertions to validate simulation behavior
assert quote.amount_in.denom == "uosmo"
assert quote.amount_in.amount == 1000000
assert quote.amount_out > 0
assert "simulation_slippage_tolerance" in quote.price_info
assert float(quote.price_info["simulation_slippage_tolerance"]) == simulation_slippage_tolerance
🧰 Tools
🪛 Ruff

307-307: ExactAmountInQuote may be undefined, or defined from star imports

(F405)

}

// SimulateQuote implements domain.QuoteSimulator
func (q *quoteSimulator) SimulateQuote(ctx context.Context, quote domain.Quote, slippageToleranceMultiplier osmomath.Dec, simulatorAddress string) (uint64, sdk.Coin, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add validation for simulatorAddress to ensure it's not empty or invalid.

The simulatorAddress is used to retrieve the account and set the sender in the swap message. If it's empty or invalid, it could lead to errors when querying the account or during message execution.

Apply this diff to add validation for simulatorAddress:

 func (q *quoteSimulator) SimulateQuote(ctx context.Context, quote domain.Quote, slippageToleranceMultiplier osmomath.Dec, simulatorAddress string) (uint64, sdk.Coin, error) {
+	if simulatorAddress == "" {
+		return 0, sdk.Coin{}, fmt.Errorf("simulator address must not be empty")
+	}

Comment on lines +44 to +45
poolsInRoute := route[0].GetPools()

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle the case where poolsInRoute is empty.

If poolsInRoute is empty, the subsequent loop will not execute, and the poolManagerRoute will remain empty, potentially leading to an invalid swap message.

Apply this diff to check for an empty poolsInRoute:

 poolsInRoute := route[0].GetPools()

+if len(poolsInRoute) == 0 {
+	return 0, sdk.Coin{}, fmt.Errorf("no pools found in the route")
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
poolsInRoute := route[0].GetPools()
poolsInRoute := route[0].GetPools()
if len(poolsInRoute) == 0 {
return 0, sdk.Coin{}, fmt.Errorf("no pools found in the route")
}

Comment on lines +55 to +58
// Slippage bound from the token in and provided slippage tolerance multiplier
tokenOutAmt := quote.GetAmountOut()
slippageBound := tokenOutAmt.ToLegacyDec().Mul(slippageToleranceMultiplier).TruncateInt()

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Validate that slippageBound is positive to prevent invalid swap messages.

If slippageBound is zero or negative, it could result in an invalid TokenOutMinAmount in the swap message, causing the simulation to fail or behave unexpectedly.

Apply this diff to ensure slippageBound is positive:

 slippageBound := tokenOutAmt.ToLegacyDec().Mul(slippageToleranceMultiplier).TruncateInt()

+if !slippageBound.IsPositive() {
+	return 0, sdk.Coin{}, fmt.Errorf("slippage bound must be positive")
+}

Committable suggestion skipped: line range outside the PR's diff.

Copy link

sonarqubecloud bot commented Nov 4, 2024

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (3)
tests/quote_response.py (2)

36-44: Add type hints and documentation for price_info parameter.

While the implementation is correct, consider adding type hints and docstring to clarify:

  • The expected type/structure of price_info
  • The purpose and usage of this parameter
  • Example values or format

Example improvement:

 def __init__(self, amount_in, amount_out, route, effective_fee, price_impact,
-             in_base_out_quote_spot_price, price_info=None):
+             in_base_out_quote_spot_price, price_info: Optional[Dict[str, Any]] = None):
+    """Initialize QuoteExactAmountInResponse.
+    
+    Args:
+        price_info: Optional dictionary containing simulation price data
+                   from quote estimation.
+    """
🧰 Tools
🪛 Ruff

40-40: Decimal may be undefined, or defined from star imports

(F405)


41-41: Decimal may be undefined, or defined from star imports

(F405)


42-42: Decimal may be undefined, or defined from star imports

(F405)


Line range hint 1-1: Replace wildcard import with explicit import.

The current wildcard import from decimal import * triggers static analysis warnings and can lead to namespace pollution. Consider explicitly importing the Decimal class:

-from decimal import *
+from decimal import Decimal
🧰 Tools
🪛 Ruff

40-40: Decimal may be undefined, or defined from star imports

(F405)


41-41: Decimal may be undefined, or defined from star imports

(F405)


42-42: Decimal may be undefined, or defined from star imports

(F405)

docs/swagger.yaml (1)

410-419: Enhance simulation parameters with validation and examples.

The new parameters would benefit from additional OpenAPI specifications for better documentation and validation.

       - description: Address of the simulator to simulate the quote. If provided,
           the quote will be simulated.
         in: query
         name: simulatorAddress
         type: string
+        example: "osmo1cyyzpxplxdzkeea7kwsydadg87357qnahakaks"
+        pattern: "^osmo1[a-zA-Z0-9]{38}$"
       - description: Slippage tolerance multiplier for the simulation. If simulatorAddress
           is provided, this must be provided.
         in: query
         name: simulationSlippageTolerance
         type: string
+        example: "0.01"
+        pattern: "^0\\.\\d+$"
+        minimum: "0"
+        maximum: "1"
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 96cf62b and 093f9e9.

📒 Files selected for processing (6)
  • docs/docs.go (3 hunks)
  • docs/swagger.json (3 hunks)
  • docs/swagger.yaml (3 hunks)
  • ingest/usecase/plugins/orderbook/fillbot/context/block/block_context.go (1 hunks)
  • router/delivery/http/router_handler.go (5 hunks)
  • tests/quote_response.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • docs/docs.go
  • docs/swagger.json
  • router/delivery/http/router_handler.go
🧰 Additional context used
🪛 Ruff
tests/quote_response.py

40-40: Decimal may be undefined, or defined from star imports

(F405)


41-41: Decimal may be undefined, or defined from star imports

(F405)


42-42: Decimal may be undefined, or defined from star imports

(F405)

🔇 Additional comments (1)
ingest/usecase/plugins/orderbook/fillbot/context/block/block_context.go (1)

61-61: Great improvement using interface instead of concrete type!

Changing from *grpc.ClientConn to grpc.ClientConnInterface is a good practice as it:

  • Follows interface-based design principles
  • Improves testability by allowing mock implementations
  • Maintains backward compatibility since *grpc.ClientConn implements the interface

@@ -58,7 +58,7 @@ type BlockGasPrice struct {
var _ BlockCtxI = &blockContext{}

// New creates a new block context.
func New(ctx context.Context, chainGRPCClient *grpc.ClientConn, uniqueDenoms []string, orderBookDenomPrices domain.PricesResult, userBalances sdk.Coins, defaultQuoteDenom string, blockHeight uint64) (*blockContext, error) {
func New(ctx context.Context, chainGRPCClient grpc.ClientConnInterface, uniqueDenoms []string, orderBookDenomPrices domain.PricesResult, userBalances sdk.Coins, defaultQuoteDenom string, blockHeight uint64) (*blockContext, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Remove unused parameter chainGRPCClient

The chainGRPCClient parameter is not used within the New function implementation or stored in the blockContext struct. It appears only in the function signature and is not referenced anywhere else in the file. This parameter can be safely removed.

🔗 Analysis chain

Verify if the chainGRPCClient parameter is needed.

The chainGRPCClient parameter isn't used within the function body. If it's not used elsewhere, consider removing it to maintain clean code.

Let's check if this parameter is used in other methods or stored for later use:


Let me gather more information to verify if the parameter is actually used within the block_context implementation.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for chainGRPCClient usage in the codebase
rg -l 'chainGRPCClient'

# Search for any gRPC client field in the blockContext struct
ast-grep --pattern 'type blockContext struct {
  $$$
  $gRPC $Type
  $$$
}'

Length of output: 219


Script:

#!/bin/bash
# Check the implementation of blockContext to see if chainGRPCClient is stored or used
rg -A 10 "func New\(" ingest/usecase/plugins/orderbook/fillbot/context/block/block_context.go

# Look for any method that might use chainGRPCClient in the same file
rg "chainGRPCClient" ingest/usecase/plugins/orderbook/fillbot/context/block/block_context.go

Length of output: 844

Comment on lines +145 to +146
math.Int:
type: object
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance the math.Int type definition with proper schema.

The current definition as an empty object type lacks proper schema definition for validation and documentation purposes. Consider adding proper type constraints and description.

   math.Int:
-    type: object
+    type: object
+    description: Represents a arbitrary-precision integer using math/big package
+    properties:
+      i:
+        type: string
+        description: String representation of the arbitrary-precision integer
+    required:
+      - i
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
math.Int:
type: object
math.Int:
type: object
description: Represents a arbitrary-precision integer using math/big package
properties:
i:
type: string
description: String representation of the arbitrary-precision integer
required:
- i

@p0mvn p0mvn merged commit f2931d8 into v26.x Nov 4, 2024
10 checks passed
@p0mvn p0mvn deleted the roman/sim-quote branch November 4, 2024 17:36
mergify bot pushed a commit that referenced this pull request Nov 4, 2024
* feat: simulate swap as part of quotes

* try adding e2e test

* updates

* lint

* fix test

* swagger

* attempt fix grpc wiring

* fix e2e sim test

* updates

(cherry picked from commit f2931d8)

# Conflicts:
#	domain/cosmos/tx/msg_simulator.go
#	domain/cosmos/tx/msg_simulator_test.go
#	domain/mocks/msg_simulator_mock.go
#	domain/passthrough/passthrough_grpc_client.go
p0mvn added a commit that referenced this pull request Nov 12, 2024
* feat: simulate swap as part of quotes

* try adding e2e test

* updates

* lint

* fix test

* swagger

* attempt fix grpc wiring

* fix e2e sim test

* updates

(cherry picked from commit f2931d8)

# Conflicts:
#	domain/cosmos/tx/msg_simulator.go
#	domain/cosmos/tx/msg_simulator_test.go
#	domain/mocks/msg_simulator_mock.go
#	domain/passthrough/passthrough_grpc_client.go

Co-authored-by: Roman <[email protected]>
deividaspetraitis pushed a commit that referenced this pull request Nov 25, 2024
* feat: simulate swap as part of quotes

* try adding e2e test

* updates

* lint

* fix test

* swagger

* attempt fix grpc wiring

* fix e2e sim test

* updates
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A:backport/v27.x Backport to v27.x branch
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants