-
Notifications
You must be signed in to change notification settings - Fork 33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(rfq-relayer): support FastBridgeV2 with arbitrary calls [SLT-320] #3258
base: master
Are you sure you want to change the base?
Changes from 74 commits
36a42bd
919a0a9
867d5d9
c93c403
9575246
7872f2a
ad7dd4c
5ca5ad1
e8355c3
eb2bbb8
c50042a
9fb4461
156e333
2b77b4a
271f59d
2317a58
c6a1fdc
e5e8646
ab63286
93d9b7d
e34a08b
1056ef1
1301270
1081e0a
467d5c7
f456bb7
de621a7
e3c1604
bff4f72
4ed813b
5b6ec12
d3dbeb0
0bc22c5
117ce59
6dee3d1
f57312b
4b8dc68
b24d31d
91a6b8f
b6a4609
05ab3dd
94ee810
337782c
8759318
f44c7ae
978313d
95ea295
23d4a7a
83cef1d
14ed93d
9dd3998
8ece825
d3be2ff
8edcb7b
884d9ae
5a6cb3c
aac6885
8f9d8ec
2132369
57efbd0
fab0b9e
c1f57ef
e2dfc5a
731a83c
a7bb3a3
1add668
ac79f4d
6dac46b
9ff5bf3
231bbea
1a12f35
bf8b443
f113504
bff2348
442be53
96055a7
b7c4f20
ba5afb9
3ba6dda
e160c45
ac4c1a0
d80847e
62e17de
15858f7
cd6015f
a90924e
67f7e14
19b24c6
551083b
d4b7d23
df72ef2
0aaf37e
15c2598
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,8 +55,12 @@ type QuoteData struct { | |
DestChainID int `json:"dest_chain_id"` | ||
OriginTokenAddr string `json:"origin_token_addr"` | ||
DestTokenAddr string `json:"dest_token_addr"` | ||
OriginAmountExact string `json:"origin_amount_exact"` | ||
ExpirationWindow int64 `json:"expiration_window"` | ||
ZapData string `json:"zap_data"` | ||
ZapNative string `json:"zap_native"` | ||
OriginAmountExact string `json:"origin_amount_exact"` | ||
OriginSender string `json:"origin_sender"` | ||
DestRecipient string `json:"dest_recipient"` | ||
Comment on lines
+61
to
+65
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification Input validation needed for new FastBridgeV2 fields Based on the codebase analysis, there is only basic validation for ZapNative (checking if it's a valid big.Int and non-zero), but no validation for:
Suggested validation points:
🔗 Analysis chainVerify input validation for new fields The new fields support FastBridgeV2's arbitrary call functionality, but there's no apparent validation for these fields. Consider adding validation to ensure:
🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Check if validation exists for the new fields
rg -A 5 "func.*validate.*Quote"
ast-grep --pattern 'if $COND {
$$$
$ERR = $$$
$$$
}'
Length of output: 66715 Script: #!/bin/bash
# Search for validation functions in the codebase
rg -A 10 "func.*validate.*Quote|func.*validate.*Request" services/rfq/api/
# Search for validation patterns in request.go
rg -A 5 "if.*invalid|if.*err.*=.*validate" services/rfq/api/model/request.go
# Look for validation in handler code
rg -A 10 "func.*Handle.*Quote.*Request" services/rfq/api/rest/
Length of output: 2919 Script: #!/bin/bash
# Search for validation in the request handling flow
rg -A 10 "func.*PutQuote|func.*PostQuote" services/rfq/api/rest/
# Look for validation in the model package
rg -A 10 "func.*Validate|func.*Check" services/rfq/api/model/
# Search for any validation related to zap or bridge data
rg -A 5 "zap|bridge.*valid" services/rfq/api/
Length of output: 2876 |
||
DestAmount *string `json:"dest_amount"` | ||
RelayerAddress *string `json:"relayer_address"` | ||
QuoteID *string `json:"quote_id"` | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,10 +41,12 @@ type PutRelayAckResponse struct { | |
RelayerAddress string `json:"relayer_address"` | ||
} | ||
|
||
// GetContractsResponse contains the schema for a GET /contract response. | ||
// GetContractsResponse contains the schema for a GET /contracts response. | ||
type GetContractsResponse struct { | ||
// Contracts is a map of chain id to contract address | ||
Contracts map[uint32]string `json:"contracts"` | ||
// ContractsV1 is a map of chain id to contract address for v1 fast bridge contracts | ||
ContractsV1 map[uint32]string `json:"contracts_v1"` | ||
// ContractsV2 is a map of chain id to contract address for v2 fast bridge contracts | ||
ContractsV2 map[uint32]string `json:"contracts_v2"` | ||
Comment on lines
+44
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification Breaking change confirmed: Multiple impacted components require updates The breaking change impacts several components that need to be updated:
🔗 Analysis chainBreaking Change: GetContractsResponse structure has been modified The separation of contracts into V1 and V2 maps is a breaking change that will require updates from API consumers. Ensure this change is properly documented in the API changelog and communicated to consumers. Let's verify if there are any direct API consumers that need to be updated: 🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Search for direct usage of the GetContractsResponse struct
rg -l "GetContractsResponse" | grep -v "response.go"
Length of output: 281 Script: #!/bin/bash
# Let's check the usage of GetContractsResponse in these files to understand the impact
echo "=== Checking client.go ==="
rg -A 5 "GetContractsResponse" services/rfq/api/client/client.go
echo -e "\n=== Checking handler.go ==="
rg -A 5 "GetContractsResponse" services/rfq/api/rest/handler.go
echo -e "\n=== Checking client.gen.go ==="
rg -A 5 "GetContractsResponse" contrib/restclient/client.gen.go
Length of output: 3607 |
||
} | ||
|
||
// ActiveRFQMessage represents the general structure of WebSocket messages for Active RFQ. | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -6,6 +6,7 @@ import ( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"strconv" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"time" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"github.com/ethereum/go-ethereum/common" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"github.com/synapsecns/sanguine/services/rfq/api/config" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"github.com/gin-gonic/gin" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -69,7 +70,7 @@ func (h *Handler) ModifyQuote(c *gin.Context) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
dbQuote, err := parseDBQuote(*putRequest, relayerAddr) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
dbQuote, err := parseDBQuote(h.cfg, *putRequest, relayerAddr) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -117,7 +118,7 @@ func (h *Handler) ModifyBulkQuotes(c *gin.Context) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
dbQuotes := []*db.Quote{} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
for _, quoteReq := range putRequest.Quotes { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
dbQuote, err := parseDBQuote(quoteReq, relayerAddr) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
dbQuote, err := parseDBQuote(h.cfg, quoteReq, relayerAddr) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid quote request"}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -134,7 +135,7 @@ func (h *Handler) ModifyBulkQuotes(c *gin.Context) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
//nolint:gosec | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func parseDBQuote(putRequest model.PutRelayerQuoteRequest, relayerAddr interface{}) (*db.Quote, error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func parseDBQuote(cfg config.Config, putRequest model.PutRelayerQuoteRequest, relayerAddr interface{}) (*db.Quote, error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
destAmount, err := decimal.NewFromString(putRequest.DestAmount) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return nil, fmt.Errorf("invalid DestAmount") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -147,6 +148,12 @@ func parseDBQuote(putRequest model.PutRelayerQuoteRequest, relayerAddr interface | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return nil, fmt.Errorf("invalid FixedFee") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
err = validateFastBridgeAddresses(cfg, putRequest) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return nil, fmt.Errorf("invalid fast bridge addresses: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// nolint: forcetypeassert | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return &db.Quote{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
OriginChainID: uint64(putRequest.OriginChainID), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -163,6 +170,24 @@ func parseDBQuote(putRequest model.PutRelayerQuoteRequest, relayerAddr interface | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
//nolint:gosec | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func validateFastBridgeAddresses(cfg config.Config, putRequest model.PutRelayerQuoteRequest) error { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Check V1 contracts | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
isV1Origin := common.HexToAddress(cfg.FastBridgeContractsV1[uint32(putRequest.OriginChainID)]) == common.HexToAddress(putRequest.OriginFastBridgeAddress) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
isV1Dest := common.HexToAddress(cfg.FastBridgeContractsV1[uint32(putRequest.DestChainID)]) == common.HexToAddress(putRequest.DestFastBridgeAddress) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Check V2 contracts | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
isV2Origin := common.HexToAddress(cfg.FastBridgeContractsV2[uint32(putRequest.OriginChainID)]) == common.HexToAddress(putRequest.OriginFastBridgeAddress) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
isV2Dest := common.HexToAddress(cfg.FastBridgeContractsV2[uint32(putRequest.DestChainID)]) == common.HexToAddress(putRequest.DestFastBridgeAddress) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Valid if both addresses match either V1 or V2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (isV1Origin && isV1Dest) || (isV2Origin && isV2Dest) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return fmt.Errorf("origin and destination fast bridge addresses must match either V1 or V2") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+173
to
+189
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add zero address validation and improve error messages While the contract version validation is good, consider these improvements:
Here's the suggested implementation: func validateFastBridgeAddresses(cfg config.Config, putRequest model.PutRelayerQuoteRequest) error {
+ // Validate non-zero addresses
+ if common.HexToAddress(putRequest.OriginFastBridgeAddress) == common.Address{} ||
+ common.HexToAddress(putRequest.DestFastBridgeAddress) == common.Address{} {
+ return fmt.Errorf("fast bridge addresses cannot be zero")
+ }
+
// Check V1 contracts
isV1Origin := common.HexToAddress(cfg.FastBridgeContractsV1[uint32(putRequest.OriginChainID)]) == common.HexToAddress(putRequest.OriginFastBridgeAddress)
isV1Dest := common.HexToAddress(cfg.FastBridgeContractsV1[uint32(putRequest.DestChainID)]) == common.HexToAddress(putRequest.DestFastBridgeAddress)
// Check V2 contracts
isV2Origin := common.HexToAddress(cfg.FastBridgeContractsV2[uint32(putRequest.OriginChainID)]) == common.HexToAddress(putRequest.OriginFastBridgeAddress)
isV2Dest := common.HexToAddress(cfg.FastBridgeContractsV2[uint32(putRequest.DestChainID)]) == common.HexToAddress(putRequest.DestFastBridgeAddress)
// Valid if both addresses match either V1 or V2
if (isV1Origin && isV1Dest) || (isV2Origin && isV2Dest) {
return nil
}
- return fmt.Errorf("origin and destination fast bridge addresses must match either V1 or V2")
+ return fmt.Errorf("mismatched fast bridge versions: origin=%s, destination=%s",
+ putRequest.OriginFastBridgeAddress, putRequest.DestFastBridgeAddress)
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
//nolint:gosec | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func quoteResponseFromDBQuote(dbQuote *db.Quote) *model.GetQuoteResponse { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return &model.GetQuoteResponse{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -301,12 +326,10 @@ func dbActiveQuoteRequestToModel(dbQuote *db.ActiveQuoteRequest) *model.GetOpenQ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// @Header 200 {string} X-Api-Version "API Version Number - See docs for more info" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// @Router /contracts [get]. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func (h *Handler) GetContracts(c *gin.Context) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Convert quotes from db model to api model | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
contracts := make(map[uint32]string) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
for chainID, address := range h.cfg.Bridges { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
contracts[chainID] = address | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
c.JSON(http.StatusOK, model.GetContractsResponse{Contracts: contracts}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
c.JSON(http.StatusOK, model.GetContractsResponse{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ContractsV1: h.cfg.FastBridgeContractsV1, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ContractsV2: h.cfg.FastBridgeContractsV2, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func filterQuoteAge(cfg config.Config, dbQuotes []*db.Quote) []*db.Quote { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO comment needs to be addressed before merging
The TODO comment indicates that v2 contract handling is missing, which is a critical component given that this PR is titled "support FastBridgeV2". This functionality should be implemented before merging.
Would you like me to help implement the v2 contract handling logic?