Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat(rfq-relayer): fee pricer considers v2 CallValue and CallParams [SLT-320] #3299

Merged
merged 36 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
1c7138e
WIP: incorporate call params into fee
dwasse Oct 15, 2024
76c1d6f
WIP: decompose getFee()
dwasse Oct 15, 2024
8aeb626
Feat: account for CallValue in getDestinationFee()
dwasse Oct 15, 2024
a42cef9
Fix: build
dwasse Oct 15, 2024
43da983
Feat: test call value and call param calculation in fee pricer
dwasse Oct 16, 2024
73531fd
Feat: add context on request body in rpc fwd errs
dwasse Oct 17, 2024
9baed17
Fix: zap estimate gas
dwasse Oct 18, 2024
b123b0f
Cleanup: move gas estimate into own func
dwasse Oct 18, 2024
4e448e8
Fix: quoter tests
dwasse Oct 18, 2024
62d16f7
Cleanup: lint
dwasse Oct 21, 2024
1376b7b
Cleanup: lint
dwasse Oct 21, 2024
2620aaf
Fix: tests
dwasse Oct 21, 2024
32df025
Cleanup: decompose func
dwasse Oct 21, 2024
b9cd947
Merge branch 'feat/relayer-arb-call' into feat/arb-call-fee-pricer
dwasse Oct 21, 2024
cfb8d3c
Cleanup: lint
dwasse Oct 21, 2024
cebce1e
Fix: tests
dwasse Oct 21, 2024
3186071
Cleanup: lint
dwasse Oct 21, 2024
af05543
Feat: always use quote fee multiplier
dwasse Oct 24, 2024
ae348be
WIP: abi encode pack relay()
dwasse Oct 24, 2024
842a0f6
Merge branch 'feat/relayer-arb-call' into feat/arb-call-fee-pricer
dwasse Oct 25, 2024
f8d6d4f
Feat: pass full RawRequest for gas estimation
dwasse Oct 25, 2024
9de9cfb
Cleanup: lint
dwasse Oct 25, 2024
a97da60
Fix: pricer tests
dwasse Oct 25, 2024
308e66b
Feat: ignore static l2 fee when incorporating call params
dwasse Oct 25, 2024
d081602
Fix: tests
dwasse Oct 25, 2024
7e6e1fc
Clarifying comment
dwasse Oct 25, 2024
596d105
Feat: add extra check for call param len
dwasse Oct 25, 2024
7a72ad4
Merge branch 'feat/relayer-arb-call' into feat/arb-call-fee-pricer
dwasse Nov 14, 2024
9fde9a8
Attempt to fix flake
dwasse Nov 14, 2024
a9b3a18
Cleanup: lint
dwasse Nov 14, 2024
2a132a8
Merge branch 'feat/relayer-arb-call' into feat/arb-call-fee-pricer
dwasse Nov 14, 2024
9833e0d
Fix: build
dwasse Nov 14, 2024
7d47ab0
Merge branch 'feat/relayer-arb-call' into feat/arb-call-fee-pricer
dwasse Nov 14, 2024
f93b3b3
feat(rfq-relayer): apply zap fee to dest amount for active quotes [SL…
dwasse Nov 14, 2024
2510a2b
fix(rfq-relayer): gas estimation for zaps (#3413)
dwasse Dec 3, 2024
d9db854
Merge branch 'feat/relayer-arb-call' into feat/arb-call-fee-pricer
parodime Dec 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions ethergo/backends/anvil/anvil.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"github.com/synapsecns/sanguine/core/dockerutil"
"github.com/synapsecns/sanguine/core/mapmutex"
"github.com/synapsecns/sanguine/core/processlog"
"github.com/synapsecns/sanguine/core/retry"
"github.com/synapsecns/sanguine/ethergo/backends"
"github.com/synapsecns/sanguine/ethergo/backends/base"
"github.com/synapsecns/sanguine/ethergo/chain"
Expand Down Expand Up @@ -386,9 +387,15 @@
var acct *keystore.Key
// TODO handle storing accounts to conform to get tx context
if address != nil {
acct = f.GetAccount(*address)
if acct == nil {
f.T().Errorf("could not get account %s", address.String())
err := retry.WithBackoff(ctx, func(_ context.Context) error {
acct = f.GetAccount(*address)
if acct == nil {
return fmt.Errorf("could not get account %s", address.String())
}
return nil

Check warning on line 395 in ethergo/backends/anvil/anvil.go

View check run for this annotation

Codecov / codecov/patch

ethergo/backends/anvil/anvil.go#L390-L395

Added lines #L390 - L395 were not covered by tests
}, retry.WithMaxTotalTime(10*time.Second))
if err != nil {
f.T().Errorf("could not get account %s: %v", address.String(), err)

Check warning on line 398 in ethergo/backends/anvil/anvil.go

View check run for this annotation

Codecov / codecov/patch

ethergo/backends/anvil/anvil.go#L397-L398

Added lines #L397 - L398 were not covered by tests
Comment on lines +390 to +398
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

Improve retry mechanism implementation

The retry mechanism needs several improvements:

  1. The context parameter is unused in the retry function
  2. The error handling could be more descriptive
  3. The changes lack test coverage

Apply this diff to improve the implementation:

-		err := retry.WithBackoff(ctx, func(_ context.Context) error {
+		err := retry.WithBackoff(ctx, func(retryCtx context.Context) error {
+			select {
+			case <-retryCtx.Done():
+				return retryCtx.Err()
+			default:
				acct = f.GetAccount(*address)
				if acct == nil {
-					return fmt.Errorf("could not get account %s", address.String())
+					return fmt.Errorf("account %s not found in backend store", address.String())
				}
				return nil
+			}
-		}, retry.WithMaxTotalTime(10*time.Second))
+		}, retry.WithMaxTotalTime(10*time.Second), retry.WithDescription("get account"))
		if err != nil {
-			f.T().Errorf("could not get account %s: %v", address.String(), err)
+			f.T().Errorf("failed to get account %s after retries: %v", address.String(), err)
			return res
		}

Additionally, add the following test to cover the retry mechanism:

func TestGetTxContextWithRetry(t *testing.T) {
    ctx := context.Background()
    backend := setupTestBackend(t)
    
    // Test retry mechanism
    address := common.HexToAddress("0x123")
    result := backend.GetTxContext(ctx, &address)
    
    // Verify result
    assert.Equal(t, backends.AuthType{}, result, "Should return empty AuthType for non-existent account")
}
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 390-395: ethergo/backends/anvil/anvil.go#L390-L395
Added lines #L390 - L395 were not covered by tests


[warning] 397-398: ethergo/backends/anvil/anvil.go#L397-L398
Added lines #L397 - L398 were not covered by tests

return res
}
} else {
Expand Down
2 changes: 1 addition & 1 deletion packages/contracts-rfq/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"lint": "forge fmt && npm run solhint",
"lint:check": "forge fmt --check && npm run solhint:check",
"ci:lint": "npm run lint:check",
"build:go": "./flatten.sh contracts/*.sol test/*.sol test/mocks/*.sol",
"build:go": "./flatten.sh contracts/*.sol test/*.sol test/mocks/*.sol test/harnesses/*.sol",
"solhint": "solhint '{contracts,script,test}/**/*.sol' --fix --noPrompt --max-warnings 3",
"solhint:check": "solhint '{contracts,script,test}/**/*.sol' --max-warnings 3"
}
Expand Down
7 changes: 4 additions & 3 deletions services/omnirpc/proxy/forward.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
"context"
"crypto/sha256"
"fmt"
goHTTP "net/http"
"strings"

"github.com/ImVexed/fasturl"
"github.com/goccy/go-json"
"github.com/jftuga/ellipsis"
Expand All @@ -14,8 +17,6 @@
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
"golang.org/x/exp/slices"
goHTTP "net/http"
"strings"
)

type rawResponse struct {
Expand Down Expand Up @@ -56,7 +57,7 @@

standardizedResponse, err = standardizeResponse(ctx, &f.rpcRequest[0], rpcMessage)
if err != nil {
return nil, fmt.Errorf("could not standardize response: %w", err)
return nil, fmt.Errorf("could not standardize response from body %s: %w", ellipsis.Shorten(string(body), 200), err)

Check warning on line 60 in services/omnirpc/proxy/forward.go

View check run for this annotation

Codecov / codecov/patch

services/omnirpc/proxy/forward.go#L60

Added line #L60 was not covered by tests
}
}

Expand Down
3 changes: 2 additions & 1 deletion services/rfq/api/model/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,12 @@ type QuoteData struct {
DestChainID int `json:"dest_chain_id"`
OriginTokenAddr string `json:"origin_token_addr"`
DestTokenAddr string `json:"dest_token_addr"`
OriginAmount string `json:"origin_amount"`
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"`
DestAmount *string `json:"dest_amount"`
RelayerAddress *string `json:"relayer_address"`
QuoteID *string `json:"quote_id"`
Expand Down
4,004 changes: 4,004 additions & 0 deletions services/rfq/contracts/bridgetransactionv2/bridgetransactionv2.abigen.go

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions services/rfq/contracts/bridgetransactionv2/generate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Package bridgetransactionv2 is the bridge transaction contract.
package bridgetransactionv2

//go:generate go run github.com/synapsecns/sanguine/tools/abigen generate --sol ../../../../packages/contracts-rfq/flattened/BridgeTransactionV2Harness.sol --pkg bridgetransactionv2 --sol-version 0.8.24 --filename bridgetransactionv2 --evm-version istanbul
35 changes: 35 additions & 0 deletions services/rfq/contracts/bridgetransactionv2/helper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package bridgetransactionv2

import (
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
)

// BridgeTransactionV2Ref is a bound fast bridge contract that returns the address of the contract.
//
//nolint:golint
type BridgeTransactionV2Ref struct {
*BridgeTransactionV2Harness
address common.Address
}

// Address gets the ocntract address.
func (f *BridgeTransactionV2Ref) Address() common.Address {
return f.address
}

// NewBridgeTransactionV2Ref creates a new fast bridge mock contract with a ref.
func NewBridgeTransactionV2Ref(address common.Address, backend bind.ContractBackend) (*BridgeTransactionV2Ref, error) {
bridgetransactionv2, err := NewBridgeTransactionV2Harness(address, backend)
if err != nil {
return nil, err
}

return &BridgeTransactionV2Ref{
BridgeTransactionV2Harness: bridgetransactionv2,
address: address,
}, nil
}

var _ vm.ContractRef = &BridgeTransactionV2Ref{}
110 changes: 110 additions & 0 deletions services/rfq/e2e/rfq_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,19 @@ import (
cctpTest "github.com/synapsecns/sanguine/services/cctp-relayer/testutil"
omnirpcClient "github.com/synapsecns/sanguine/services/omnirpc/client"
"github.com/synapsecns/sanguine/services/rfq/api/client"
"github.com/synapsecns/sanguine/services/rfq/contracts/bridgetransactionv2"
"github.com/synapsecns/sanguine/services/rfq/contracts/fastbridge"
"github.com/synapsecns/sanguine/services/rfq/contracts/fastbridgev2"
"github.com/synapsecns/sanguine/services/rfq/guard/guarddb"
guardService "github.com/synapsecns/sanguine/services/rfq/guard/service"
"github.com/synapsecns/sanguine/services/rfq/relayer/chain"
"github.com/synapsecns/sanguine/services/rfq/relayer/reldb"
"github.com/synapsecns/sanguine/services/rfq/relayer/service"
"github.com/synapsecns/sanguine/services/rfq/testutil"
"github.com/synapsecns/sanguine/services/rfq/util"
"golang.org/x/sync/errgroup"

"github.com/brianvoe/gofakeit/v6"
)

type IntegrationSuite struct {
Expand Down Expand Up @@ -403,6 +407,8 @@ func (i *IntegrationSuite) TestZap() {
_ = i.guard.Start(i.GetTestContext())
}()

fmt.Printf("omnirpc url: %s\n", i.destBackend.RPCAddress())
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

Remove unnecessary debug print statement

The line fmt.Printf("omnirpc url: %s\n", i.destBackend.RPCAddress()) appears to be a debug statement used during development. To keep the test output clean and avoid cluttering the logs, consider removing this line.

Apply this diff to remove the debug print:

-	fmt.Printf("omnirpc url: %s\n", i.destBackend.RPCAddress())
📝 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
fmt.Printf("omnirpc url: %s\n", i.destBackend.RPCAddress())


// load token contracts
const startAmount = 1000
const rfqAmount = 900
Expand Down Expand Up @@ -868,3 +874,107 @@ func (i *IntegrationSuite) TestConcurrentBridges() {
return true
})
}

//nolint:gosec
func (i *IntegrationSuite) TestEncodeBridgeTransactionParity() {
_, handle := i.manager.GetBridgeTransactionV2(i.GetTestContext(), i.originBackend)

mockAddress := func() common.Address {
// Generate 20 random bytes for the address
b := make([]byte, 20)
for i := range b {
b[i] = byte(gofakeit.Number(0, 255))
}
return common.BytesToAddress(b)
}

// Generate random values that will be used for both transactions
originChainId := uint32(gofakeit.Number(1, 1000000))
destChainId := uint32(gofakeit.Number(1, 1000000))
originSender := mockAddress()
destRecipient := mockAddress()
originToken := mockAddress()
destToken := mockAddress()
originAmount := new(big.Int).SetUint64(gofakeit.Uint64())
destAmount := new(big.Int).SetUint64(gofakeit.Uint64())
originFeeAmount := new(big.Int).SetUint64(gofakeit.Uint64())
deadline := new(big.Int).SetUint64(gofakeit.Uint64())
nonce := new(big.Int).SetUint64(gofakeit.Uint64())
exclusivityRelayer := mockAddress()
exclusivityEndTime := new(big.Int).SetUint64(gofakeit.Uint64())
zapNative := new(big.Int).SetUint64(gofakeit.Uint64())

// Random size and values for zapData
zapDataSize := gofakeit.Number(0, 1000)
zapData := make([]byte, zapDataSize)
for i := range zapDataSize {
zapData[i] = gofakeit.Uint8()
}
Comment on lines +910 to +912
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

Fix incorrect range loop syntax

Using range on an integer (zapDataSize) is incorrect. This will cause a compilation error.

Apply this diff to fix the loop:

-for i := range zapDataSize {
+for i := 0; i < zapDataSize; i++ {
     zapData[i] = gofakeit.Uint8()
}
📝 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 i := range zapDataSize {
zapData[i] = gofakeit.Uint8()
}
for i := 0; i < zapDataSize; i++ {
zapData[i] = gofakeit.Uint8()
}


// Create first transaction
bridgeTx := bridgetransactionv2.IFastBridgeV2BridgeTransactionV2{
OriginChainId: originChainId,
DestChainId: destChainId,
OriginSender: originSender,
DestRecipient: destRecipient,
OriginToken: originToken,
DestToken: destToken,
OriginAmount: originAmount,
DestAmount: destAmount,
OriginFeeAmount: originFeeAmount,
Deadline: deadline,
Nonce: nonce,
ExclusivityRelayer: exclusivityRelayer,
ExclusivityEndTime: exclusivityEndTime,
ZapNative: zapNative,
ZapData: zapData,
}

// Create second transaction with same values
tx := fastbridgev2.IFastBridgeV2BridgeTransactionV2{
OriginChainId: originChainId,
DestChainId: destChainId,
OriginSender: originSender,
DestRecipient: destRecipient,
OriginToken: originToken,
DestToken: destToken,
OriginAmount: originAmount,
DestAmount: destAmount,
OriginFeeAmount: originFeeAmount,
Deadline: deadline,
Nonce: nonce,
ExclusivityRelayer: exclusivityRelayer,
ExclusivityEndTime: exclusivityEndTime,
ZapNative: zapNative,
ZapData: zapData,
}

expectedEncoded, err := handle.EncodeV2(&bind.CallOpts{Context: i.GetTestContext()}, bridgeTx)
i.NoError(err)

encoded, err := chain.EncodeBridgeTx(tx)
i.NoError(err)

i.Equal(expectedEncoded, encoded)

// Test decoding
decodedTx, err := chain.DecodeBridgeTx(encoded)
i.NoError(err)

// Verify all fields match the original transaction
i.Equal(tx.OriginChainId, decodedTx.OriginChainId)
i.Equal(tx.DestChainId, decodedTx.DestChainId)
i.Equal(tx.OriginSender, decodedTx.OriginSender)
i.Equal(tx.DestRecipient, decodedTx.DestRecipient)
i.Equal(tx.OriginToken, decodedTx.OriginToken)
i.Equal(tx.DestToken, decodedTx.DestToken)
i.Equal(tx.OriginAmount.String(), decodedTx.OriginAmount.String())
i.Equal(tx.DestAmount.String(), decodedTx.DestAmount.String())
i.Equal(tx.OriginFeeAmount.String(), decodedTx.OriginFeeAmount.String())
i.Equal(tx.Deadline.String(), decodedTx.Deadline.String())
i.Equal(tx.Nonce.String(), decodedTx.Nonce.String())
i.Equal(tx.ExclusivityRelayer, decodedTx.ExclusivityRelayer)
i.Equal(tx.ExclusivityEndTime.String(), decodedTx.ExclusivityEndTime.String())
i.Equal(tx.ZapNative.String(), decodedTx.ZapNative.String())
i.Equal(tx.ZapData, decodedTx.ZapData)
}
113 changes: 113 additions & 0 deletions services/rfq/relayer/chain/encoding.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package chain

import (
"encoding/binary"
"fmt"
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/synapsecns/sanguine/services/rfq/contracts/fastbridgev2"
)

const (
// Field sizes in bytes.
sizeVersion = 2
sizeChainID = 4
sizeAddress = 20
sizeUint256 = 32

// Field offsets in bytes.
offsetVersion = 0
offsetOriginChainID = offsetVersion + sizeVersion
offsetDestChainID = offsetOriginChainID + sizeChainID
offsetOriginSender = offsetDestChainID + sizeChainID
offsetDestRecipient = offsetOriginSender + sizeAddress
offsetOriginToken = offsetDestRecipient + sizeAddress
offsetDestToken = offsetOriginToken + sizeAddress
offsetOriginAmount = offsetDestToken + sizeAddress
offsetDestAmount = offsetOriginAmount + sizeUint256
offsetOriginFeeAmount = offsetDestAmount + sizeUint256
offsetDeadline = offsetOriginFeeAmount + sizeUint256
offsetNonce = offsetDeadline + sizeUint256
offsetExclusivityRelayer = offsetNonce + sizeUint256
offsetExclusivityEndTime = offsetExclusivityRelayer + sizeAddress
offsetZapNative = offsetExclusivityEndTime + sizeUint256
offsetZapData = offsetZapNative + sizeUint256
)

// Helper function to properly encode uint256.
func padUint256(b *big.Int) []byte {
// Convert big.Int to bytes
bytes := b.Bytes()
// Create 32-byte array (initialized to zeros)
result := make([]byte, 32)
// Copy bytes to right side of array (left-pad with zeros)
copy(result[32-len(bytes):], bytes)
return result
}

// EncodeBridgeTx encodes a bridge transaction into a byte array.
func EncodeBridgeTx(tx fastbridgev2.IFastBridgeV2BridgeTransactionV2) ([]byte, error) {
// Initialize with total size including ZapData
result := make([]byte, offsetZapData+len(tx.ZapData))

// Version
result[offsetVersion] = 0
result[offsetVersion+1] = 2

// Chain IDs
binary.BigEndian.PutUint32(result[offsetOriginChainID:offsetOriginChainID+sizeChainID], tx.OriginChainId)
binary.BigEndian.PutUint32(result[offsetDestChainID:offsetDestChainID+sizeChainID], tx.DestChainId)

// Addresses
copy(result[offsetOriginSender:offsetOriginSender+sizeAddress], tx.OriginSender.Bytes())
copy(result[offsetDestRecipient:offsetDestRecipient+sizeAddress], tx.DestRecipient.Bytes())
copy(result[offsetOriginToken:offsetOriginToken+sizeAddress], tx.OriginToken.Bytes())
copy(result[offsetDestToken:offsetDestToken+sizeAddress], tx.DestToken.Bytes())

// uint256 values
copy(result[offsetOriginAmount:offsetOriginAmount+sizeUint256], padUint256(tx.OriginAmount))
copy(result[offsetDestAmount:offsetDestAmount+sizeUint256], padUint256(tx.DestAmount))
copy(result[offsetOriginFeeAmount:offsetOriginFeeAmount+sizeUint256], padUint256(tx.OriginFeeAmount))
copy(result[offsetDeadline:offsetDeadline+sizeUint256], padUint256(tx.Deadline))
copy(result[offsetNonce:offsetNonce+sizeUint256], padUint256(tx.Nonce))

// Exclusivity address
copy(result[offsetExclusivityRelayer:offsetExclusivityRelayer+sizeAddress], tx.ExclusivityRelayer.Bytes())

// More uint256 values
copy(result[offsetExclusivityEndTime:offsetExclusivityEndTime+sizeUint256], padUint256(tx.ExclusivityEndTime))
copy(result[offsetZapNative:offsetZapNative+sizeUint256], padUint256(tx.ZapNative))

// Replace append with copy for ZapData
copy(result[offsetZapData:], tx.ZapData)

return result, nil
}

// DecodeBridgeTx decodes a byte array into a bridge transaction.
func DecodeBridgeTx(data []byte) (fastbridgev2.IFastBridgeV2BridgeTransactionV2, error) {
if len(data) < offsetZapData {
return fastbridgev2.IFastBridgeV2BridgeTransactionV2{}, fmt.Errorf("data too short: got %d bytes, need at least %d", len(data), offsetZapData)
}

tx := fastbridgev2.IFastBridgeV2BridgeTransactionV2{
OriginChainId: binary.BigEndian.Uint32(data[offsetOriginChainID:offsetDestChainID]),
DestChainId: binary.BigEndian.Uint32(data[offsetDestChainID:offsetOriginSender]),
OriginSender: common.BytesToAddress(data[offsetOriginSender:offsetDestRecipient]),
DestRecipient: common.BytesToAddress(data[offsetDestRecipient:offsetOriginToken]),
OriginToken: common.BytesToAddress(data[offsetOriginToken:offsetDestToken]),
DestToken: common.BytesToAddress(data[offsetDestToken:offsetOriginAmount]),
OriginAmount: new(big.Int).SetBytes(data[offsetOriginAmount:offsetDestAmount]),
DestAmount: new(big.Int).SetBytes(data[offsetDestAmount:offsetOriginFeeAmount]),
OriginFeeAmount: new(big.Int).SetBytes(data[offsetOriginFeeAmount:offsetDeadline]),
Deadline: new(big.Int).SetBytes(data[offsetDeadline:offsetNonce]),
Nonce: new(big.Int).SetBytes(data[offsetNonce:offsetExclusivityRelayer]),
ExclusivityRelayer: common.BytesToAddress(data[offsetExclusivityRelayer:offsetExclusivityEndTime]),
ExclusivityEndTime: new(big.Int).SetBytes(data[offsetExclusivityEndTime:offsetZapNative]),
ZapNative: new(big.Int).SetBytes(data[offsetZapNative:offsetZapData]),
ZapData: data[offsetZapData:],
}
Comment on lines +90 to +110
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 comprehensive validation in DecodeBridgeTx to prevent panics

While DecodeBridgeTx checks if len(data) < offsetZapData, it does not verify that the subsequent slice operations are within the bounds of the data slice. If data is shorter than expected, this could lead to runtime panics due to out-of-bounds access. Consider adding bounds checks before each slice operation or ensuring that the length of data is sufficient for all fields being decoded.


return tx, nil
}
Comment on lines +94 to +113
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

Implement unit tests for encoding and decoding functions

To ensure the reliability of EncodeBridgeTx and DecodeBridgeTx, consider adding unit tests that verify the correctness of these functions. Testing with various transaction data, including edge cases, will help catch potential issues with offsets and data handling.

Would you like assistance in creating unit tests for these functions?

Loading
Loading