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

[SLT-315] refactor(opbot): use gql api #3260

Merged
merged 47 commits into from
Nov 7, 2024
Merged

Conversation

golangisfun123
Copy link
Collaborator

@golangisfun123 golangisfun123 commented Oct 9, 2024

Description
A clear and concise description of the features you're adding in this pull request.

Additional context
Add any other context about the problem you're solving.

Metadata

  • Fixes #[Link to Issue]

Summary by CodeRabbit

  • New Features

    • Introduced a new RFQ client for streamlined quote request handling.
    • Added support for additional blockchain chains: "linea" and "world chain."
  • Bug Fixes

    • Simplified command handling logic for RFQ lookups and refunds, improving performance and reliability.
  • Documentation

    • Updated command descriptions to clarify data retrieval from the RFQ-indexer API.
  • Chores

    • Removed unused functions and cleaned up the codebase for better maintainability.

02ea573: explorer-ui preview link
7f43b20: explorer-ui preview link
5e94f50: explorer-ui preview link
385000a: explorer-ui preview link
e111b9e: explorer-ui preview link
14756bd: explorer-ui preview link
f28af6a: explorer-ui preview link
9671087: explorer-ui preview link
59c875c: explorer-ui preview link
d3ddc9f: explorer-ui preview link
8ddb7de: explorer-ui preview link
47954f4: explorer-ui preview link
df3e3b0: explorer-ui preview link
749a014: explorer-ui preview link
bf87f93: explorer-ui preview link

@github-actions github-actions bot added go Pull requests that update Go code size/s labels Oct 9, 2024
Copy link

cloudflare-workers-and-pages bot commented Oct 9, 2024

Deploying sanguine-fe with  Cloudflare Pages  Cloudflare Pages

Latest commit: 1fbfbfa
Status: ✅  Deploy successful!
Preview URL: https://08ae5784.sanguine-fe.pages.dev
Branch Preview URL: https://opbot-migration.sanguine-fe.pages.dev

View logs

@github-actions github-actions bot added size/m and removed size/s labels Oct 9, 2024
Copy link

codecov bot commented Oct 9, 2024

Codecov Report

Attention: Patch coverage is 0% with 102 lines in your changes missing coverage. Please review.

Project coverage is 30.65365%. Comparing base (f2f1139) to head (1fbfbfa).
Report is 3 commits behind head on master.

Files with missing lines Patch % Lines
contrib/opbot/botmd/commands.go 0.00000% 63 Missing ⚠️
contrib/opbot/internal/client.go 0.00000% 34 Missing ⚠️
contrib/opbot/botmd/botmd.go 0.00000% 5 Missing ⚠️
Additional details and impacted files
@@                 Coverage Diff                  @@
##              master       #3260          +/-   ##
====================================================
- Coverage   43.84981%   30.65365%   -13.19616%     
====================================================
  Files             74         448         +374     
  Lines           2317       29817       +27500     
  Branches          82          82                  
====================================================
+ Hits            1016        9140        +8124     
- Misses          1295       19827       +18532     
- Partials           6         850         +844     
Flag Coverage Δ
cctp-relayer 31.97848% <ø> (?)
ethergo 47.23943% <ø> (?)
omnirpc 32.65401% <ø> (?)
opbot 0.18645% <0.00000%> (?)
promexporter 6.81642% <ø> (ø)
rfq 24.57747% <ø> (?)
scribe 18.18182% <ø> (?)
tools 30.55118% <ø> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@golangisfun123 golangisfun123 changed the title [WIP] use gql api [SLT-315] refactor(opbot): use gql api Oct 10, 2024
Copy link
Contributor

coderabbitai bot commented Oct 10, 2024

Walkthrough

The changes in this pull request primarily focus on the botmd package, specifically enhancing the command handling logic of the Bot struct. Notably, the rfqLookupCommand and rfqRefund methods have been refactored to use a direct call to the RFQ client, simplifying the previous multi-relayer approach. The getTxAge function has been removed, and several unused types and variables have been eliminated. Additionally, a new RFQClient interface has been introduced, along with a corresponding implementation, to streamline RFQ data retrieval.

Changes

File Change Summary
contrib/opbot/botmd/commands.go Refactored rfqLookupCommand and rfqRefund methods to use b.rfqClient.GetRFQ. Removed getTxAge function. Updated makeFastBridge method signature.
contrib/opbot/botmd/commands_test.go Removed import statements and deleted TestTxAge function.
contrib/opbot/botmd/export_test.go Removed import statements and deleted GetTxAge function.
contrib/opbot/config/config.go Added new field RFQIndexerAPIURL to Config struct.
contrib/opbot/internal/client.go Introduced RFQClient interface and rfqClientImpl struct with GetRFQ method. Added constructor NewRFQClient.
contrib/opbot/internal/model.go Added new types: GetRFQByTxIDResponse, Bridge, BridgeRequest, BridgeRelay, BridgeProof, BridgeClaim.
ethergo/chaindata/chaindata.go Updated ChainMetadataList to include "linea" and "world chain".
contrib/opbot/botmd/botmd.go Added rfqClient field to Bot struct and initialized it in NewBot.

Possibly related PRs

Suggested labels

M-deps

Suggested reviewers

  • trajan0x
  • dwasse
  • aureliusbtc

🐰 In the meadow where bunnies play,
The RFQ changes brighten the day.
With simpler calls and less to manage,
Our bot hops forth, ready to advantage!
No more old ways, just fresh and new,
A leap of joy for all to pursue! 🌼


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: 7

🧹 Outside diff range and nitpick comments (8)
contrib/opbot/internal/model.go (6)

3-9: Add a comment for the exported type GetRFQByTxIDResponse

The struct is well-structured and follows Go conventions. However, to improve documentation and address the static analysis hint, please add a comment describing the purpose and usage of this type.

Example comment:

// GetRFQByTxIDResponse represents the complete response structure for a Request for Quotation (RFQ) lookup by transaction ID.
// It includes details about the bridge, request, relay, proof, and claim associated with the RFQ.
🧰 Tools
🪛 GitHub Check: Lint (contrib/opbot)

[failure] 3-3:
exported: exported type GetRFQByTxIDResponse should have comment or be unexported (revive)


11-23: Add a comment and consider changing the type of SendChainGas

The Bridge struct is well-structured and follows Go conventions. However, please consider the following improvements:

  1. Add a comment describing the purpose and usage of this type to improve documentation and address the static analysis hint.

  2. The SendChainGas field is currently of type int. Depending on the expected range of values, this might not be sufficient. Consider using uint64 or big.Int if larger values are possible.

Example comment and type change:

// Bridge represents the core information of a bridge transaction,
// including details about the origin and destination chains, tokens, and amounts.
type Bridge struct {
    // ... other fields ...
    SendChainGas          uint64 `json:"sendChainGas"`
}
🧰 Tools
🪛 GitHub Check: Lint (contrib/opbot)

[failure] 11-11:
exported: exported type Bridge should have comment or be unexported (revive)


25-29: Add a comment and consider changing the type of BlockNumber

The BridgeRequest struct is well-structured and follows Go conventions. However, please consider the following improvements:

  1. Add a comment describing the purpose and usage of this type to improve documentation and address the static analysis hint.

  2. The BlockNumber field is currently of type string. Depending on how this value is used, consider changing it to a numeric type like uint64 for better type safety and easier numerical operations.

Example comment and type change:

// BridgeRequest represents the details of a bridge request,
// including the block number, timestamp, and transaction hash.
type BridgeRequest struct {
    BlockNumber     uint64 `json:"blockNumber,string"`
    BlockTimestamp  int64  `json:"blockTimestamp"`
    TransactionHash string `json:"transactionHash"`
}

Note: If you need to keep the JSON representation as a string, you can use the ,string tag as shown above.

🧰 Tools
🪛 GitHub Check: Lint (contrib/opbot)

[failure] 25-25:
exported: exported type BridgeRequest should have comment or be unexported (revive)


31-37: Add a comment and consider changing the type of BlockNumber

The BridgeRelay struct is well-structured and follows Go conventions. However, please consider the following improvements:

  1. Add a comment describing the purpose and usage of this type to improve documentation and address the static analysis hint.

  2. The BlockNumber field is currently of type string. Depending on how this value is used, consider changing it to a numeric type like uint64 for better type safety and easier numerical operations.

Example comment and type change:

// BridgeRelay represents the details of a bridge relay operation,
// including block information, transaction hash, relayer, and destination address.
type BridgeRelay struct {
    BlockNumber     uint64 `json:"blockNumber,string"`
    BlockTimestamp  int64  `json:"blockTimestamp"`
    TransactionHash string `json:"transactionHash"`
    Relayer         string `json:"relayer"`
    To              string `json:"to"`
}

Note: If you need to keep the JSON representation as a string, you can use the ,string tag as shown above.

🧰 Tools
🪛 GitHub Check: Lint (contrib/opbot)

[failure] 31-31:
exported: exported type BridgeRelay should have comment or be unexported (revive)


39-44: Add a comment and consider changing the type of BlockNumber

The BridgeProof struct is well-structured and follows Go conventions. However, please consider the following improvements:

  1. Add a comment describing the purpose and usage of this type to improve documentation and address the static analysis hint.

  2. The BlockNumber field is currently of type string. Depending on how this value is used, consider changing it to a numeric type like uint64 for better type safety and easier numerical operations.

Example comment and type change:

// BridgeProof represents the proof details for a bridge operation,
// including block information, transaction hash, and relayer address.
type BridgeProof struct {
    BlockNumber     uint64 `json:"blockNumber,string"`
    BlockTimestamp  int64  `json:"blockTimestamp"`
    TransactionHash string `json:"transactionHash"`
    Relayer         string `json:"relayer"`
}

Note: If you need to keep the JSON representation as a string, you can use the ,string tag as shown above.

🧰 Tools
🪛 GitHub Check: Lint (contrib/opbot)

[failure] 39-39:
exported: exported type BridgeProof should have comment or be unexported (revive)


46-53: Add a comment and consider changing the types of BlockNumber and AmountFormatted

The BridgeClaim struct is well-structured and follows Go conventions. However, please consider the following improvements:

  1. Add a comment describing the purpose and usage of this type to improve documentation and address the static analysis hint.

  2. The BlockNumber field is currently of type string. Depending on how this value is used, consider changing it to a numeric type like uint64 for better type safety and easier numerical operations.

  3. The AmountFormatted field is of type string. Depending on the precision required and how this value is used, consider using a more appropriate type such as decimal.Decimal from the shopspring/decimal package for accurate decimal arithmetic.

Example comment and type changes:

import "github.com/shopspring/decimal"

// BridgeClaim represents the details of a bridge claim operation,
// including block information, transaction details, and the claimed amount.
type BridgeClaim struct {
    BlockNumber     uint64          `json:"blockNumber,string"`
    BlockTimestamp  int64           `json:"blockTimestamp"`
    TransactionHash string          `json:"transactionHash"`
    To              string          `json:"to"`
    Relayer         string          `json:"relayer"`
    AmountFormatted decimal.Decimal `json:"amountFormatted"`
}

Note: If you need to keep the JSON representation as a string for BlockNumber, you can use the ,string tag as shown above. For AmountFormatted, the decimal.Decimal type handles JSON marshaling and unmarshaling automatically.

🧰 Tools
🪛 GitHub Check: Lint (contrib/opbot)

[failure] 46-46:
exported: exported type BridgeClaim should have comment or be unexported (revive)

contrib/opbot/botmd/botmd.go (1)

33-33: LGTM: New rfqClient field added

The addition of the rfqClient field of type internal.RFQClient aligns well with the PR objective of refactoring to use a GraphQL API. This encapsulation of RFQ functionality in an internal client is a good design choice.

Consider adding a brief comment explaining the purpose of this field, e.g.:

// rfqClient handles interactions with the RFQ (Request for Quote) system
rfqClient internal.RFQClient
contrib/opbot/internal/client.go (1)

1-1: Add a package comment for the 'internal' package

It's recommended to include a package-level comment that summarizes the purpose of the package.

🧰 Tools
🪛 GitHub Check: Lint (contrib/opbot)

[failure] 1-1:
package-comments: should have a package comment (revive)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between e74153c and cd2187a.

📒 Files selected for processing (7)
  • contrib/opbot/botmd/botmd.go (3 hunks)
  • contrib/opbot/botmd/commands.go (5 hunks)
  • contrib/opbot/botmd/commands_test.go (0 hunks)
  • contrib/opbot/botmd/export_test.go (0 hunks)
  • contrib/opbot/config/config.go (1 hunks)
  • contrib/opbot/internal/client.go (1 hunks)
  • contrib/opbot/internal/model.go (1 hunks)
💤 Files with no reviewable changes (2)
  • contrib/opbot/botmd/commands_test.go
  • contrib/opbot/botmd/export_test.go
🧰 Additional context used
🪛 GitHub Check: Lint (contrib/opbot)
contrib/opbot/botmd/commands.go

[failure] 194-194:
G115: integer overflow conversion int -> uint32 (gosec)


[failure] 210-210:
G115: integer overflow conversion int -> uint32 (gosec)

contrib/opbot/internal/client.go

[failure] 66-66:
ifElseChain: rewrite if-else to switch statement (gocritic)


[failure] 1-1:
package-comments: should have a package comment (revive)

contrib/opbot/internal/model.go

[failure] 3-3:
exported: exported type GetRFQByTxIDResponse should have comment or be unexported (revive)


[failure] 11-11:
exported: exported type Bridge should have comment or be unexported (revive)


[failure] 25-25:
exported: exported type BridgeRequest should have comment or be unexported (revive)


[failure] 31-31:
exported: exported type BridgeRelay should have comment or be unexported (revive)


[failure] 39-39:
exported: exported type BridgeProof should have comment or be unexported (revive)


[failure] 46-46:
exported: exported type BridgeClaim should have comment or be unexported (revive)

🔇 Additional comments (2)
contrib/opbot/internal/model.go (1)

1-53: Overall structure is good, but some improvements are recommended

The file is well-structured and follows Go conventions. The defined structs provide a clear representation of the RFQ (Request for Quotation) process and related components. However, there are a few areas for improvement:

  1. Add comments for all exported types to improve documentation and address static analysis hints.
  2. Consider changing the type of BlockNumber fields from string to uint64 for better type safety and easier numerical operations.
  3. For the AmountFormatted field in BridgeClaim, consider using a more appropriate type for decimal arithmetic, such as decimal.Decimal.
  4. Review the SendChainGas field in the Bridge struct to ensure the int type is sufficient for all possible gas values.

These changes will enhance the overall quality, maintainability, and type safety of the code.

🧰 Tools
🪛 GitHub Check: Lint (contrib/opbot)

[failure] 3-3:
exported: exported type GetRFQByTxIDResponse should have comment or be unexported (revive)


[failure] 11-11:
exported: exported type Bridge should have comment or be unexported (revive)


[failure] 25-25:
exported: exported type BridgeRequest should have comment or be unexported (revive)


[failure] 31-31:
exported: exported type BridgeRelay should have comment or be unexported (revive)


[failure] 39-39:
exported: exported type BridgeProof should have comment or be unexported (revive)


[failure] 46-46:
exported: exported type BridgeClaim should have comment or be unexported (revive)

contrib/opbot/botmd/botmd.go (1)

9-9: LGTM: New import for internal package

The addition of the internal package import is appropriate for the refactoring being done. Using an internal package is a good practice for code that should not be imported by external projects.

Comment on lines +41 to +42
// RFQIndexerAPIURL is the URL of the RFQ indexer API.
RFQIndexerAPIURL string `yaml:"rfq_indexer_api_url"`
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

Consider enhancing the new RFQIndexerAPIURL field

The addition of the RFQIndexerAPIURL field is good, but there are a few suggestions to improve it:

  1. Add a comment explaining the purpose of this field, similar to other fields in the struct.
  2. Consider adding validation for this field in the Validate method if it's required for the application to function correctly.
  3. For consistency, you might want to rename RFQApiURL to RFQAPIUrl to match the naming convention of the new field.

Here's a suggested improvement for the field declaration:

+    // RFQIndexerAPIURL is the URL of the RFQ indexer API.
     RFQIndexerAPIURL string `yaml:"rfq_indexer_api_url"`

Consider updating the Validate method to include a check for this new field if necessary:

func (c *Config) Validate() error {
    // ... existing validations ...
    
    if c.RFQIndexerAPIURL == "" {
        return errors.New("rfq_indexer_api_url is required")
    }
    
    return nil
}

Comment on lines 47 to 51
handler: handler,
cfg: cfg,
server: server,
logger: experimentalLogger.MakeWrappedSugaredLogger(sugaredLogger),
rfqClient: internal.NewRFQClient(handler, cfg.RFQIndexerAPIURL, cfg.RelayerURLS),
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

Issue Found: Missing RFQIndexerAPIURL in config.Config

The RFQIndexerAPIURL field is missing in the config.Config struct, which is required for the proper initialization of rfqClient.

  • File: contrib/opbot/config/config.go
🔗 Analysis chain

LGTM: Initialization of rfqClient and improved formatting

The initialization of the rfqClient in the NewBot function is correct and aligns with the new field added to the Bot struct. The reformatting of the struct initialization improves readability.

Please ensure that the RFQIndexerAPIURL and RelayerURLS fields have been added to the config.Config struct. Run the following script to verify:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the presence of new config fields

# Test: Check for RFQIndexerAPIURL and RelayerURLS in the config struct
rg --type go 'type\s+Config\s+struct' -A 20 | rg 'RFQIndexerAPIURL|RelayerURLS'

Length of output: 240

SetResult(&res).
Get(fmt.Sprintf(getRequestByTxHash, txID))
if err != nil {
return nil, "", fmt.Errorf("failed to get quote request by tx hash: %w", err)
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

Correct the error message in GetRFQByTxID function

The error message refers to "tx hash" instead of "tx ID". Update it for consistency with the function's purpose.

Apply this diff to fix the error message:

-		return nil, "", fmt.Errorf("failed to get quote request by tx hash: %w", err)
+		return nil, "", fmt.Errorf("failed to get quote request by tx ID: %w", err)
📝 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
return nil, "", fmt.Errorf("failed to get quote request by tx hash: %w", err)
return nil, "", fmt.Errorf("failed to get quote request by tx ID: %w", err)

Comment on lines 86 to 93
for _, relayerClient := range r.relayerClients {
resp, err = relayerClient.GetQuoteRequestByTxHash(ctx, txHash)
if err != nil {
return nil, fmt.Errorf("failed to get quote request by tx hash: %w", err)
}
}

return resp, nil
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 errors from multiple relayer clients appropriately

Currently, the function returns on the first error encountered, which means it doesn't attempt to fetch from other relayer clients. Consider modifying the logic to try all clients before returning an error.

Apply this diff to continue trying other clients on error:

	func (r *rfqClientImpl) GetRFQByTxHash(ctx context.Context, txHash string) (*relapi.GetQuoteRequestResponse, error) {
		var resp *relapi.GetQuoteRequestResponse
-		var err error
+		var errs []error
		for _, relayerClient := range r.relayerClients {
			resp, err := relayerClient.GetQuoteRequestByTxHash(ctx, txHash)
			if err != nil {
-				return nil, fmt.Errorf("failed to get quote request by tx hash: %w", err)
+				errs = append(errs, err)
+				continue
			}
+			return resp, nil
		}
-	}
-
-	return resp, nil
+	return nil, fmt.Errorf("failed to get quote request by tx hash from all relayer clients: %v", errs)
	}
📝 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
for _, relayerClient := range r.relayerClients {
resp, err = relayerClient.GetQuoteRequestByTxHash(ctx, txHash)
if err != nil {
return nil, fmt.Errorf("failed to get quote request by tx hash: %w", err)
}
}
return resp, nil
func (r *rfqClientImpl) GetRFQByTxHash(ctx context.Context, txHash string) (*relapi.GetQuoteRequestResponse, error) {
var resp *relapi.GetQuoteRequestResponse
var errs []error
for _, relayerClient := range r.relayerClients {
resp, err := relayerClient.GetQuoteRequestByTxHash(ctx, txHash)
if err != nil {
errs = append(errs, err)
continue
}
return resp, nil
}
return nil, fmt.Errorf("failed to get quote request by tx hash from all relayer clients: %v", errs)
}

Comment on lines 66 to 76
if res.BridgeClaim != (BridgeClaim{}) {
status = "Claimed"
} else if res.BridgeProof != (BridgeProof{}) {
status = "Proven"
} else if res.BridgeRelay != (BridgeRelay{}) {
status = "Relayed"
} else if res.BridgeRequest != (BridgeRequest{}) {
status = "Requested"
} else {
status = "Unknown"
}
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

Refactor the if-else chain into a switch statement

The current if-else chain can be simplified using a switch statement for improved readability and maintainability.

Apply this diff to refactor the code:

	var status string
-	if res.BridgeClaim != (BridgeClaim{}) {
+	switch {
+	case res.BridgeClaim != (BridgeClaim{}):
		status = "Claimed"
-	} else if res.BridgeProof != (BridgeProof{}) {
+	case res.BridgeProof != (BridgeProof{}):
		status = "Proven"
-	} else if res.BridgeRelay != (BridgeRelay{}) {
+	case res.BridgeRelay != (BridgeRelay{}):
		status = "Relayed"
-	} else if res.BridgeRequest != (BridgeRequest{}) {
+	case res.BridgeRequest != (BridgeRequest{}):
		status = "Requested"
-	} else {
+	default:
		status = "Unknown"
	}
📝 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 res.BridgeClaim != (BridgeClaim{}) {
status = "Claimed"
} else if res.BridgeProof != (BridgeProof{}) {
status = "Proven"
} else if res.BridgeRelay != (BridgeRelay{}) {
status = "Relayed"
} else if res.BridgeRequest != (BridgeRequest{}) {
status = "Requested"
} else {
status = "Unknown"
}
var status string
switch {
case res.BridgeClaim != (BridgeClaim{}):
status = "Claimed"
case res.BridgeProof != (BridgeProof{}):
status = "Proven"
case res.BridgeRelay != (BridgeRelay{}):
status = "Relayed"
case res.BridgeRequest != (BridgeRequest{}):
status = "Requested"
default:
status = "Unknown"
}
🧰 Tools
🪛 GitHub Check: Lint (contrib/opbot)

[failure] 66-66:
ifElseChain: rewrite if-else to switch statement (gocritic)

} else {
objects = append(objects, &slack.TextBlockObject{
Type: slack.MarkdownType,
Text: fmt.Sprintf("*DestTxHash*: %s", toTXSlackLink(res.BridgeRelay.TransactionHash, uint32(res.Bridge.DestChainID))),
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

Potential integer overflow when converting int to uint32

At line 210, res.Bridge.DestChainID is being converted from int to uint32 using uint32(res.Bridge.DestChainID). Similar to the issue at line 194, this could cause an integer overflow if res.Bridge.DestChainID exceeds the uint32 maximum value, leading to incorrect chain IDs.

Ensure that res.Bridge.DestChainID is within the uint32 range. If not, consider:

  • Switching to a larger unsigned integer type like uint64.
  • Adjusting the toTXSlackLink function to accept an int or int64 to safely handle larger chain ID values without risking overflow.
🧰 Tools
🪛 GitHub Check: Lint (contrib/opbot)

[failure] 210-210:
G115: integer overflow conversion int -> uint32 (gosec)

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: 5

🧹 Outside diff range and nitpick comments (6)
contrib/opbot/internal/client.go (1)

28-50: LGTM with a suggestion: Consider adding URL validation.

The rfqClientImpl struct and NewRFQClient function are well-implemented. The client is properly configured with headers and tracing. However, consider adding validation for the indexerURL and relayerURLs to ensure they are valid before creating the clients.

Consider adding URL validation before creating the clients. Here's a simple example:

import "net/url"

func validateURL(urlStr string) error {
    _, err := url.ParseRequestURI(urlStr)
    return err
}

// In NewRFQClient
if err := validateURL(indexerURL); err != nil {
    return nil, fmt.Errorf("invalid indexer URL: %w", err)
}

for _, urlStr := range relayerURLs {
    if err := validateURL(urlStr); err != nil {
        return nil, fmt.Errorf("invalid relayer URL %s: %w", urlStr, err)
    }
    // ... create relayer client
}

This will help prevent runtime errors due to invalid URLs.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 34-44: contrib/opbot/internal/client.go#L34-L44
Added lines #L34 - L44 were not covered by tests


[warning] 46-49: contrib/opbot/internal/client.go#L46-L49
Added lines #L46 - L49 were not covered by tests

contrib/opbot/botmd/commands.go (5)

167-170: Add unit tests for new RFQ client call

The new interaction with b.rfqClient.GetRFQByTxID is not covered by tests. Adding unit tests for this functionality will help catch potential issues and improve code reliability.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 167-170: contrib/opbot/botmd/commands.go#L167-L170
Added lines #L167 - L170 were not covered by tests


179-201: Enhance test coverage for Slack message construction

The block constructing objects for the Slack message is currently not covered by tests. Consider adding unit tests to ensure the message formatting and content are correct.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 179-201: contrib/opbot/botmd/commands.go#L179-L201
Added lines #L179 - L201 were not covered by tests


203-213: Verify DestTxHash handling for different statuses

Ensure that the logic for appending DestTxHash to objects handles all possible statuses correctly. Consider adding unit tests to cover both the "Requested" status and other cases.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 203-213: contrib/opbot/botmd/commands.go#L203-L213
Added lines #L203 - L213 were not covered by tests


243-243: Add unit tests for refund functionality

The call to b.rfqClient.GetRFQByTxHash introduces new logic for refunds that is not currently tested. Adding unit tests will help verify that the refund process works as intended.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 243-243: contrib/opbot/botmd/commands.go#L243
Added line #L243 was not covered by tests


349-350: Add unit tests for getTxAge function

The new getTxAge function is crucial for accurate time display. Adding unit tests will help ensure it behaves as expected with various timestamp inputs.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 349-350: contrib/opbot/botmd/commands.go#L349-L350
Added lines #L349 - L350 were not covered by tests

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between cd2187a and 9dc780d.

📒 Files selected for processing (3)
  • contrib/opbot/botmd/commands.go (5 hunks)
  • contrib/opbot/internal/client.go (1 hunks)
  • contrib/opbot/internal/model.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • contrib/opbot/internal/model.go
🧰 Additional context used
🪛 GitHub Check: codecov/patch
contrib/opbot/botmd/commands.go

[warning] 167-170: contrib/opbot/botmd/commands.go#L167-L170
Added lines #L167 - L170 were not covered by tests


[warning] 179-201: contrib/opbot/botmd/commands.go#L179-L201
Added lines #L179 - L201 were not covered by tests


[warning] 203-213: contrib/opbot/botmd/commands.go#L203-L213
Added lines #L203 - L213 were not covered by tests


[warning] 216-218: contrib/opbot/botmd/commands.go#L216-L218
Added lines #L216 - L218 were not covered by tests


[warning] 243-243: contrib/opbot/botmd/commands.go#L243
Added line #L243 was not covered by tests


[warning] 306-309: contrib/opbot/botmd/commands.go#L306-L309
Added lines #L306 - L309 were not covered by tests


[warning] 313-313: contrib/opbot/botmd/commands.go#L313
Added line #L313 was not covered by tests


[warning] 349-350: contrib/opbot/botmd/commands.go#L349-L350
Added lines #L349 - L350 were not covered by tests


[warning] 356-356: contrib/opbot/botmd/commands.go#L356
Added line #L356 was not covered by tests

contrib/opbot/internal/client.go

[warning] 34-44: contrib/opbot/internal/client.go#L34-L44
Added lines #L34 - L44 were not covered by tests


[warning] 46-49: contrib/opbot/internal/client.go#L46-L49
Added lines #L46 - L49 were not covered by tests


[warning] 53-60: contrib/opbot/internal/client.go#L53-L60
Added lines #L53 - L60 were not covered by tests


[warning] 62-64: contrib/opbot/internal/client.go#L62-L64
Added lines #L62 - L64 were not covered by tests


[warning] 66-77: contrib/opbot/internal/client.go#L66-L77
Added lines #L66 - L77 were not covered by tests


[warning] 80-80: contrib/opbot/internal/client.go#L80
Added line #L80 was not covered by tests


[warning] 84-91: contrib/opbot/internal/client.go#L84-L91
Added lines #L84 - L91 were not covered by tests


[warning] 94-94: contrib/opbot/internal/client.go#L94
Added line #L94 was not covered by tests

🔇 Additional comments (5)
contrib/opbot/internal/client.go (5)

1-14: LGTM: Package declaration and imports look good.

The package name "internal" is appropriate for an internal implementation, and the imports seem relevant to the RFQ client functionality being implemented.


16-18: LGTM: Constant declaration is appropriate.

The getRequestByTxHash constant is well-named and follows Go conventions. It's used for constructing the API endpoint, which is a good practice for maintaining consistency and ease of updates.


20-26: LGTM: RFQClient interface is well-defined.

The RFQClient interface is well-documented and provides a clear abstraction for RFQ operations. The method signatures are appropriate and follow Go conventions.


52-81: LGTM: GetRFQByTxID implementation looks good.

The GetRFQByTxID method is well-implemented with proper error handling and status determination. The switch statement for status determination is a good improvement from the previous if-else chain.

The error message in line 59 still refers to "tx hash" instead of "tx ID". This issue was pointed out in a previous review. Please update it for consistency:

-		return nil, "", fmt.Errorf("failed to get quote request by tx hash: %w", err)
+		return nil, "", fmt.Errorf("failed to get quote request by tx ID: %w", err)
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 53-60: contrib/opbot/internal/client.go#L53-L60
Added lines #L53 - L60 were not covered by tests


[warning] 62-64: contrib/opbot/internal/client.go#L62-L64
Added lines #L62 - L64 were not covered by tests


[warning] 66-77: contrib/opbot/internal/client.go#L66-L77
Added lines #L66 - L77 were not covered by tests


[warning] 80-80: contrib/opbot/internal/client.go#L80
Added line #L80 was not covered by tests


97-97: LGTM: Good practice for interface compliance check.

The type assertion var _ RFQClient = &rfqClientImpl{} is a great practice. It ensures at compile-time that rfqClientImpl correctly implements the RFQClient interface.

Comment on lines 83 to 95
// GetRFQByTxHash gets a quote request by transaction hash.
func (r *rfqClientImpl) GetRFQByTxHash(ctx context.Context, txHash string) (*relapi.GetQuoteRequestResponse, error) {
var resp *relapi.GetQuoteRequestResponse
var err error
for _, relayerClient := range r.relayerClients {
resp, err = relayerClient.GetQuoteRequestByTxHash(ctx, txHash)
if err != nil {
return nil, fmt.Errorf("failed to get quote request by tx hash: %w", err)
}
}

return resp, nil
}
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

Improve error handling in GetRFQByTxHash method.

The current implementation of GetRFQByTxHash returns on the first error encountered, which means it doesn't attempt to fetch from other relayer clients. This might not be optimal if some relayer clients are temporarily unavailable.

Consider modifying the logic to try all clients before returning an error. Here's a suggested improvement:

func (r *rfqClientImpl) GetRFQByTxHash(ctx context.Context, txHash string) (*relapi.GetQuoteRequestResponse, error) {
    var lastErr error
    for _, relayerClient := range r.relayerClients {
        resp, err := relayerClient.GetQuoteRequestByTxHash(ctx, txHash)
        if err == nil {
            return resp, nil
        }
        lastErr = err
    }
    return nil, fmt.Errorf("failed to get quote request by tx hash from all relayer clients: %w", lastErr)
}

This change will attempt to fetch from all available relayer clients before giving up, improving the robustness of the method.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 84-91: contrib/opbot/internal/client.go#L84-L91
Added lines #L84 - L91 were not covered by tests


[warning] 94-94: contrib/opbot/internal/client.go#L94
Added line #L94 was not covered by tests

Comment on lines 1 to 97
// Package internal provides the RFQ client implementation.
package internal

import (
"context"
"fmt"
"net/http"

"github.com/dubonzi/otelresty"
"github.com/go-http-utils/headers"
"github.com/go-resty/resty/v2"
"github.com/synapsecns/sanguine/core/metrics"
"github.com/synapsecns/sanguine/services/rfq/relayer/relapi"
)

const (
getRequestByTxHash = "/api/transaction-id/%s"
)

// RFQClient is the interface for the RFQ client.
type RFQClient interface {
// GetRFQByTxID gets a quote request by transaction ID.
GetRFQByTxID(ctx context.Context, txID string) (resp *GetRFQByTxIDResponse, status string, err error)
// GetRFQByTxHash gets a quote request by transaction hash.
GetRFQByTxHash(ctx context.Context, txHash string) (resp *relapi.GetQuoteRequestResponse, err error)
}

type rfqClientImpl struct {
client *resty.Client
relayerClients []relapi.RelayerClient
}

// NewRFQClient creates a new RFQClient.
func NewRFQClient(handler metrics.Handler, indexerURL string, relayerURLs []string) RFQClient {
client := resty.New()
client.SetBaseURL(indexerURL)
client.SetHeader(headers.UserAgent, "rfq-client")

otelresty.TraceClient(client, otelresty.WithTracerProvider(handler.GetTracerProvider()))

var relayerClients []relapi.RelayerClient
for _, url := range relayerURLs {
relayerClients = append(relayerClients, relapi.NewRelayerClient(handler, url))
}

return &rfqClientImpl{
client: client,
relayerClients: relayerClients,
}
}

// GetRFQByTxID gets a quote request by transaction ID.
func (r *rfqClientImpl) GetRFQByTxID(ctx context.Context, txID string) (*GetRFQByTxIDResponse, string, error) {
var res GetRFQByTxIDResponse
resp, err := r.client.R().SetContext(ctx).
SetResult(&res).
Get(fmt.Sprintf(getRequestByTxHash, txID))
if err != nil {
return nil, "", fmt.Errorf("failed to get quote request by tx hash: %w", err)
}

if resp.StatusCode() != http.StatusOK {
return nil, "", fmt.Errorf("unexpected status code: %d", resp.StatusCode())
}

var status string
switch {
case res.BridgeClaim != (BridgeClaim{}):
status = "Claimed"
case res.BridgeProof != (BridgeProof{}):
status = "Proven"
case res.BridgeRelay != (BridgeRelay{}):
status = "Relayed"
case res.BridgeRequest != (BridgeRequest{}):
status = "Requested"
default:
status = "Unknown"
}

return &res, status, nil
}

// GetRFQByTxHash gets a quote request by transaction hash.
func (r *rfqClientImpl) GetRFQByTxHash(ctx context.Context, txHash string) (*relapi.GetQuoteRequestResponse, error) {
var resp *relapi.GetQuoteRequestResponse
var err error
for _, relayerClient := range r.relayerClients {
resp, err = relayerClient.GetQuoteRequestByTxHash(ctx, txHash)
if err != nil {
return nil, fmt.Errorf("failed to get quote request by tx hash: %w", err)
}
}

return resp, nil
}

var _ RFQClient = &rfqClientImpl{}
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

Improve test coverage for the RFQ client implementation.

The static analysis tool indicates that several parts of this file are not covered by tests. While 100% test coverage isn't always necessary, it's important to have good test coverage for critical parts of the code, especially for a client implementation like this.

Consider adding unit tests for the following:

  1. NewRFQClient function
  2. GetRFQByTxID method
  3. GetRFQByTxHash method

These tests should cover various scenarios, including:

  • Successful requests
  • Error handling
  • Edge cases (e.g., empty responses, network errors)

Adding these tests will improve the reliability and maintainability of the code.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 34-44: contrib/opbot/internal/client.go#L34-L44
Added lines #L34 - L44 were not covered by tests


[warning] 46-49: contrib/opbot/internal/client.go#L46-L49
Added lines #L46 - L49 were not covered by tests


[warning] 53-60: contrib/opbot/internal/client.go#L53-L60
Added lines #L53 - L60 were not covered by tests


[warning] 62-64: contrib/opbot/internal/client.go#L62-L64
Added lines #L62 - L64 were not covered by tests


[warning] 66-77: contrib/opbot/internal/client.go#L66-L77
Added lines #L66 - L77 were not covered by tests


[warning] 80-80: contrib/opbot/internal/client.go#L80
Added line #L80 was not covered by tests


[warning] 84-91: contrib/opbot/internal/client.go#L84-L91
Added lines #L84 - L91 were not covered by tests


[warning] 94-94: contrib/opbot/internal/client.go#L94
Added line #L94 was not covered by tests

return
}

_, err = ctx.Response().Reply(fmt.Sprintf("refund submitted: %s", toExplorerSlackLink(status.TxHash().String())))
_, err = ctx.Response().Reply(fmt.Sprintf("refund submitted: %s", toTXSlackLink(status.TxHash().String(), rawRequest.OriginChainID)))
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

Handle errors when sending refund transaction link

Similar to the previous point, if there's an error when sending the refund transaction link, consider handling it appropriately to enhance user experience and error transparency.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 313-313: contrib/opbot/botmd/commands.go#L313
Added line #L313 was not covered by tests

}

func toExplorerSlackLink(ogHash string) string {
rfqHash := strings.ToUpper(ogHash)
// cut off 0x
if strings.HasPrefix(rfqHash, "0x") {
if strings.HasPrefix(rfqHash, "0X") {
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

Ensure consistent hash prefix handling in toExplorerSlackLink

The current code checks for the "0X" prefix but does not handle the lowercase "0x" prefix. Consider updating the condition to handle both cases to prevent inconsistent behavior with different hash formats.

Apply this diff to address the issue:

 if strings.HasPrefix(rfqHash, "0X") || strings.HasPrefix(rfqHash, "0x") {
     rfqHash = rfqHash[2:]
 }
 rfqHash = strings.ToLower(rfqHash)

Committable suggestion was skipped due to low confidence.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 356-356: contrib/opbot/botmd/commands.go#L356
Added line #L356 was not covered by tests

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 (4)
contrib/opbot/internal/client.go (4)

16-18: Consider using a more descriptive constant name.

The constant getRequestByTxHash is used, but it might be more clear if it was named to indicate it's an API endpoint path.

Consider renaming the constant to something like getRequestByTxHashEndpoint for improved clarity:

-	getRequestByTxHash = "/api/transaction-id/%s"
+	getRequestByTxHashEndpoint = "/api/transaction-id/%s"

28-50: LGTM: rfqClientImpl and NewRFQClient are well-implemented.

The rfqClientImpl struct and NewRFQClient function are correctly implemented. The function properly initializes the HTTP client with tracing and sets up the relayer clients.

Consider adding error handling for the case when no relayer URLs are provided:

 func NewRFQClient(handler metrics.Handler, indexerURL string, relayerURLs []string) RFQClient {
+	if len(relayerURLs) == 0 {
+		panic("no relayer URLs provided")
+	}
 	client := resty.New()
 	// ... rest of the function
 }

52-81: LGTM: GetRFQByTxID method is well-implemented.

The GetRFQByTxID method correctly retrieves the quote request by transaction ID and handles errors appropriately. The status determination logic uses a switch statement as suggested in a previous review.

Consider extracting the status determination logic into a separate function for improved readability and reusability:

func determineStatus(res *GetRFQByTxIDResponse) string {
	switch {
	case res.BridgeClaim != (BridgeClaim{}):
		return "Claimed"
	case res.BridgeProof != (BridgeProof{}):
		return "Proven"
	case res.BridgeRelay != (BridgeRelay{}):
		return "Relayed"
	case res.BridgeRequest != (BridgeRequest{}):
		return "Requested"
	default:
		return "Unknown"
	}
}

Then, you can simplify the GetRFQByTxID method:

status := determineStatus(&res)
return &res, status, nil

83-95: LGTM: GetRFQByTxHash method is well-implemented.

The GetRFQByTxHash method correctly iterates through all relayer clients and returns the first successful response. Error handling has been improved as suggested in a previous review.

Consider using a more descriptive error message when all clients fail:

-	return nil, fmt.Errorf("could not get quote request by tx hash: %v", errs)
+	return nil, fmt.Errorf("failed to get quote request by tx hash from all relayer clients (%d): %v", len(errs), errs)

This change provides more context about the number of failed attempts.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 9dc780d and a4cd9aa.

📒 Files selected for processing (1)
  • contrib/opbot/internal/client.go (1 hunks)
🧰 Additional context used
🔇 Additional comments (3)
contrib/opbot/internal/client.go (3)

1-14: LGTM: Package declaration and imports are appropriate.

The package name "internal" is suitable for an internal implementation. The imports include necessary standard library packages and relevant external dependencies for the RFQ client implementation.


20-26: LGTM: RFQClient interface is well-defined.

The RFQClient interface is clearly defined with two methods, GetRFQByTxID and GetRFQByTxHash. Each method has a descriptive comment explaining its purpose.


1-97: Overall, the RFQ client implementation is well-structured and follows good practices.

The code addresses previous review comments and implements proper error handling. Minor suggestions for improvements have been made in this review.

As mentioned in a previous review comment, it's important to improve test coverage for this file. Please add unit tests for the following:

  1. NewRFQClient function
  2. GetRFQByTxID method
  3. GetRFQByTxHash method

These tests should cover various scenarios, including successful requests, error handling, and edge cases. To verify the current test coverage, run the following command:

This will help ensure the reliability and maintainability of the code.

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: 6

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between a4cd9aa and 7c12249.

📒 Files selected for processing (1)
  • contrib/opbot/botmd/commands.go (5 hunks)
🧰 Additional context used
🪛 GitHub Check: codecov/patch
contrib/opbot/botmd/commands.go

[warning] 167-170: contrib/opbot/botmd/commands.go#L167-L170
Added lines #L167 - L170 were not covered by tests


[warning] 179-201: contrib/opbot/botmd/commands.go#L179-L201
Added lines #L179 - L201 were not covered by tests


[warning] 203-213: contrib/opbot/botmd/commands.go#L203-L213
Added lines #L203 - L213 were not covered by tests


[warning] 216-218: contrib/opbot/botmd/commands.go#L216-L218
Added lines #L216 - L218 were not covered by tests


[warning] 243-243: contrib/opbot/botmd/commands.go#L243
Added line #L243 was not covered by tests


[warning] 306-309: contrib/opbot/botmd/commands.go#L306-L309
Added lines #L306 - L309 were not covered by tests


[warning] 313-313: contrib/opbot/botmd/commands.go#L313
Added line #L313 was not covered by tests


[warning] 352-352: contrib/opbot/botmd/commands.go#L352
Added line #L352 was not covered by tests

Comment on lines 167 to 170
res, status, err := b.rfqClient.GetRFQByTxID(ctx.Context(), tx)
if err != nil {
b.logger.Errorf(ctx.Context(), "error fetching quote request: %v", err)
_, err := ctx.Response().Reply(fmt.Sprintf("error fetching quote request %s", err.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 unit tests for error handling in rfqLookupCommand

Lines 167-170 introduce new error handling when fetching the quote request using GetRFQByTxID. These lines are not covered by unit tests. Consider adding tests to ensure this error handling behaves as expected and to improve code coverage.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 167-170: contrib/opbot/botmd/commands.go#L167-L170
Added lines #L167 - L170 were not covered by tests

Comment on lines +203 to +213
if status == "Requested" {
objects = append(objects, &slack.TextBlockObject{
Type: slack.MarkdownType,
Text: "*DestTxHash*: not available",
})
} else {
//nolint: gosec
objects = append(objects, &slack.TextBlockObject{
Type: slack.MarkdownType,
Text: fmt.Sprintf("*DestTxHash*: %s", toTXSlackLink(res.BridgeRelay.TransactionHash, uint32(res.Bridge.DestChainID))),
})
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 unit tests for conditional handling of DestTxHash

Lines 203-213 introduce conditional logic to handle the presence or absence of DestTxHash based on the status. This conditional logic is not currently covered by unit tests. Consider adding tests to verify that the correct message is displayed for each possible status.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 203-213: contrib/opbot/botmd/commands.go#L203-L213
Added lines #L203 - L213 were not covered by tests

Comment on lines +216 to +218
slackBlocks = append(slackBlocks, slack.NewSectionBlock(nil, objects, nil))

_, err = ctx.Response().ReplyBlocks(slackBlocks, slacker.WithUnfurlLinks(false))
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 errors when sending Slack message blocks

When sending Slack message blocks (lines 216-218), if an error occurs, it is logged but not communicated to the user. Consider notifying the user when message delivery fails to enhance error transparency and user experience.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 216-218: contrib/opbot/botmd/commands.go#L216-L218
Added lines #L216 - L218 were not covered by tests

break
}
}
rawRequest, err := b.rfqClient.GetRFQByTxHash(ctx.Context(), tx)
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 unit tests for fetching quote request in rfqRefund

Line 243 adds a call to GetRFQByTxHash in the rfqRefund command. This line is not covered by unit tests. Adding tests will help ensure that fetching the quote request and error handling work correctly.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 243-243: contrib/opbot/botmd/commands.go#L243
Added line #L243 was not covered by tests

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: 3

🧹 Outside diff range and nitpick comments (2)
contrib/opbot/botmd/commands.go (2)

Line range hint 243-287: Improve error handling in refund submission process

The refactored code simplifies the refund process by using a dedicated client and improves overall error handling. However, there's room for improvement in error propagation during the transaction submission:

On lines 278-287, the error from b.submitter.SubmitTransaction is logged but not propagated to the user. This could lead to silent failures where the user is not informed of the submission error.

Suggestion:

nonce, err := b.submitter.SubmitTransaction(
    ctx.Context(),
    big.NewInt(int64(rawRequest.Bridge.OriginChainID)),
    func(transactor *bind.TransactOpts) (tx *types.Transaction, err error) {
        tx, err = fastBridgeContract.Refund(transactor, common.Hex2Bytes(rawRequest.Bridge.Request))
        if err != nil {
            return nil, fmt.Errorf("error submitting refund: %w", err)
        }
        return tx, nil
    })
if err != nil {
-   log.Printf("error submitting refund: %v\n", err)
-   return
+   b.logger.Errorf(ctx.Context(), "error submitting refund: %v", err)
+   _, replyErr := ctx.Response().Reply("Error submitting refund. Please try again later or contact support.")
+   if replyErr != nil {
+       log.Println(replyErr)
+   }
+   return
}

This change ensures that the user is informed of submission errors while maintaining appropriate error logging for debugging purposes.


Line range hint 324-349: Enhance error wrapping in makeFastBridge function

The changes in the makeFastBridge function align well with the new RFQ client implementation. The error handling has been improved, but there's an opportunity to enhance error wrapping for better debugging:

Consider using fmt.Errorf with %w verb consistently for all error wrapping. This allows for better error inspection and handling up the call stack. Apply this change:

func (b *Bot) makeFastBridge(ctx context.Context, req *internal.GetRFQByTxIDResponse) (*fastbridge.FastBridge, error) {
    client, err := rfqClient.NewUnauthenticatedClient(b.handler, b.cfg.RFQApiURL)
    if err != nil {
        return nil, fmt.Errorf("error creating rfq client: %w", err)
    }

    contracts, err := client.GetRFQContracts(ctx)
    if err != nil {
        return nil, fmt.Errorf("error fetching rfq contracts: %w", err)
    }

    chainClient, err := b.rpcClient.GetChainClient(ctx, int(req.Bridge.OriginChainID))
    if err != nil {
        return nil, fmt.Errorf("error getting chain client: %w", err)
    }

    contractAddress, ok := contracts.Contracts[uint32(req.Bridge.OriginChainID)]
    if !ok {
-       return nil, errors.New("contract address not found")
+       return nil, fmt.Errorf("contract address not found for chain ID %d", req.Bridge.OriginChainID)
    }

    fastBridgeHandle, err := fastbridge.NewFastBridge(common.HexToAddress(contractAddress), chainClient)
    if err != nil {
        return nil, fmt.Errorf("error creating fast bridge: %w", err)
    }
    return fastBridgeHandle, nil
}

This change provides more context in the error message and maintains consistency in error wrapping throughout the function.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 7835278 and 56bcab9.

📒 Files selected for processing (2)
  • contrib/opbot/botmd/commands.go (10 hunks)
  • contrib/opbot/internal/model.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • contrib/opbot/internal/model.go
🧰 Additional context used
🔇 Additional comments (2)
contrib/opbot/botmd/commands.go (2)

23-28: New imports align with refactored RFQ functionality

The added imports for the internal package and RFQ client are consistent with the changes made to the RFQ lookup and refund functionality. These additions support the new implementation.


355-358: 🛠️ Refactor suggestion

Improve hash prefix handling in toExplorerSlackLink

The current implementation only handles the uppercase "0X" prefix. To ensure consistent behavior with different hash formats, consider updating the condition to handle both uppercase and lowercase prefixes.

Apply this diff to address the issue:

-if strings.HasPrefix(rfqHash, "0X") {
+if strings.HasPrefix(rfqHash, "0X") || strings.HasPrefix(rfqHash, "0x") {
     rfqHash = strings.ToLower(rfqHash[2:])
 }

This change will ensure that both "0X" and "0x" prefixes are handled consistently, improving the robustness of the function.

Likely invalid or redundant comment.

Comment on lines +203 to +218
if status == "Requested" {
objects = append(objects, &slack.TextBlockObject{
Type: slack.MarkdownType,
Text: "*DestTxHash*: not available",
})
} else {
//nolint: gosec
objects = append(objects, &slack.TextBlockObject{
Type: slack.MarkdownType,
Text: fmt.Sprintf("*DestTxHash*: %s", toTXSlackLink(res.BridgeRelay.TransactionHash, uint32(res.Bridge.DestChainID))),
})
}

_, err := ctx.Response().ReplyBlocks(slackBlocks, slacker.WithUnfurlLinks(false))
slackBlocks = append(slackBlocks, slack.NewSectionBlock(nil, objects, nil))

_, err = ctx.Response().ReplyBlocks(slackBlocks, slacker.WithUnfurlLinks(false))
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

⚠️ Potential issue

Improve DestTxHash handling and address potential integer overflow

The conditional logic for handling different RFQ statuses improves the user experience by providing relevant information. However, there are two areas for improvement:

  1. DRY Principle: The code for adding TextBlockObjects is repeated. Consider refactoring to a helper function:
func addTextBlockObject(objects []*slack.TextBlockObject, text string) []*slack.TextBlockObject {
    return append(objects, &slack.TextBlockObject{
        Type: slack.MarkdownType,
        Text: text,
    })
}
  1. Integer Overflow: There's another potential integer overflow when converting res.Bridge.DestChainID to uint32 on line 212. Apply the same safe casting solution as suggested for the OriginChainID.

Apply these changes to address both issues:

func addTextBlockObject(objects []*slack.TextBlockObject, text string) []*slack.TextBlockObject {
    return append(objects, &slack.TextBlockObject{
        Type: slack.MarkdownType,
        Text: text,
    })
}

// In the main function
if status == "Requested" {
-   objects = append(objects, &slack.TextBlockObject{
-       Type: slack.MarkdownType,
-       Text: "*DestTxHash*: not available",
-   })
+   objects = addTextBlockObject(objects, "*DestTxHash*: not available")
} else {
-   objects = append(objects, &slack.TextBlockObject{
-       Type: slack.MarkdownType,
-       Text: fmt.Sprintf("*DestTxHash*: %s", toTXSlackLink(res.BridgeRelay.TransactionHash, uint32(res.Bridge.DestChainID))),
-   })
+   objects = addTextBlockObject(objects, fmt.Sprintf("*DestTxHash*: %s", toTXSlackLink(res.BridgeRelay.TransactionHash, safeCastToUint32(res.Bridge.DestChainID))))
}

These changes will improve code reusability and prevent potential runtime errors due to integer overflow.

Comment on lines +160 to +170
Description: "find a rfq transaction by either tx hash or txid from the rfq-indexer api",
Examples: []string{
"rfq 0x30f96b45ba689c809f7e936c140609eb31c99b182bef54fccf49778716a7e1ca",
},
Handler: func(ctx *slacker.CommandContext) {
type Status struct {
relayer string
*relapi.GetQuoteRequestResponse
}

var statuses []Status
var sliceMux sync.Mutex

if len(b.cfg.RelayerURLS) == 0 {
_, err := ctx.Response().Reply("no relayer urls configured")
if err != nil {
log.Println(err)
}
return
}

tx := stripLinks(ctx.Request().Param("tx"))

var wg sync.WaitGroup
// 2 routines per relayer, one for tx hashh one for tx id
wg.Add(len(b.cfg.RelayerURLS) * 2)
for _, relayer := range b.cfg.RelayerURLS {
client := relapi.NewRelayerClient(b.handler, relayer)
go func() {
defer wg.Done()
res, err := client.GetQuoteRequestByTxHash(ctx.Context(), tx)
if err != nil {
log.Printf("error fetching quote request status by tx hash: %v\n", err)
return
}
sliceMux.Lock()
defer sliceMux.Unlock()
statuses = append(statuses, Status{relayer: relayer, GetQuoteRequestResponse: res})
}()

go func() {
defer wg.Done()
res, err := client.GetQuoteRequestByTXID(ctx.Context(), tx)
if err != nil {
log.Printf("error fetching quote request status by tx id: %v\n", err)
return
}
sliceMux.Lock()
defer sliceMux.Unlock()
statuses = append(statuses, Status{relayer: relayer, GetQuoteRequestResponse: res})
}()
}
wg.Wait()

if len(statuses) == 0 {
_, err := ctx.Response().Reply("no quote request found")
res, status, err := b.rfqClient.GetRFQ(ctx.Context(), tx)
if err != nil {
b.logger.Errorf(ctx.Context(), "error fetching quote request: %v", err)
_, err := ctx.Response().Reply(fmt.Sprintf("error fetching quote request %s", err.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

Improve error message handling in rfqLookupCommand

The refactored code simplifies the RFQ lookup process by using a dedicated client, which is a good improvement. However, there's a potential issue with error message construction:

On line 170, the error message directly includes err.Error(). This could lead to leaking sensitive information or presenting unclear messages to the user. Consider using a more generic error message or sanitizing the error before including it in the response.

Suggestion:

- _, err := ctx.Response().Reply(fmt.Sprintf("error fetching quote request %s", err.Error()))
+ _, err := ctx.Response().Reply("Error fetching quote request. Please try again later or contact support.")

Also, consider logging the detailed error for debugging purposes while keeping the user-facing message generic.

📝 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
Description: "find a rfq transaction by either tx hash or txid from the rfq-indexer api",
Examples: []string{
"rfq 0x30f96b45ba689c809f7e936c140609eb31c99b182bef54fccf49778716a7e1ca",
},
Handler: func(ctx *slacker.CommandContext) {
type Status struct {
relayer string
*relapi.GetQuoteRequestResponse
}
var statuses []Status
var sliceMux sync.Mutex
if len(b.cfg.RelayerURLS) == 0 {
_, err := ctx.Response().Reply("no relayer urls configured")
if err != nil {
log.Println(err)
}
return
}
tx := stripLinks(ctx.Request().Param("tx"))
var wg sync.WaitGroup
// 2 routines per relayer, one for tx hashh one for tx id
wg.Add(len(b.cfg.RelayerURLS) * 2)
for _, relayer := range b.cfg.RelayerURLS {
client := relapi.NewRelayerClient(b.handler, relayer)
go func() {
defer wg.Done()
res, err := client.GetQuoteRequestByTxHash(ctx.Context(), tx)
if err != nil {
log.Printf("error fetching quote request status by tx hash: %v\n", err)
return
}
sliceMux.Lock()
defer sliceMux.Unlock()
statuses = append(statuses, Status{relayer: relayer, GetQuoteRequestResponse: res})
}()
go func() {
defer wg.Done()
res, err := client.GetQuoteRequestByTXID(ctx.Context(), tx)
if err != nil {
log.Printf("error fetching quote request status by tx id: %v\n", err)
return
}
sliceMux.Lock()
defer sliceMux.Unlock()
statuses = append(statuses, Status{relayer: relayer, GetQuoteRequestResponse: res})
}()
}
wg.Wait()
if len(statuses) == 0 {
_, err := ctx.Response().Reply("no quote request found")
res, status, err := b.rfqClient.GetRFQ(ctx.Context(), tx)
if err != nil {
b.logger.Errorf(ctx.Context(), "error fetching quote request: %v", err)
_, err := ctx.Response().Reply(fmt.Sprintf("error fetching quote request %s", err.Error()))
Description: "find a rfq transaction by either tx hash or txid from the rfq-indexer api",
Examples: []string{
"rfq 0x30f96b45ba689c809f7e936c140609eb31c99b182bef54fccf49778716a7e1ca",
},
Handler: func(ctx *slacker.CommandContext) {
tx := stripLinks(ctx.Request().Param("tx"))
res, status, err := b.rfqClient.GetRFQ(ctx.Context(), tx)
if err != nil {
b.logger.Errorf(ctx.Context(), "error fetching quote request: %v", err)
_, err := ctx.Response().Reply("Error fetching quote request. Please try again later or contact support.")

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: 5

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 56bcab9 and 5e3f0e2.

📒 Files selected for processing (1)
  • contrib/opbot/botmd/commands.go (9 hunks)
🧰 Additional context used
🪛 GitHub Check: codecov/patch
contrib/opbot/botmd/commands.go

[warning] 160-160: contrib/opbot/botmd/commands.go#L160
Added line #L160 was not covered by tests


[warning] 167-170: contrib/opbot/botmd/commands.go#L167-L170
Added lines #L167 - L170 were not covered by tests


[warning] 179-201: contrib/opbot/botmd/commands.go#L179-L201
Added lines #L179 - L201 were not covered by tests


[warning] 203-213: contrib/opbot/botmd/commands.go#L203-L213
Added lines #L203 - L213 were not covered by tests


[warning] 216-218: contrib/opbot/botmd/commands.go#L216-L218
Added lines #L216 - L218 were not covered by tests


[warning] 243-243: contrib/opbot/botmd/commands.go#L243
Added line #L243 was not covered by tests


[warning] 262-262: contrib/opbot/botmd/commands.go#L262
Added line #L262 was not covered by tests


[warning] 278-286: contrib/opbot/botmd/commands.go#L278-L286
Added lines #L278 - L286 were not covered by tests


[warning] 297-297: contrib/opbot/botmd/commands.go#L297
Added line #L297 was not covered by tests


[warning] 309-312: contrib/opbot/botmd/commands.go#L309-L312
Added lines #L309 - L312 were not covered by tests


[warning] 316-316: contrib/opbot/botmd/commands.go#L316
Added line #L316 was not covered by tests


[warning] 324-324: contrib/opbot/botmd/commands.go#L324
Added line #L324 was not covered by tests


[warning] 335-335: contrib/opbot/botmd/commands.go#L335
Added line #L335 was not covered by tests


[warning] 340-340: contrib/opbot/botmd/commands.go#L340
Added line #L340 was not covered by tests


[warning] 355-355: contrib/opbot/botmd/commands.go#L355
Added line #L355 was not covered by tests

🪛 GitHub Check: Lint (contrib/opbot)
contrib/opbot/botmd/commands.go

[failure] 316-316:
G115: integer overflow conversion int -> uint32 (gosec)


[failure] 340-340:
G115: integer overflow conversion int -> uint32 (gosec)


[failure] 335-335:
unnecessary conversion (unconvert)

🔇 Additional comments (3)
contrib/opbot/botmd/commands.go (3)

23-23: LGTM: Good use of internal package

The addition of the internal package import is a positive change, promoting better code organization and encapsulation.


Line range hint 1-378: LGTM: Improved code structure and consistency

The refactoring to use the new RFQ client and the consistent use of Slack block objects for message construction have improved the overall code structure and readability. The error handling has been updated consistently throughout the file, which is a positive change.

These changes make the code more maintainable and easier to understand. Good job on the refactoring effort!

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 179-201: contrib/opbot/botmd/commands.go#L179-L201
Added lines #L179 - L201 were not covered by tests


[warning] 203-213: contrib/opbot/botmd/commands.go#L203-L213
Added lines #L203 - L213 were not covered by tests


[warning] 216-218: contrib/opbot/botmd/commands.go#L216-L218
Added lines #L216 - L218 were not covered by tests


Line range hint 160-358: Improve test coverage for new and modified code

The static analysis indicates that many of the new and modified lines of code are not covered by tests. This includes critical parts of the rfqLookupCommand, rfqRefund, and makeFastBridge functions.

To improve the overall quality and reliability of the code, please add or update unit tests to cover the new functionality. This will help prevent regressions and make future refactoring easier.

Here's a shell script to check the current test coverage:

Please run this script and aim to increase the coverage, particularly for the modified functions.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 179-201: contrib/opbot/botmd/commands.go#L179-L201
Added lines #L179 - L201 were not covered by tests


[warning] 203-213: contrib/opbot/botmd/commands.go#L203-L213
Added lines #L203 - L213 were not covered by tests


[warning] 216-218: contrib/opbot/botmd/commands.go#L216-L218
Added lines #L216 - L218 were not covered by tests

{
Type: slack.MarkdownType,
//nolint: gosec
Text: fmt.Sprintf("*OriginTxHash*: %s", toTXSlackLink(res.BridgeRequest.TransactionHash, uint32(res.Bridge.OriginChainID))),
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

Potential integer overflow when converting chain ID

The conversion of res.Bridge.OriginChainID to uint32 could lead to an integer overflow if the chain ID exceeds the maximum value of uint32 (4,294,967,295).

Consider implementing a safe conversion function:

func safeUint32(v int) uint32 {
    if v < 0 || v > math.MaxUint32 {
        // Handle error or return a default value
        return 0
    }
    return uint32(v)
}

Then use it in the function:

- Text: fmt.Sprintf("*OriginTxHash*: %s", toTXSlackLink(res.BridgeRequest.TransactionHash, uint32(res.Bridge.OriginChainID))),
+ Text: fmt.Sprintf("*OriginTxHash*: %s", toTXSlackLink(res.BridgeRequest.TransactionHash, safeUint32(res.Bridge.OriginChainID))),

if err != nil {
return nil, fmt.Errorf("error getting chain client: %w", err)
}

contractAddress, ok := contracts.Contracts[req.OriginChainID]
contractAddress, ok := contracts.Contracts[uint32(req.Bridge.OriginChainID)]
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

Potential integer overflow when accessing contract address

The conversion of req.Bridge.OriginChainID to uint32 could lead to an integer overflow if the chain ID exceeds the maximum value of uint32 (4,294,967,295).

Consider using a safe conversion function similar to the one suggested earlier:

- contractAddress, ok := contracts.Contracts[uint32(req.Bridge.OriginChainID)]
+ contractAddress, ok := contracts.Contracts[safeUint32(req.Bridge.OriginChainID)]

Where safeUint32 is defined as:

func safeUint32(v int) uint32 {
    if v < 0 || v > math.MaxUint32 {
        // Handle error or return a default value
        return 0
    }
    return uint32(v)
}
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 340-340: contrib/opbot/botmd/commands.go#L340
Added line #L340 was not covered by tests

🪛 GitHub Check: Lint (contrib/opbot)

[failure] 340-340:
G115: integer overflow conversion int -> uint32 (gosec)

Comment on lines +355 to 358
if strings.HasPrefix(rfqHash, "0X") {
rfqHash = strings.ToLower(rfqHash[2:])
}

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

Improve hash prefix handling in toExplorerSlackLink

The current implementation only handles the uppercase "0X" prefix. To ensure consistent behavior with different hash formats, consider updating the condition to handle both uppercase and lowercase prefixes.

Apply this diff to address the issue:

- if strings.HasPrefix(rfqHash, "0X") {
+ if strings.HasPrefix(rfqHash, "0X") || strings.HasPrefix(rfqHash, "0x") {
    rfqHash = strings.ToLower(rfqHash[2:])
}

This change will ensure that both "0X" and "0x" prefixes are handled consistently.

📝 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 strings.HasPrefix(rfqHash, "0X") {
rfqHash = strings.ToLower(rfqHash[2:])
}
if strings.HasPrefix(rfqHash, "0X") || strings.HasPrefix(rfqHash, "0x") {
rfqHash = strings.ToLower(rfqHash[2:])
}
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 355-355: contrib/opbot/botmd/commands.go#L355
Added line #L355 was not covered by tests

Comment on lines +309 to +312
_, err := ctx.Response().Reply(fmt.Sprintf("refund submitted with nonce %d", nonce))
if err != nil {
log.Println(err)
}
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

Improve error handling after refund submission

The current implementation sends a success message even when an error occurs during the status check. This could mislead users about the state of their refund.

Consider modifying the error handling to provide more accurate feedback:

if err != nil {
    b.logger.Errorf(ctx.Context(), "error fetching quote request: %v", err)
-   _, err := ctx.Response().Reply(fmt.Sprintf("refund submitted with nonce %d", nonce))
+   _, err := ctx.Response().Reply(fmt.Sprintf("Refund submitted with nonce %d, but there was an error confirming the status. Please check later.", nonce))
    if err != nil {
        log.Println(err)
    }
    return
}

Committable suggestion was skipped due to low confidence.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 309-312: contrib/opbot/botmd/commands.go#L309-L312
Added lines #L309 - L312 were not covered by tests

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: 3

🧹 Outside diff range and nitpick comments (1)
contrib/opbot/botmd/commands.go (1)

Line range hint 297-301: Improve error handling in retry block.

The error handling combines both nil check and HasTx check in a single error message, making it harder to diagnose issues.

Apply this diff to improve error handling:

status, err = b.submitter.GetSubmissionStatus(ctx, big.NewInt(int64(rawRequest.Bridge.OriginChainID)), nonce)
-if err != nil || !status.HasTx() {
-    b.logger.Errorf(ctx, "error fetching quote request: %v", err)
-    return fmt.Errorf("error fetching quote request: %w", err)
+if err != nil {
+    b.logger.Errorf(ctx, "failed to get submission status: %v", err)
+    return fmt.Errorf("failed to get submission status: %w", err)
+}
+if !status.HasTx() {
+    return errors.New("transaction not found in submission status")
}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 5e3f0e2 and 88f9165.

📒 Files selected for processing (2)
  • contrib/opbot/botmd/commands.go (10 hunks)
  • services/rfq/relayer/quoter/quoter_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • services/rfq/relayer/quoter/quoter_test.go
🧰 Additional context used
🪛 GitHub Check: Lint (contrib/opbot)
contrib/opbot/botmd/commands.go

[failure] 316-316:
G115: integer overflow conversion int -> uint32 (gosec)


[failure] 340-340:
G115: integer overflow conversion int -> uint32 (gosec)


[failure] 335-335:
unnecessary conversion (unconvert)

🔇 Additional comments (1)
contrib/opbot/botmd/commands.go (1)

340-340: ⚠️ Potential issue

Use safe chain ID conversion in contract lookup.

Direct conversion of chain ID to uint32 in map lookup could cause integer overflow.

Apply this diff:

-contractAddress, ok := contracts.Contracts[uint32(req.Bridge.OriginChainID)]
+contractAddress, ok := contracts.Contracts[safeChainIDToUint32(req.Bridge.OriginChainID)]

Likely invalid or redundant comment.

🧰 Tools
🪛 GitHub Check: Lint (contrib/opbot)

[failure] 340-340:
G115: integer overflow conversion int -> uint32 (gosec)

Comment on lines +194 to +195
//nolint: gosec
Text: fmt.Sprintf("*OriginTxHash*: %s", toTXSlackLink(res.BridgeRequest.TransactionHash, uint32(res.Bridge.OriginChainID))),
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 safe chain ID conversion to prevent integer overflow.

The direct conversion of chain IDs to uint32 could cause integer overflow if the values exceed 4,294,967,295.

Add a safe conversion function and use it:

+func safeChainIDToUint32(chainID int) uint32 {
+    if chainID < 0 || chainID > math.MaxUint32 {
+        // Log the error and return a default value
+        return 0
+    }
+    return uint32(chainID)
+}

// Use in both places:
-Text: fmt.Sprintf("*OriginTxHash*: %s", toTXSlackLink(res.BridgeRequest.TransactionHash, uint32(res.Bridge.OriginChainID))),
+Text: fmt.Sprintf("*OriginTxHash*: %s", toTXSlackLink(res.BridgeRequest.TransactionHash, safeChainIDToUint32(res.Bridge.OriginChainID))),

-Text: fmt.Sprintf("*DestTxHash*: %s", toTXSlackLink(res.BridgeRelay.TransactionHash, uint32(res.Bridge.DestChainID))),
+Text: fmt.Sprintf("*DestTxHash*: %s", toTXSlackLink(res.BridgeRelay.TransactionHash, safeChainIDToUint32(res.Bridge.DestChainID))),

Also applies to: 209-212

return
}

_, err = ctx.Response().Reply(fmt.Sprintf("refund submitted: %s", toExplorerSlackLink(status.TxHash().String())))
_, err = ctx.Response().Reply(fmt.Sprintf("refund submitted: %s", toTXSlackLink(status.TxHash().String(), uint32(rawRequest.Bridge.OriginChainID))))
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

Use safe chain ID conversion in toTXSlackLink call.

Similar to previous occurrences, direct conversion of chain ID to uint32 could cause integer overflow.

Apply this diff:

-_, err = ctx.Response().Reply(fmt.Sprintf("refund submitted: %s", toTXSlackLink(status.TxHash().String(), uint32(rawRequest.Bridge.OriginChainID))))
+_, err = ctx.Response().Reply(fmt.Sprintf("refund submitted: %s", toTXSlackLink(status.TxHash().String(), safeChainIDToUint32(rawRequest.Bridge.OriginChainID))))

Committable suggestion was skipped due to low confidence.

🧰 Tools
🪛 GitHub Check: Lint (contrib/opbot)

[failure] 316-316:
G115: integer overflow conversion int -> uint32 (gosec)

Comment on lines +167 to +170
res, status, err := b.rfqClient.GetRFQ(ctx.Context(), tx)
if err != nil {
b.logger.Errorf(ctx.Context(), "error fetching quote request: %v", err)
_, err := ctx.Response().Reply(fmt.Sprintf("error fetching quote request %s", err.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

Improve error handling to avoid exposing sensitive information.

The current implementation directly exposes the error message to users, which could potentially leak sensitive information.

Apply this diff to improve error handling:

if err != nil {
    b.logger.Errorf(ctx.Context(), "error fetching quote request: %v", err)
-   _, err := ctx.Response().Reply(fmt.Sprintf("error fetching quote request %s", err.Error()))
+   _, err := ctx.Response().Reply("Failed to fetch quote request. Please try again later.")
    if err != nil {
        log.Println(err)
    }
    return
}
📝 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
res, status, err := b.rfqClient.GetRFQ(ctx.Context(), tx)
if err != nil {
b.logger.Errorf(ctx.Context(), "error fetching quote request: %v", err)
_, err := ctx.Response().Reply(fmt.Sprintf("error fetching quote request %s", err.Error()))
res, status, err := b.rfqClient.GetRFQ(ctx.Context(), tx)
if err != nil {
b.logger.Errorf(ctx.Context(), "error fetching quote request: %v", err)
_, err := ctx.Response().Reply("Failed to fetch quote request. Please try again later.")
if err != nil {
log.Println(err)
}
return
}

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 (2)
contrib/opbot/internal/client.go (2)

1-2: Enhance package documentation.

Consider adding more comprehensive package documentation that describes:

  • The purpose of the RFQ client
  • Key components and responsibilities
  • Usage examples or guidelines
-// Package internal provides the RFQ client implementation.
+// Package internal provides the RFQ (Request for Quote) client implementation.
+// It offers functionality to interact with RFQ services through a clean interface,
+// supporting operations such as retrieving quote requests by transaction ID or hash.
+// The package handles communication with both the indexer and relayer services.

16-18: Make the route constant name more descriptive.

The current constant name getRFQRoute could be more specific about its purpose.

-	getRFQRoute = "/transaction-id/%s"
+	getRFQByTxIDRoute = "/transaction-id/%s"
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 88f9165 and 643c0dc.

📒 Files selected for processing (1)
  • contrib/opbot/internal/client.go (1 hunks)
🧰 Additional context used
🪛 GitHub Check: codecov/patch
contrib/opbot/internal/client.go

[warning] 32-42: contrib/opbot/internal/client.go#L32-L42
Added lines #L32 - L42 were not covered by tests


[warning] 44-47: contrib/opbot/internal/client.go#L44-L47
Added lines #L44 - L47 were not covered by tests


[warning] 51-58: contrib/opbot/internal/client.go#L51-L58
Added lines #L51 - L58 were not covered by tests


[warning] 60-62: contrib/opbot/internal/client.go#L60-L62
Added lines #L60 - L62 were not covered by tests


[warning] 64-75: contrib/opbot/internal/client.go#L64-L75
Added lines #L64 - L75 were not covered by tests


[warning] 78-78: contrib/opbot/internal/client.go#L78
Added line #L78 was not covered by tests

🔇 Additional comments (1)
contrib/opbot/internal/client.go (1)

51-79: 🛠️ Refactor suggestion

Add timeout and response validation.

The implementation could be improved in several ways:

  1. Add context timeout for the request
  2. Validate the response body
  3. Add comprehensive test coverage
 func (r *rfqClientImpl) GetRFQ(ctx context.Context, txIdentifier string) (*GetRFQByTxIDResponse, string, error) {
+	if txIdentifier == "" {
+		return nil, "", fmt.Errorf("transaction identifier cannot be empty")
+	}
+
+	// Add timeout to prevent long-running requests
+	ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
+	defer cancel()
+
 	var res GetRFQByTxIDResponse
 	resp, err := r.client.R().SetContext(ctx).
 		SetResult(&res).
 		Get(fmt.Sprintf(getRFQRoute, txIdentifier))
 	if err != nil {
 		return nil, "", fmt.Errorf("failed to get quote request by tx ID: %w", err)
 	}
 
 	if resp.StatusCode() != http.StatusOK {
-		return nil, "", fmt.Errorf("unexpected status code: %d", resp.StatusCode())
+		return nil, "", fmt.Errorf("unexpected status code: %d, body: %s", resp.StatusCode(), resp.String())
 	}

Consider adding the following test cases:

  • Test successful request
  • Test timeout scenario
  • Test various status responses
  • Test error conditions
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 51-58: contrib/opbot/internal/client.go#L51-L58
Added lines #L51 - L58 were not covered by tests


[warning] 60-62: contrib/opbot/internal/client.go#L60-L62
Added lines #L60 - L62 were not covered by tests


[warning] 64-75: contrib/opbot/internal/client.go#L64-L75
Added lines #L64 - L75 were not covered by tests


[warning] 78-78: contrib/opbot/internal/client.go#L78
Added line #L78 was not covered by tests

Comment on lines 32 to 48
func NewRFQClient(handler metrics.Handler, indexerURL string, relayerURLs []string) RFQClient {
client := resty.New()
client.SetBaseURL(indexerURL)
client.SetHeader(headers.UserAgent, "rfq-client")

otelresty.TraceClient(client, otelresty.WithTracerProvider(handler.GetTracerProvider()))

var relayerClients []relapi.RelayerClient
for _, url := range relayerURLs {
relayerClients = append(relayerClients, relapi.NewRelayerClient(handler, url))
}

return &rfqClientImpl{
client: client,
relayerClients: relayerClients,
}
}
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

⚠️ Potential issue

Add input validation and test coverage for RFQClient constructor

The review comment's suggestions are valid as:

  • There are no tests for the NewRFQClient constructor in the package
  • URL validation is a common practice in the codebase, as seen in other components that panic on empty inputs
  • The constructor lacks validation for critical URL parameters that could cause runtime issues if empty

Required changes:

  • Add input validation as suggested in the review
  • Create unit tests for the constructor in contrib/opbot/internal/client_test.go
🔗 Analysis chain

Add input validation and error handling.

The constructor should validate its inputs to ensure neither the indexer URL nor relayer URLs are empty.

 func NewRFQClient(handler metrics.Handler, indexerURL string, relayerURLs []string) RFQClient {
+	if indexerURL == "" {
+		panic("indexer URL cannot be empty")
+	}
+	if len(relayerURLs) == 0 {
+		panic("at least one relayer URL must be provided")
+	}
+
 	client := resty.New()

Additionally, consider adding unit tests to cover the constructor's functionality:

  • Test with valid inputs
  • Test with invalid inputs
  • Verify client configuration
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if there are any test files for this package
find . -name "*_test.go" -type f -exec grep -l "TestNewRFQClient" {} \;

Length of output: 74


Script:

#!/bin/bash
# Let's first check if there are any test files in the package directory
fd "_test.go" contrib/opbot/internal/

# Then let's check the current test coverage for client.go
rg -l "NewRFQClient" contrib/opbot/internal/

Length of output: 115


Script:

#!/bin/bash
# Let's check if there are any existing tests in the package and their patterns
rg "func Test" contrib/opbot/internal/ -A 2

# Also check if there are any URL validations in other constructors
ast-grep --pattern 'if $_ == "" {
  panic($_)
}'

Length of output: 477

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 32-42: contrib/opbot/internal/client.go#L32-L42
Added lines #L32 - L42 were not covered by tests


[warning] 44-47: contrib/opbot/internal/client.go#L44-L47
Added lines #L44 - L47 were not covered by tests

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

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 643c0dc and 634144a.

📒 Files selected for processing (1)
  • ethergo/chaindata/chaindata.go (1 hunks)

Comment on lines +171 to +173
ChainID: 480,
ChainName: "world chain",
Explorer: "https://worldscan.org",
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

Standardize chain name format for consistency

The chain name "world chain" contains a space, which deviates from the naming pattern used in other entries (e.g., "ethereum", "arbitrum"). This might cause issues with API integrations or string matching.

Consider updating to remove the space:

-		ChainName: "world chain",
+		ChainName: "worldchain",
📝 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
ChainID: 480,
ChainName: "world chain",
Explorer: "https://worldscan.org",
ChainID: 480,
ChainName: "worldchain",
Explorer: "https://worldscan.org",

Comment on lines +165 to +174
{
ChainID: 59144,
ChainName: "linea",
Explorer: "https://lineascan.build",
},
{
ChainID: 480,
ChainName: "world chain",
Explorer: "https://worldscan.org",
},
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

⚠️ Duplicate Chain ID 66 detected for different chains

Found duplicate entries with ChainID 66:

This appears to be the same chain with different names (OKX Chain/OKC), but having duplicate entries could cause confusion or issues. Consider consolidating these entries into a single one with the most current chain name and explorer URL.

🔗 Analysis chain

LGTM! Chain metadata entries are well-structured

The new entries for Linea and World Chain follow the correct structure and include all required fields. The chain IDs and explorer URLs are properly formatted.

Let's verify there are no duplicate chain IDs:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for duplicate chain IDs in the file
# Expect: No duplicate chain IDs should be found

rg -o 'ChainID:\s+\d+' ethergo/chaindata/chaindata.go | sort | uniq -d

Length of output: 86


Script:

#!/bin/bash
# Let's get the full context of both occurrences of ChainID 66
rg -B2 -A2 'ChainID:\s+66' ethergo/chaindata/chaindata.go

Length of output: 251

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: 5

🧹 Outside diff range and nitpick comments (2)
contrib/opbot/botmd/commands.go (2)

160-160: Update command description to be more descriptive

The command description could be more informative about what the command actually does (e.g., retrieving and displaying RFQ transaction details).

-		Description: "find a rfq transaction by either tx hash or txid from the rfq-indexer api",
+		Description: "retrieve and display detailed information about an RFQ transaction using either transaction hash or ID",

Line range hint 324-328: Consider caching the RFQ client

The function creates a new unauthenticated RFQ client on every call, which could be inefficient for frequent requests. Consider creating the client once during bot initialization and reusing it.

 type Bot struct {
     // ... existing fields ...
+    rfqUnauthClient *rfqClient.Client
 }

 func NewBot(...) *Bot {
     // ... existing initialization ...
+    unauthClient, _ := rfqClient.NewUnauthenticatedClient(handler, cfg.RFQApiURL)
+    return &Bot{
         // ... existing fields ...
+        rfqUnauthClient: unauthClient,
     }
 }

 func (b *Bot) makeFastBridge(ctx context.Context, req *internal.GetRFQByTxIDResponse) (*fastbridge.FastBridge, error) {
-    client, err := rfqClient.NewUnauthenticatedClient(b.handler, b.cfg.RFQApiURL)
-    if err != nil {
-        return nil, fmt.Errorf("error creating rfq client: %w", err)
-    }
-
-    contracts, err := client.GetRFQContracts(ctx)
+    contracts, err := b.rfqUnauthClient.GetRFQContracts(ctx)
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 634144a and 72fa23b.

📒 Files selected for processing (3)
  • contrib/opbot/botmd/botmd.go (3 hunks)
  • contrib/opbot/botmd/commands.go (10 hunks)
  • contrib/opbot/internal/client.go (1 hunks)
🔇 Additional comments (5)
contrib/opbot/botmd/botmd.go (4)

9-9: LGTM: Import added for internal package

The new import is correctly added to support the RFQ client integration.


33-33: LGTM: RFQ client field added to Bot struct

The new rfqClient field is properly integrated into the Bot struct with appropriate type.


47-50: LGTM: Improved struct initialization formatting

The struct initialization is now more readable with explicit field names.


51-51: Verify RFQIndexerAPIURL configuration

Please ensure:

  1. The RFQIndexerAPIURL field exists in the config struct
  2. The URL is properly validated before being used
✅ Verification successful

Let me check for URL validation in the RFQ client initialization.


Let me try searching for the RFQ client implementation with a different approach.


RFQIndexerAPIURL configuration is properly handled

The verification shows that:

  1. The RFQIndexerAPIURL field exists in the config struct with proper YAML binding
  2. The URL is used correctly in NewRFQClient where it's set as the base URL for the REST client
  3. Basic validation is inherently handled by the resty client's SetBaseURL method
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify config field and validation

# Test 1: Check if RFQIndexerAPIURL exists in config
echo "Checking config struct..."
rg --type go "type\s+Config\s+struct" -A 20 config/config.go

# Test 2: Check for URL validation
echo "Checking URL validation..."
rg --type go "RFQIndexerAPIURL" -A 5 -B 5

Length of output: 1913


Script:

#!/bin/bash
# Check RFQ client initialization and URL validation
ast-grep --pattern 'func NewRFQClient($_, $_) {
  $$$
}'

Length of output: 58


Script:

#!/bin/bash
# Search for RFQ client implementation
echo "Searching for NewRFQClient implementation..."
rg --type go "func NewRFQClient" -A 10

echo "Searching for RFQ client URL usage..."
rg --type go "RFQIndexerAPIURL" -A 10 contrib/opbot/internal/

Length of output: 967

contrib/opbot/internal/client.go (1)

30-34: Add input validation for 'indexerURL' in 'NewRFQClient'.

Ensure that the indexerURL parameter is not empty to prevent potential runtime errors when initializing the client.

func (b *Bot) rfqRefund() *slacker.CommandDefinition {
return &slacker.CommandDefinition{
Command: "refund <tx>",
Description: "refund a quote request",
Description: "TESTING TESTING a quote request",
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

Update command description to reflect production status

The current description suggests this is a testing command ("TESTING TESTING"), but the implementation appears to be production-ready.

-		Description: "TESTING TESTING a quote request",
+		Description: "process a refund for the specified quote request transaction",
📝 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
Description: "TESTING TESTING a quote request",
Description: "process a refund for the specified quote request transaction",

Comment on lines +30 to +40
func NewRFQClient(handler metrics.Handler, indexerURL string) RFQClient {
client := resty.New()
client.SetBaseURL(indexerURL)
client.SetHeader(headers.UserAgent, "rfq-client")

otelresty.TraceClient(client, otelresty.WithTracerProvider(handler.GetTracerProvider()))

return &rfqClientImpl{
client: client,
}
}
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

Add unit tests for 'NewRFQClient' constructor.

To improve test coverage and ensure reliability, consider adding unit tests for the NewRFQClient function, covering cases such as valid and invalid inputs.

Comment on lines +43 to +71
func (r *rfqClientImpl) GetRFQ(ctx context.Context, txIdentifier string) (*GetRFQByTxIDResponse, string, error) {
var res GetRFQByTxIDResponse
resp, err := r.client.R().SetContext(ctx).
SetResult(&res).
Get(fmt.Sprintf(getRFQRoute, txIdentifier))
if err != nil {
return nil, "", fmt.Errorf("failed to get quote request by tx ID: %w", err)
}

if resp.StatusCode() != http.StatusOK {
return nil, "", fmt.Errorf("unexpected status code: %d", resp.StatusCode())
}

var status string
switch {
case res.BridgeClaim != (BridgeClaim{}):
status = "Claimed"
case res.BridgeProof != (BridgeProof{}):
status = "Proven"
case res.BridgeRelay != (BridgeRelay{}):
status = "Relayed"
case res.BridgeRequest != (BridgeRequest{}):
status = "Requested"
default:
status = "Unknown"
}

return &res, status, nil
}
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

Add unit tests for 'GetRFQ' method.

The GetRFQ method is essential for fetching quote requests. Adding unit tests for this method will enhance code quality and reliability.

SetResult(&res).
Get(fmt.Sprintf(getRFQRoute, txIdentifier))
if err != nil {
return nil, "", fmt.Errorf("failed to get quote request by tx ID: %w", err)
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

Clarify the error message in 'GetRFQ' method.

The error message specifies "tx ID", but txIdentifier can be a transaction ID or transaction hash. Consider updating the message for accuracy.

Apply this diff to fix the error message:

-return nil, "", fmt.Errorf("failed to get quote request by tx ID: %w", err)
+return nil, "", fmt.Errorf("failed to get quote request by transaction identifier: %w", err)
📝 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
return nil, "", fmt.Errorf("failed to get quote request by tx ID: %w", err)
return nil, "", fmt.Errorf("failed to get quote request by transaction identifier: %w", err)

}
}

// GetRFQByTxID gets a quote request by transaction ID or transaction hash.
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

Update the function comment to match the method name.

The comment refers to GetRFQByTxID, but the method is named GetRFQ. Please update the comment for consistency.

Apply this diff to fix the comment:

-// GetRFQByTxID gets a quote request by transaction ID or transaction hash.
+// GetRFQ gets a quote request by transaction ID or transaction hash.
📝 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
// GetRFQByTxID gets a quote request by transaction ID or transaction hash.
// GetRFQ gets a quote request by transaction ID or transaction hash.

Copy link
Collaborator

@ChiTimesChi ChiTimesChi left a comment

Choose a reason for hiding this comment

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

I believe this has been live for a while, so let's get this meeges once the lint issues are resolved.

#3375 can then be updated with latest master and will therefore be ready for staging

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 (1)
contrib/opbot/botmd/commands.go (1)

Line range hint 325-342: Consider caching RFQ contracts

The makeFastBridge function fetches RFQ contracts on every call, which could be inefficient for frequent requests.

Consider implementing a cache for the RFQ contracts with an appropriate TTL to reduce API calls:

type contractCache struct {
    contracts map[uint32]string
    expiry    time.Time
    mu        sync.RWMutex
}

func (b *Bot) getCachedContracts(ctx context.Context) (map[uint32]string, error) {
    b.contractCache.mu.RLock()
    if time.Now().Before(b.contractCache.expiry) {
        defer b.contractCache.mu.RUnlock()
        return b.contractCache.contracts, nil
    }
    b.contractCache.mu.RUnlock()
    
    // Fetch new contracts
    client, err := rfqClient.NewUnauthenticatedClient(b.handler, b.cfg.RFQApiURL)
    if err != nil {
        return nil, err
    }
    
    contracts, err := client.GetRFQContracts(ctx)
    if err != nil {
        return nil, err
    }
    
    b.contractCache.mu.Lock()
    defer b.contractCache.mu.Unlock()
    b.contractCache.contracts = contracts.Contracts
    b.contractCache.expiry = time.Now().Add(1 * time.Hour)
    return contracts.Contracts, nil
}
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 342-342: contrib/opbot/botmd/commands.go#L342
Added line #L342 was not covered by tests

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 72fa23b and 1fbfbfa.

📒 Files selected for processing (1)
  • contrib/opbot/botmd/commands.go (10 hunks)
🧰 Additional context used
🪛 GitHub Check: codecov/patch
contrib/opbot/botmd/commands.go

[warning] 160-160: contrib/opbot/botmd/commands.go#L160
Added line #L160 was not covered by tests


[warning] 167-170: contrib/opbot/botmd/commands.go#L167-L170
Added lines #L167 - L170 were not covered by tests


[warning] 179-201: contrib/opbot/botmd/commands.go#L179-L201
Added lines #L179 - L201 were not covered by tests


[warning] 203-213: contrib/opbot/botmd/commands.go#L203-L213
Added lines #L203 - L213 were not covered by tests


[warning] 216-218: contrib/opbot/botmd/commands.go#L216-L218
Added lines #L216 - L218 were not covered by tests


[warning] 243-243: contrib/opbot/botmd/commands.go#L243
Added line #L243 was not covered by tests


[warning] 262-262: contrib/opbot/botmd/commands.go#L262
Added line #L262 was not covered by tests


[warning] 278-286: contrib/opbot/botmd/commands.go#L278-L286
Added lines #L278 - L286 were not covered by tests


[warning] 297-297: contrib/opbot/botmd/commands.go#L297
Added line #L297 was not covered by tests


[warning] 309-312: contrib/opbot/botmd/commands.go#L309-L312
Added lines #L309 - L312 were not covered by tests


[warning] 317-317: contrib/opbot/botmd/commands.go#L317
Added line #L317 was not covered by tests


[warning] 325-325: contrib/opbot/botmd/commands.go#L325
Added line #L325 was not covered by tests


[warning] 342-342: contrib/opbot/botmd/commands.go#L342
Added line #L342 was not covered by tests


[warning] 357-357: contrib/opbot/botmd/commands.go#L357
Added line #L357 was not covered by tests

🔇 Additional comments (3)
contrib/opbot/botmd/commands.go (3)

23-23: LGTM: New import for internal package

The addition of the internal package import aligns with the refactoring to use the GraphQL API.


357-360: 🛠️ Refactor suggestion

Improve hash prefix handling

The current implementation only handles the uppercase "0X" prefix.

Apply this diff to handle both uppercase and lowercase prefixes consistently:

-if strings.HasPrefix(rfqHash, "0X") {
+if strings.HasPrefix(rfqHash, "0X") || strings.HasPrefix(rfqHash, "0x") {
    rfqHash = strings.ToLower(rfqHash[2:])
}

Likely invalid or redundant comment.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 357-357: contrib/opbot/botmd/commands.go#L357
Added line #L357 was not covered by tests


160-170: ⚠️ Potential issue

Add error context and improve error handling

The error message directly exposes internal error details to users, which could leak sensitive information.

Apply this diff to improve error handling:

if err != nil {
    b.logger.Errorf(ctx.Context(), "error fetching quote request: %v", err)
-   _, err := ctx.Response().Reply(fmt.Sprintf("error fetching quote request %s", err.Error()))
+   _, err := ctx.Response().Reply("Failed to fetch quote request. Please try again later.")
    if err != nil {
        log.Println(err)
    }
    return
}

Likely invalid or redundant comment.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 160-160: contrib/opbot/botmd/commands.go#L160
Added line #L160 was not covered by tests


[warning] 167-170: contrib/opbot/botmd/commands.go#L167-L170
Added lines #L167 - L170 were not covered by tests

Comment on lines +278 to +287
nonce, err := b.submitter.SubmitTransaction(
ctx.Context(),
big.NewInt(int64(rawRequest.Bridge.OriginChainID)),
func(transactor *bind.TransactOpts) (tx *types.Transaction, err error) {
tx, err = fastBridgeContract.Refund(transactor, common.Hex2Bytes(rawRequest.Bridge.Request[2:]))
if err != nil {
return nil, fmt.Errorf("error submitting refund: %w", err)
}
return tx, nil
})
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 error handling for transaction submission

The function returns early on error without notifying the user about the transaction submission failure.

Apply this diff to improve error handling:

if err != nil {
    log.Printf("error submitting refund: %v\n", err)
+   _, replyErr := ctx.Response().Reply("Failed to submit refund transaction. Please try again later.")
+   if replyErr != nil {
+       log.Println(replyErr)
+   }
    return
}

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

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 278-286: contrib/opbot/botmd/commands.go#L278-L286
Added lines #L278 - L286 were not covered by tests

Comment on lines +179 to +201
objects := []*slack.TextBlockObject{
{
Type: slack.MarkdownType,
Text: fmt.Sprintf("*Relayer*: %s", res.BridgeRelay.Relayer),
},
{
Type: slack.MarkdownType,
Text: fmt.Sprintf("*Status*: %s", status),
},
{
Type: slack.MarkdownType,
Text: fmt.Sprintf("*TxID*: %s", toExplorerSlackLink(res.Bridge.TransactionID)),
},
{
Type: slack.MarkdownType,
//nolint: gosec
Text: fmt.Sprintf("*OriginTxHash*: %s", toTXSlackLink(res.BridgeRequest.TransactionHash, uint32(res.Bridge.OriginChainID))),
},
{
Type: slack.MarkdownType,
Text: fmt.Sprintf("*Estimated Tx Age*: %s", humanize.Time(time.Unix(res.BridgeRelay.BlockTimestamp, 0))),
},
}
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

Add unit tests for Slack message construction and helper functions

The codebase currently lacks test coverage for Slack message construction and its helper functions. Specifically:

  • No tests exist for the Slack message block construction logic
  • Helper functions toExplorerSlackLink and toTXSlackLink are untested
  • Only a basic StripLinks utility function is tested

Key areas that need test coverage:

  • Message block construction with different bridge relay statuses
  • Link formatting for transaction hashes and explorer URLs
  • Edge cases for transaction hash formatting and chain IDs
🔗 Analysis chain

Add unit tests for Slack message construction

The new Slack message construction logic lacks test coverage, which could lead to undetected issues with message formatting or content.

Would you like me to help create unit tests for the Slack message construction logic?

Also applies to: 203-218

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for existing tests of Slack message construction
rg -l "Test.*SlackMessage" --type go

Length of output: 38


Script:

#!/bin/bash
# Let's check for any existing test files in the same directory
fd ".*_test.go" contrib/opbot/botmd/

# Also check for any test files that might be testing Slack-related functionality
rg -l "slack\." --type go | grep "_test"

# Look for the actual Slack message construction implementation
rg -B 5 -A 5 "slack\.TextBlockObject" --type go

Length of output: 2650


Script:

#!/bin/bash
# Let's examine the existing test files in the botmd directory to see if they contain any Slack-related tests
cat contrib/opbot/botmd/commands_test.go
cat contrib/opbot/botmd/export_test.go

# Also check for any helper functions that might be used in Slack message construction
rg -B 2 -A 2 "toTXSlackLink|toExplorerSlackLink" --type go

Length of output: 2572

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 179-201: contrib/opbot/botmd/commands.go#L179-L201
Added lines #L179 - L201 were not covered by tests

@golangisfun123 golangisfun123 merged commit dc1ca02 into master Nov 7, 2024
47 of 50 checks passed
@golangisfun123 golangisfun123 deleted the opbot-migration branch November 7, 2024 01:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
go Pull requests that update Go code needs-go-generate-services/rfq size/m
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants