status-im/status-go

View on GitHub
services/wallet/router/router_v2_test.go

Summary

Maintainability
F
2 wks
Test Coverage
package router

import (
    "context"
    "database/sql"
    "math/big"
    "testing"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/common/hexutil"

    "github.com/status-im/status-go/appdatabase"
    "github.com/status-im/status-go/params"
    "github.com/status-im/status-go/rpc"
    walletCommon "github.com/status-im/status-go/services/wallet/common"
    "github.com/status-im/status-go/services/wallet/router/pathprocessor"
    "github.com/status-im/status-go/services/wallet/token"
    "github.com/status-im/status-go/t/helpers"

    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/require"
)

const (
    testBaseFee           = 50000000000
    testGasPrice          = 10000000000
    testPriorityFeeLow    = 1000000000
    testPriorityFeeMedium = 2000000000
    testPriorityFeeHigh   = 3000000000
    testBonderFeeETH      = 150000000000000
    testBonderFeeUSDC     = 10000

    testAmount0Point1ETHInWei = 100000000000000000
    testAmount0Point2ETHInWei = 200000000000000000
    testAmount0Point3ETHInWei = 300000000000000000
    testAmount0Point4ETHInWei = 400000000000000000
    testAmount0Point5ETHInWei = 500000000000000000
    testAmount0Point6ETHInWei = 600000000000000000
    testAmount0Point8ETHInWei = 800000000000000000
    testAmount1ETHInWei       = 1000000000000000000
    testAmount2ETHInWei       = 2000000000000000000
    testAmount3ETHInWei       = 3000000000000000000
    testAmount5ETHInWei       = 5000000000000000000

    testAmount1USDC   = 1000000
    testAmount100USDC = 100000000

    testApprovalGasEstimation = 1000
    testApprovalL1Fee         = 100000000000
)

var (
    testEstimationMap = map[string]uint64{
        pathprocessor.ProcessorTransferName:  uint64(1000),
        pathprocessor.ProcessorBridgeHopName: uint64(5000),
    }

    testBbonderFeeMap = map[string]*big.Int{
        pathprocessor.EthSymbol:  big.NewInt(testBonderFeeETH),
        pathprocessor.UsdcSymbol: big.NewInt(testBonderFeeUSDC),
    }

    testTokenPrices = map[string]float64{
        pathprocessor.EthSymbol:  2000,
        pathprocessor.UsdcSymbol: 1,
    }

    testSuggestedFees = &SuggestedFees{
        GasPrice:             big.NewInt(testGasPrice),
        BaseFee:              big.NewInt(testBaseFee),
        MaxPriorityFeePerGas: big.NewInt(testPriorityFeeLow),
        MaxFeesLevels: &MaxFeesLevels{
            Low:    (*hexutil.Big)(big.NewInt(testPriorityFeeLow)),
            Medium: (*hexutil.Big)(big.NewInt(testPriorityFeeMedium)),
            High:   (*hexutil.Big)(big.NewInt(testPriorityFeeHigh)),
        },
        EIP1559Enabled: false,
    }

    testBalanceMapPerChain = map[string]*big.Int{
        makeBalanceKey(walletCommon.EthereumMainnet, pathprocessor.EthSymbol):  big.NewInt(testAmount2ETHInWei),
        makeBalanceKey(walletCommon.EthereumMainnet, pathprocessor.UsdcSymbol): big.NewInt(testAmount100USDC),
        makeBalanceKey(walletCommon.OptimismMainnet, pathprocessor.EthSymbol):  big.NewInt(testAmount2ETHInWei),
        makeBalanceKey(walletCommon.OptimismMainnet, pathprocessor.UsdcSymbol): big.NewInt(testAmount100USDC),
        makeBalanceKey(walletCommon.ArbitrumMainnet, pathprocessor.EthSymbol):  big.NewInt(testAmount2ETHInWei),
        makeBalanceKey(walletCommon.ArbitrumMainnet, pathprocessor.UsdcSymbol): big.NewInt(testAmount100USDC),
    }
)

var mainnet = params.Network{
    ChainID:                walletCommon.EthereumMainnet,
    ChainName:              "Mainnet",
    RPCURL:                 "https://eth-archival.rpc.grove.city/v1/",
    FallbackURL:            "https://mainnet.infura.io/v3/",
    BlockExplorerURL:       "https://etherscan.io/",
    IconURL:                "network/Network=Ethereum",
    ChainColor:             "#627EEA",
    ShortName:              "eth",
    NativeCurrencyName:     "Ether",
    NativeCurrencySymbol:   "ETH",
    NativeCurrencyDecimals: 18,
    IsTest:                 false,
    Layer:                  1,
    Enabled:                true,
    RelatedChainID:         walletCommon.EthereumMainnet,
}

var sepolia = params.Network{
    ChainID:                walletCommon.EthereumSepolia,
    ChainName:              "Mainnet",
    RPCURL:                 "https://sepolia-archival.rpc.grove.city/v1/",
    FallbackURL:            "https://sepolia.infura.io/v3/",
    BlockExplorerURL:       "https://sepolia.etherscan.io/",
    IconURL:                "network/Network=Ethereum",
    ChainColor:             "#627EEA",
    ShortName:              "eth",
    NativeCurrencyName:     "Ether",
    NativeCurrencySymbol:   "ETH",
    NativeCurrencyDecimals: 18,
    IsTest:                 true,
    Layer:                  1,
    Enabled:                true,
    RelatedChainID:         walletCommon.EthereumMainnet,
}

var optimism = params.Network{
    ChainID:                walletCommon.OptimismMainnet,
    ChainName:              "Optimism",
    RPCURL:                 "https://optimism-mainnet.rpc.grove.city/v1/",
    FallbackURL:            "https://optimism-mainnet.infura.io/v3/",
    BlockExplorerURL:       "https://optimistic.etherscan.io",
    IconURL:                "network/Network=Optimism",
    ChainColor:             "#E90101",
    ShortName:              "oeth",
    NativeCurrencyName:     "Ether",
    NativeCurrencySymbol:   "ETH",
    NativeCurrencyDecimals: 18,
    IsTest:                 false,
    Layer:                  2,
    Enabled:                true,
    RelatedChainID:         walletCommon.OptimismMainnet,
}

var optimismSepolia = params.Network{
    ChainID:                walletCommon.OptimismSepolia,
    ChainName:              "Optimism",
    RPCURL:                 "https://optimism-sepolia-archival.rpc.grove.city/v1/",
    FallbackURL:            "https://optimism-sepolia.infura.io/v3/",
    BlockExplorerURL:       "https://sepolia-optimism.etherscan.io/",
    IconURL:                "network/Network=Optimism",
    ChainColor:             "#E90101",
    ShortName:              "oeth",
    NativeCurrencyName:     "Ether",
    NativeCurrencySymbol:   "ETH",
    NativeCurrencyDecimals: 18,
    IsTest:                 true,
    Layer:                  2,
    Enabled:                false,
    RelatedChainID:         walletCommon.OptimismMainnet,
}

var arbitrum = params.Network{
    ChainID:                walletCommon.ArbitrumMainnet,
    ChainName:              "Arbitrum",
    RPCURL:                 "https://arbitrum-one.rpc.grove.city/v1/",
    FallbackURL:            "https://arbitrum-mainnet.infura.io/v3/",
    BlockExplorerURL:       "https://arbiscan.io/",
    IconURL:                "network/Network=Arbitrum",
    ChainColor:             "#51D0F0",
    ShortName:              "arb1",
    NativeCurrencyName:     "Ether",
    NativeCurrencySymbol:   "ETH",
    NativeCurrencyDecimals: 18,
    IsTest:                 false,
    Layer:                  2,
    Enabled:                true,
    RelatedChainID:         walletCommon.ArbitrumMainnet,
}

var arbitrumSepolia = params.Network{
    ChainID:                walletCommon.ArbitrumSepolia,
    ChainName:              "Arbitrum",
    RPCURL:                 "https://arbitrum-sepolia-archival.rpc.grove.city/v1/",
    FallbackURL:            "https://arbitrum-sepolia.infura.io/v3/",
    BlockExplorerURL:       "https://sepolia-explorer.arbitrum.io/",
    IconURL:                "network/Network=Arbitrum",
    ChainColor:             "#51D0F0",
    ShortName:              "arb1",
    NativeCurrencyName:     "Ether",
    NativeCurrencySymbol:   "ETH",
    NativeCurrencyDecimals: 18,
    IsTest:                 true,
    Layer:                  2,
    Enabled:                false,
    RelatedChainID:         walletCommon.ArbitrumMainnet,
}

var defaultNetworks = []params.Network{
    mainnet,
    sepolia,
    optimism,
    optimismSepolia,
    arbitrum,
    arbitrumSepolia,
}

func amountOptionEqual(a, b amountOption) bool {
    return a.amount.Cmp(b.amount) == 0 && a.locked == b.locked
}

func contains(slice []amountOption, val amountOption) bool {
    for _, item := range slice {
        if amountOptionEqual(item, val) {
            return true
        }
    }
    return false
}

func amountOptionsMapsEqual(map1, map2 map[uint64][]amountOption) bool {
    if len(map1) != len(map2) {
        return false
    }

    for key, slice1 := range map1 {
        slice2, ok := map2[key]
        if !ok || len(slice1) != len(slice2) {
            return false
        }

        for _, val1 := range slice1 {
            if !contains(slice2, val1) {
                return false
            }
        }

        for _, val2 := range slice2 {
            if !contains(slice1, val2) {
                return false
            }
        }
    }

    return true
}

func setupTestNetworkDB(t *testing.T) (*sql.DB, func()) {
    db, cleanup, err := helpers.SetupTestSQLDB(appdatabase.DbInitializer{}, "wallet-router-tests")
    require.NoError(t, err)
    return db, func() { require.NoError(t, cleanup()) }
}

func setupRouter(t *testing.T) (*Router, func()) {
    db, cleanTmpDb := setupTestNetworkDB(t)

    client, _ := rpc.NewClient(nil, 1, params.UpstreamRPCConfig{Enabled: false, URL: ""}, defaultNetworks, db)

    router := NewRouter(client, nil, nil, nil, nil, nil, nil, nil)

    transfer := pathprocessor.NewTransferProcessor(nil, nil)
    router.AddPathProcessor(transfer)

    erc721Transfer := pathprocessor.NewERC721Processor(nil, nil)
    router.AddPathProcessor(erc721Transfer)

    erc1155Transfer := pathprocessor.NewERC1155Processor(nil, nil)
    router.AddPathProcessor(erc1155Transfer)

    hop := pathprocessor.NewHopBridgeProcessor(nil, nil, nil, nil)
    router.AddPathProcessor(hop)

    paraswap := pathprocessor.NewSwapParaswapProcessor(nil, nil, nil)
    router.AddPathProcessor(paraswap)

    ensRegister := pathprocessor.NewENSReleaseProcessor(nil, nil, nil)
    router.AddPathProcessor(ensRegister)

    ensRelease := pathprocessor.NewENSReleaseProcessor(nil, nil, nil)
    router.AddPathProcessor(ensRelease)

    ensPublicKey := pathprocessor.NewENSPublicKeyProcessor(nil, nil, nil)
    router.AddPathProcessor(ensPublicKey)

    buyStickers := pathprocessor.NewStickersBuyProcessor(nil, nil, nil)
    router.AddPathProcessor(buyStickers)

    return router, cleanTmpDb
}

func TestRouterV2(t *testing.T) {
    router, cleanTmpDb := setupRouter(t)
    defer cleanTmpDb()

    tests := []struct {
        name               string
        input              *RouteInputParams
        expectedCandidates []*PathV2
        expectedError      error
    }{
        {
            name: "ETH transfer - No Specific FromChain - No Specific ToChain",
            input: &RouteInputParams{
                testnetMode: false,
                SendType:    Transfer,
                AddrFrom:    common.HexToAddress("0x1"),
                AddrTo:      common.HexToAddress("0x2"),
                AmountIn:    (*hexutil.Big)(big.NewInt(testAmount1ETHInWei)),
                TokenID:     pathprocessor.EthSymbol,

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.EthSymbol,
                        Decimals: 18,
                    },
                    tokenPrices:           testTokenPrices,
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &mainnet,
                    ToChain:          &mainnet,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &optimism,
                    ToChain:          &optimism,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &arbitrum,
                    ToChain:          &arbitrum,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &mainnet,
                    ToChain:          &optimism,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &mainnet,
                    ToChain:          &arbitrum,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &optimism,
                    ToChain:          &mainnet,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &optimism,
                    ToChain:          &arbitrum,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &mainnet,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &optimism,
                    ApprovalRequired: false,
                },
            },
        },
        {
            name: "ETH transfer - No Specific FromChain - Specific Single ToChain",
            input: &RouteInputParams{
                testnetMode:        false,
                SendType:           Transfer,
                AddrFrom:           common.HexToAddress("0x1"),
                AddrTo:             common.HexToAddress("0x2"),
                AmountIn:           (*hexutil.Big)(big.NewInt(testAmount1ETHInWei)),
                TokenID:            pathprocessor.EthSymbol,
                DisabledToChainIDs: []uint64{walletCommon.OptimismMainnet, walletCommon.ArbitrumMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.EthSymbol,
                        Decimals: 18,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &mainnet,
                    ToChain:          &mainnet,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &optimism,
                    ToChain:          &mainnet,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &mainnet,
                    ApprovalRequired: false,
                },
            },
        },
        {
            name: "ETH transfer - No Specific FromChain - Specific Multiple ToChain",
            input: &RouteInputParams{
                testnetMode:        false,
                SendType:           Transfer,
                AddrFrom:           common.HexToAddress("0x1"),
                AddrTo:             common.HexToAddress("0x2"),
                AmountIn:           (*hexutil.Big)(big.NewInt(testAmount1ETHInWei)),
                TokenID:            pathprocessor.EthSymbol,
                DisabledToChainIDs: []uint64{walletCommon.EthereumMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.EthSymbol,
                        Decimals: 18,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &optimism,
                    ToChain:          &optimism,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &arbitrum,
                    ToChain:          &arbitrum,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &mainnet,
                    ToChain:          &optimism,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &mainnet,
                    ToChain:          &arbitrum,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &optimism,
                    ToChain:          &arbitrum,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &optimism,
                    ApprovalRequired: false,
                },
            },
        },
        {
            name: "ETH transfer - Specific Single FromChain - No Specific ToChain",
            input: &RouteInputParams{
                testnetMode:          false,
                SendType:             Transfer,
                AddrFrom:             common.HexToAddress("0x1"),
                AddrTo:               common.HexToAddress("0x2"),
                AmountIn:             (*hexutil.Big)(big.NewInt(testAmount1ETHInWei)),
                TokenID:              pathprocessor.EthSymbol,
                DisabledFromChainIDs: []uint64{walletCommon.EthereumMainnet, walletCommon.OptimismMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.EthSymbol,
                        Decimals: 18,
                    },
                    tokenPrices:   testTokenPrices,
                    baseFee:       big.NewInt(testBaseFee),
                    suggestedFees: testSuggestedFees,
                    balanceMap:    testBalanceMapPerChain,

                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &arbitrum,
                    ToChain:          &arbitrum,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &mainnet,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &optimism,
                    ApprovalRequired: false,
                },
            },
        },
        {
            name: "ETH transfer - Specific Multiple FromChain - No Specific ToChain",
            input: &RouteInputParams{
                testnetMode:          false,
                SendType:             Transfer,
                AddrFrom:             common.HexToAddress("0x1"),
                AddrTo:               common.HexToAddress("0x2"),
                AmountIn:             (*hexutil.Big)(big.NewInt(testAmount1ETHInWei)),
                TokenID:              pathprocessor.EthSymbol,
                DisabledFromChainIDs: []uint64{walletCommon.EthereumMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.EthSymbol,
                        Decimals: 18,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &optimism,
                    ToChain:          &optimism,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &arbitrum,
                    ToChain:          &arbitrum,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &optimism,
                    ToChain:          &mainnet,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &optimism,
                    ToChain:          &arbitrum,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &mainnet,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &optimism,
                    ApprovalRequired: false,
                },
            },
        },
        {
            name: "ETH transfer - Specific Single FromChain - Specific Single ToChain - Same Chains",
            input: &RouteInputParams{
                testnetMode:          false,
                SendType:             Transfer,
                AddrFrom:             common.HexToAddress("0x1"),
                AddrTo:               common.HexToAddress("0x2"),
                AmountIn:             (*hexutil.Big)(big.NewInt(testAmount1ETHInWei)),
                TokenID:              pathprocessor.EthSymbol,
                DisabledFromChainIDs: []uint64{walletCommon.EthereumMainnet, walletCommon.OptimismMainnet},
                DisabledToChainIDs:   []uint64{walletCommon.EthereumMainnet, walletCommon.OptimismMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.EthSymbol,
                        Decimals: 18,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &arbitrum,
                    ToChain:          &arbitrum,
                    ApprovalRequired: false,
                },
            },
        },
        {
            name: "ETH transfer - Specific Single FromChain - Specific Single ToChain - Different Chains",
            input: &RouteInputParams{
                testnetMode:          false,
                SendType:             Transfer,
                AddrFrom:             common.HexToAddress("0x1"),
                AddrTo:               common.HexToAddress("0x2"),
                AmountIn:             (*hexutil.Big)(big.NewInt(testAmount1ETHInWei)),
                TokenID:              pathprocessor.EthSymbol,
                DisabledFromChainIDs: []uint64{walletCommon.EthereumMainnet, walletCommon.OptimismMainnet},
                DisabledToChainIDs:   []uint64{walletCommon.OptimismMainnet, walletCommon.ArbitrumMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.EthSymbol,
                        Decimals: 18,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &mainnet,
                    ApprovalRequired: false,
                },
            },
        },
        {
            name: "ETH transfer - Specific Multiple FromChain - Specific Multiple ToChain - Single Common Chain",
            input: &RouteInputParams{
                testnetMode:          false,
                SendType:             Transfer,
                AddrFrom:             common.HexToAddress("0x1"),
                AddrTo:               common.HexToAddress("0x2"),
                AmountIn:             (*hexutil.Big)(big.NewInt(testAmount1ETHInWei)),
                TokenID:              pathprocessor.EthSymbol,
                DisabledFromChainIDs: []uint64{walletCommon.EthereumMainnet, walletCommon.OptimismMainnet},
                DisabledToChainIDs:   []uint64{walletCommon.EthereumMainnet, walletCommon.OptimismMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.EthSymbol,
                        Decimals: 18,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &arbitrum,
                    ToChain:          &arbitrum,
                    ApprovalRequired: false,
                },
            },
        },
        {
            name: "ETH transfer - Specific Multiple FromChain - Specific Multiple ToChain - Multiple Common Chains",
            input: &RouteInputParams{
                testnetMode:          false,
                SendType:             Transfer,
                AddrFrom:             common.HexToAddress("0x1"),
                AddrTo:               common.HexToAddress("0x2"),
                AmountIn:             (*hexutil.Big)(big.NewInt(testAmount1ETHInWei)),
                TokenID:              pathprocessor.EthSymbol,
                DisabledFromChainIDs: []uint64{walletCommon.OptimismMainnet},
                DisabledToChainIDs:   []uint64{walletCommon.OptimismMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.EthSymbol,
                        Decimals: 18,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &mainnet,
                    ToChain:          &mainnet,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &arbitrum,
                    ToChain:          &arbitrum,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &mainnet,
                    ToChain:          &arbitrum,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &mainnet,
                    ApprovalRequired: false,
                },
            },
        },
        {
            name: "ETH transfer - Specific Multiple FromChain - Specific Multiple ToChain - No Common Chains",
            input: &RouteInputParams{
                testnetMode:          false,
                SendType:             Transfer,
                AddrFrom:             common.HexToAddress("0x1"),
                AddrTo:               common.HexToAddress("0x2"),
                AmountIn:             (*hexutil.Big)(big.NewInt(testAmount1ETHInWei)),
                TokenID:              pathprocessor.EthSymbol,
                DisabledFromChainIDs: []uint64{walletCommon.EthereumMainnet, walletCommon.OptimismMainnet},
                DisabledToChainIDs:   []uint64{walletCommon.OptimismMainnet, walletCommon.ArbitrumMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.EthSymbol,
                        Decimals: 18,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &mainnet,
                    ApprovalRequired: false,
                },
            },
        },
        {
            name: "ETH transfer - All FromChains Disabled - All ToChains Disabled",
            input: &RouteInputParams{
                testnetMode:          false,
                SendType:             Transfer,
                AddrFrom:             common.HexToAddress("0x1"),
                AddrTo:               common.HexToAddress("0x2"),
                AmountIn:             (*hexutil.Big)(big.NewInt(testAmount1ETHInWei)),
                TokenID:              pathprocessor.EthSymbol,
                DisabledFromChainIDs: []uint64{walletCommon.EthereumMainnet, walletCommon.OptimismMainnet, walletCommon.ArbitrumMainnet},
                DisabledToChainIDs:   []uint64{walletCommon.EthereumMainnet, walletCommon.OptimismMainnet, walletCommon.ArbitrumMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.EthSymbol,
                        Decimals: 18,
                    },
                    tokenPrices:   testTokenPrices,
                    baseFee:       big.NewInt(testBaseFee),
                    suggestedFees: testSuggestedFees,
                    balanceMap:    testBalanceMapPerChain,

                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{},
        },
        {
            name: "ETH transfer - No Specific FromChain - No Specific ToChain - Single Chain LockedAmount",
            input: &RouteInputParams{
                testnetMode: false,
                SendType:    Transfer,
                AddrFrom:    common.HexToAddress("0x1"),
                AddrTo:      common.HexToAddress("0x2"),
                AmountIn:    (*hexutil.Big)(big.NewInt(testAmount1ETHInWei)),
                TokenID:     pathprocessor.EthSymbol,
                FromLockedAmount: map[uint64]*hexutil.Big{
                    walletCommon.EthereumMainnet: (*hexutil.Big)(big.NewInt(testAmount0Point2ETHInWei)),
                },

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.EthSymbol,
                        Decimals: 18,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &mainnet,
                    ToChain:          &mainnet,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount0Point2ETHInWei)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &optimism,
                    ToChain:          &optimism,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount0Point8ETHInWei)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &arbitrum,
                    ToChain:          &arbitrum,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount0Point8ETHInWei)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &mainnet,
                    ToChain:          &optimism,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount0Point2ETHInWei - testBonderFeeETH)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &mainnet,
                    ToChain:          &arbitrum,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount0Point2ETHInWei - testBonderFeeETH)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &optimism,
                    ToChain:          &mainnet,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount0Point8ETHInWei - testBonderFeeETH)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &optimism,
                    ToChain:          &arbitrum,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount0Point8ETHInWei - testBonderFeeETH)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &mainnet,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount0Point8ETHInWei - testBonderFeeETH)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &optimism,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount0Point8ETHInWei - testBonderFeeETH)),
                    ApprovalRequired: false,
                },
            },
        },
        {
            name: "ETH transfer - No Specific FromChain - Specific ToChain - Single Chain LockedAmount",
            input: &RouteInputParams{
                testnetMode:        false,
                SendType:           Transfer,
                AddrFrom:           common.HexToAddress("0x1"),
                AddrTo:             common.HexToAddress("0x2"),
                AmountIn:           (*hexutil.Big)(big.NewInt(testAmount1ETHInWei)),
                TokenID:            pathprocessor.EthSymbol,
                DisabledToChainIDs: []uint64{walletCommon.EthereumMainnet, walletCommon.ArbitrumMainnet},
                FromLockedAmount: map[uint64]*hexutil.Big{
                    walletCommon.EthereumMainnet: (*hexutil.Big)(big.NewInt(testAmount0Point2ETHInWei)),
                    walletCommon.ArbitrumMainnet: (*hexutil.Big)(big.NewInt(testAmount0Point3ETHInWei)),
                },

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.EthSymbol,
                        Decimals: 18,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &optimism,
                    ToChain:          &optimism,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount1ETHInWei - testAmount0Point2ETHInWei - testAmount0Point3ETHInWei)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &mainnet,
                    ToChain:          &optimism,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount0Point2ETHInWei - testBonderFeeETH)), //(*hexutil.Big)(big.NewInt(testAmount0Point2ETHInWei)), //(big.NewInt(testAmount0Point2ETHInWei - testBonderFeeETH)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &optimism,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount0Point3ETHInWei - testBonderFeeETH)), //(*hexutil.Big)(big.NewInt(testAmount0Point3ETHInWei)),
                    ApprovalRequired: false,
                },
            },
        },
        {
            name: "ETH transfer - No Specific FromChain - No Specific ToChain - Multiple Chains LockedAmount",
            input: &RouteInputParams{
                testnetMode: false,
                SendType:    Transfer,
                AddrFrom:    common.HexToAddress("0x1"),
                AddrTo:      common.HexToAddress("0x2"),
                AmountIn:    (*hexutil.Big)(big.NewInt(testAmount1ETHInWei)),
                TokenID:     pathprocessor.EthSymbol,
                FromLockedAmount: map[uint64]*hexutil.Big{
                    walletCommon.EthereumMainnet: (*hexutil.Big)(big.NewInt(testAmount0Point2ETHInWei)),
                    walletCommon.OptimismMainnet: (*hexutil.Big)(big.NewInt(testAmount0Point3ETHInWei)),
                },

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.EthSymbol,
                        Decimals: 18,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &mainnet,
                    ToChain:          &mainnet,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount0Point2ETHInWei)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &optimism,
                    ToChain:          &optimism,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount0Point3ETHInWei)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &arbitrum,
                    ToChain:          &arbitrum,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount0Point5ETHInWei)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &mainnet,
                    ToChain:          &optimism,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount0Point2ETHInWei - testBonderFeeETH)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &mainnet,
                    ToChain:          &arbitrum,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount0Point2ETHInWei - testBonderFeeETH)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &optimism,
                    ToChain:          &mainnet,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount0Point3ETHInWei - testBonderFeeETH)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &optimism,
                    ToChain:          &arbitrum,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount0Point3ETHInWei - testBonderFeeETH)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &mainnet,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount0Point5ETHInWei - testBonderFeeETH)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &optimism,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount0Point5ETHInWei - testBonderFeeETH)),
                    ApprovalRequired: false,
                },
            },
        },
        {
            name: "ETH transfer - No Specific FromChain - No Specific ToChain - All Chains LockedAmount",
            input: &RouteInputParams{
                testnetMode: false,
                SendType:    Transfer,
                AddrFrom:    common.HexToAddress("0x1"),
                AddrTo:      common.HexToAddress("0x2"),
                AmountIn:    (*hexutil.Big)(big.NewInt(testAmount1ETHInWei)),
                TokenID:     pathprocessor.EthSymbol,
                FromLockedAmount: map[uint64]*hexutil.Big{
                    walletCommon.EthereumMainnet: (*hexutil.Big)(big.NewInt(testAmount0Point2ETHInWei)),
                    walletCommon.OptimismMainnet: (*hexutil.Big)(big.NewInt(testAmount0Point3ETHInWei)),
                    walletCommon.ArbitrumMainnet: (*hexutil.Big)(big.NewInt(testAmount0Point5ETHInWei)),
                },

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.EthSymbol,
                        Decimals: 18,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &mainnet,
                    ToChain:          &mainnet,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount0Point2ETHInWei)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &optimism,
                    ToChain:          &optimism,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount0Point3ETHInWei)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &arbitrum,
                    ToChain:          &arbitrum,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount0Point5ETHInWei)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &mainnet,
                    ToChain:          &optimism,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount0Point2ETHInWei - testBonderFeeETH)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &mainnet,
                    ToChain:          &arbitrum,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount0Point2ETHInWei - testBonderFeeETH)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &optimism,
                    ToChain:          &mainnet,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount0Point3ETHInWei - testBonderFeeETH)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &optimism,
                    ToChain:          &arbitrum,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount0Point3ETHInWei - testBonderFeeETH)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &mainnet,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount0Point5ETHInWei - testBonderFeeETH)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &optimism,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount0Point5ETHInWei - testBonderFeeETH)),
                    ApprovalRequired: false,
                },
            },
        },
        {
            name: "ETH transfer - No Specific FromChain - No Specific ToChain - All Chains LockedAmount with insufficient amount",
            input: &RouteInputParams{
                testnetMode: false,
                SendType:    Transfer,
                AddrFrom:    common.HexToAddress("0x1"),
                AddrTo:      common.HexToAddress("0x2"),
                AmountIn:    (*hexutil.Big)(big.NewInt(testAmount1ETHInWei)),
                TokenID:     pathprocessor.EthSymbol,
                FromLockedAmount: map[uint64]*hexutil.Big{
                    walletCommon.EthereumMainnet: (*hexutil.Big)(big.NewInt(testAmount0Point2ETHInWei)),
                    walletCommon.OptimismMainnet: (*hexutil.Big)(big.NewInt(testAmount0Point3ETHInWei)),
                    walletCommon.ArbitrumMainnet: (*hexutil.Big)(big.NewInt(testAmount0Point4ETHInWei)),
                },

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.EthSymbol,
                        Decimals: 18,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedError:      ErrLockedAmountLessThanSendAmountAllNetworks,
            expectedCandidates: []*PathV2{},
        },
        {
            name: "ETH transfer - No Specific FromChain - No Specific ToChain - LockedAmount exceeds sending amount",
            input: &RouteInputParams{
                testnetMode: false,
                SendType:    Transfer,
                AddrFrom:    common.HexToAddress("0x1"),
                AddrTo:      common.HexToAddress("0x2"),
                AmountIn:    (*hexutil.Big)(big.NewInt(testAmount1ETHInWei)),
                TokenID:     pathprocessor.EthSymbol,
                FromLockedAmount: map[uint64]*hexutil.Big{
                    walletCommon.OptimismMainnet: (*hexutil.Big)(big.NewInt(testAmount0Point3ETHInWei)),
                    walletCommon.ArbitrumMainnet: (*hexutil.Big)(big.NewInt(testAmount0Point8ETHInWei)),
                },

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.EthSymbol,
                        Decimals: 18,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedError:      ErrLockedAmountExceedsTotalSendAmount,
            expectedCandidates: []*PathV2{},
        },
        {
            name: "ERC20 transfer - No Specific FromChain - No Specific ToChain",
            input: &RouteInputParams{
                testnetMode: false,
                SendType:    Transfer,
                AddrFrom:    common.HexToAddress("0x1"),
                AddrTo:      common.HexToAddress("0x2"),
                AmountIn:    (*hexutil.Big)(big.NewInt(testAmount1USDC)),
                TokenID:     pathprocessor.UsdcSymbol,

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.UsdcSymbol,
                        Decimals: 6,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &mainnet,
                    ToChain:          &mainnet,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &optimism,
                    ToChain:          &optimism,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &arbitrum,
                    ToChain:          &arbitrum,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &mainnet,
                    ToChain:          &optimism,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &mainnet,
                    ToChain:          &arbitrum,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &optimism,
                    ToChain:          &mainnet,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &optimism,
                    ToChain:          &arbitrum,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &mainnet,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &optimism,
                    ApprovalRequired: true,
                },
            },
        },
        {
            name: "ERC20 transfer - No Specific FromChain - Specific Single ToChain",
            input: &RouteInputParams{
                testnetMode:        false,
                SendType:           Transfer,
                AddrFrom:           common.HexToAddress("0x1"),
                AddrTo:             common.HexToAddress("0x2"),
                AmountIn:           (*hexutil.Big)(big.NewInt(testAmount1USDC)),
                TokenID:            pathprocessor.UsdcSymbol,
                DisabledToChainIDs: []uint64{walletCommon.OptimismMainnet, walletCommon.ArbitrumMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.UsdcSymbol,
                        Decimals: 6,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &mainnet,
                    ToChain:          &mainnet,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &optimism,
                    ToChain:          &mainnet,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &mainnet,
                    ApprovalRequired: true,
                },
            },
        },
        {
            name: "ERC20 transfer - No Specific FromChain - Specific Multiple ToChain",
            input: &RouteInputParams{
                testnetMode:        false,
                SendType:           Transfer,
                AddrFrom:           common.HexToAddress("0x1"),
                AddrTo:             common.HexToAddress("0x2"),
                AmountIn:           (*hexutil.Big)(big.NewInt(testAmount1USDC)),
                TokenID:            pathprocessor.UsdcSymbol,
                DisabledToChainIDs: []uint64{walletCommon.EthereumMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.UsdcSymbol,
                        Decimals: 6,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &optimism,
                    ToChain:          &optimism,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &arbitrum,
                    ToChain:          &arbitrum,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &mainnet,
                    ToChain:          &optimism,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &mainnet,
                    ToChain:          &arbitrum,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &optimism,
                    ToChain:          &arbitrum,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &optimism,
                    ApprovalRequired: true,
                },
            },
        },
        {
            name: "ERC20 transfer - Specific Single FromChain - No Specific ToChain",
            input: &RouteInputParams{
                testnetMode:          false,
                SendType:             Transfer,
                AddrFrom:             common.HexToAddress("0x1"),
                AddrTo:               common.HexToAddress("0x2"),
                AmountIn:             (*hexutil.Big)(big.NewInt(testAmount1USDC)),
                TokenID:              pathprocessor.UsdcSymbol,
                DisabledFromChainIDs: []uint64{walletCommon.EthereumMainnet, walletCommon.OptimismMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.UsdcSymbol,
                        Decimals: 6,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &arbitrum,
                    ToChain:          &arbitrum,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &mainnet,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &optimism,
                    ApprovalRequired: true,
                },
            },
        },
        {
            name: "ERC20 transfer - Specific Multiple FromChain - No Specific ToChain",
            input: &RouteInputParams{
                testnetMode:          false,
                SendType:             Transfer,
                AddrFrom:             common.HexToAddress("0x1"),
                AddrTo:               common.HexToAddress("0x2"),
                AmountIn:             (*hexutil.Big)(big.NewInt(testAmount1USDC)),
                TokenID:              pathprocessor.UsdcSymbol,
                DisabledFromChainIDs: []uint64{walletCommon.EthereumMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.UsdcSymbol,
                        Decimals: 6,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &optimism,
                    ToChain:          &optimism,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &arbitrum,
                    ToChain:          &arbitrum,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &optimism,
                    ToChain:          &mainnet,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &optimism,
                    ToChain:          &arbitrum,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &mainnet,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &optimism,
                    ApprovalRequired: true,
                },
            },
        },
        {
            name: "ERC20 transfer - Specific Single FromChain - Specific Single ToChain - Same Chains",
            input: &RouteInputParams{
                testnetMode:          false,
                SendType:             Transfer,
                AddrFrom:             common.HexToAddress("0x1"),
                AddrTo:               common.HexToAddress("0x2"),
                AmountIn:             (*hexutil.Big)(big.NewInt(testAmount1USDC)),
                TokenID:              pathprocessor.UsdcSymbol,
                DisabledFromChainIDs: []uint64{walletCommon.EthereumMainnet, walletCommon.OptimismMainnet},
                DisabledToChainIDs:   []uint64{walletCommon.EthereumMainnet, walletCommon.OptimismMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.UsdcSymbol,
                        Decimals: 6,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &arbitrum,
                    ToChain:          &arbitrum,
                    ApprovalRequired: false,
                },
            },
        },
        {
            name: "ERC20 transfer - Specific Single FromChain - Specific Single ToChain - Different Chains",
            input: &RouteInputParams{
                testnetMode:          false,
                SendType:             Transfer,
                AddrFrom:             common.HexToAddress("0x1"),
                AddrTo:               common.HexToAddress("0x2"),
                AmountIn:             (*hexutil.Big)(big.NewInt(testAmount1USDC)),
                TokenID:              pathprocessor.UsdcSymbol,
                DisabledFromChainIDs: []uint64{walletCommon.EthereumMainnet, walletCommon.OptimismMainnet},
                DisabledToChainIDs:   []uint64{walletCommon.OptimismMainnet, walletCommon.ArbitrumMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.UsdcSymbol,
                        Decimals: 6,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &mainnet,
                    ApprovalRequired: true,
                },
            },
        },
        {
            name: "ERC20 transfer - Specific Multiple FromChain - Specific Multiple ToChain - Single Common Chain",
            input: &RouteInputParams{
                testnetMode:          false,
                SendType:             Transfer,
                AddrFrom:             common.HexToAddress("0x1"),
                AddrTo:               common.HexToAddress("0x2"),
                AmountIn:             (*hexutil.Big)(big.NewInt(testAmount1USDC)),
                TokenID:              pathprocessor.UsdcSymbol,
                DisabledFromChainIDs: []uint64{walletCommon.EthereumMainnet, walletCommon.OptimismMainnet},
                DisabledToChainIDs:   []uint64{walletCommon.EthereumMainnet, walletCommon.OptimismMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.UsdcSymbol,
                        Decimals: 6,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &arbitrum,
                    ToChain:          &arbitrum,
                    ApprovalRequired: false,
                },
            },
        },
        {
            name: "ERC20 transfer - Specific Multiple FromChain - Specific Multiple ToChain - Multiple Common Chains",
            input: &RouteInputParams{
                testnetMode:          false,
                SendType:             Transfer,
                AddrFrom:             common.HexToAddress("0x1"),
                AddrTo:               common.HexToAddress("0x2"),
                AmountIn:             (*hexutil.Big)(big.NewInt(testAmount1USDC)),
                TokenID:              pathprocessor.UsdcSymbol,
                DisabledFromChainIDs: []uint64{walletCommon.OptimismMainnet},
                DisabledToChainIDs:   []uint64{walletCommon.OptimismMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.UsdcSymbol,
                        Decimals: 6,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &mainnet,
                    ToChain:          &mainnet,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &arbitrum,
                    ToChain:          &arbitrum,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &mainnet,
                    ToChain:          &arbitrum,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &mainnet,
                    ApprovalRequired: true,
                },
            },
        },
        {
            name: "ERC20 transfer - Specific Multiple FromChain - Specific Multiple ToChain - No Common Chains",
            input: &RouteInputParams{
                testnetMode:          false,
                SendType:             Transfer,
                AddrFrom:             common.HexToAddress("0x1"),
                AddrTo:               common.HexToAddress("0x2"),
                AmountIn:             (*hexutil.Big)(big.NewInt(testAmount1USDC)),
                TokenID:              pathprocessor.UsdcSymbol,
                DisabledFromChainIDs: []uint64{walletCommon.EthereumMainnet, walletCommon.OptimismMainnet},
                DisabledToChainIDs:   []uint64{walletCommon.OptimismMainnet, walletCommon.ArbitrumMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.UsdcSymbol,
                        Decimals: 6,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &mainnet,
                    ApprovalRequired: true,
                },
            },
        },
        {
            name: "ERC20 transfer - All FromChains Disabled - All ToChains Disabled",
            input: &RouteInputParams{
                testnetMode:          false,
                SendType:             Transfer,
                AddrFrom:             common.HexToAddress("0x1"),
                AddrTo:               common.HexToAddress("0x2"),
                AmountIn:             (*hexutil.Big)(big.NewInt(testAmount1USDC)),
                TokenID:              pathprocessor.UsdcSymbol,
                DisabledFromChainIDs: []uint64{walletCommon.EthereumMainnet, walletCommon.OptimismMainnet, walletCommon.ArbitrumMainnet},
                DisabledToChainIDs:   []uint64{walletCommon.EthereumMainnet, walletCommon.OptimismMainnet, walletCommon.ArbitrumMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.UsdcSymbol,
                        Decimals: 6,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{},
        },
        {
            name: "ERC20 transfer - All FromChains - No Locked Amount - Enough Token Balance Across All Chains",
            input: &RouteInputParams{
                testnetMode: false,
                SendType:    Transfer,
                AddrFrom:    common.HexToAddress("0x1"),
                AddrTo:      common.HexToAddress("0x2"),
                AmountIn:    (*hexutil.Big)(big.NewInt(2.5 * testAmount100USDC)),
                TokenID:     pathprocessor.UsdcSymbol,

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.UsdcSymbol,
                        Decimals: 6,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &mainnet,
                    ToChain:          &mainnet,
                    AmountOut:        (*hexutil.Big)(big.NewInt(2.5 * testAmount100USDC)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &mainnet,
                    ToChain:          &mainnet,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount100USDC)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &mainnet,
                    ToChain:          &optimism,
                    AmountOut:        (*hexutil.Big)(big.NewInt(2.5*testAmount100USDC - testBonderFeeUSDC)),
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &mainnet,
                    ToChain:          &optimism,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount100USDC - testBonderFeeUSDC)),
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &mainnet,
                    ToChain:          &arbitrum,
                    AmountOut:        (*hexutil.Big)(big.NewInt(2.5*testAmount100USDC - testBonderFeeUSDC)),
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &mainnet,
                    ToChain:          &arbitrum,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount100USDC - testBonderFeeUSDC)),
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &optimism,
                    ToChain:          &optimism,
                    AmountOut:        (*hexutil.Big)(big.NewInt(0.5 * testAmount100USDC)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &optimism,
                    ToChain:          &optimism,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount100USDC)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &optimism,
                    ToChain:          &optimism,
                    AmountOut:        (*hexutil.Big)(big.NewInt(2.5 * testAmount100USDC)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &optimism,
                    ToChain:          &mainnet,
                    AmountOut:        (*hexutil.Big)(big.NewInt(0.5*testAmount100USDC - testBonderFeeUSDC)),
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &optimism,
                    ToChain:          &mainnet,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount100USDC - testBonderFeeUSDC)),
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &optimism,
                    ToChain:          &mainnet,
                    AmountOut:        (*hexutil.Big)(big.NewInt(2.5*testAmount100USDC - testBonderFeeUSDC)),
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &optimism,
                    ToChain:          &arbitrum,
                    AmountOut:        (*hexutil.Big)(big.NewInt(0.5*testAmount100USDC - testBonderFeeUSDC)),
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &optimism,
                    ToChain:          &arbitrum,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount100USDC - testBonderFeeUSDC)),
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &optimism,
                    ToChain:          &arbitrum,
                    AmountOut:        (*hexutil.Big)(big.NewInt(2.5*testAmount100USDC - testBonderFeeUSDC)),
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &arbitrum,
                    ToChain:          &arbitrum,
                    AmountOut:        (*hexutil.Big)(big.NewInt(0.5 * testAmount100USDC)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &arbitrum,
                    ToChain:          &arbitrum,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount100USDC)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &arbitrum,
                    ToChain:          &arbitrum,
                    AmountOut:        (*hexutil.Big)(big.NewInt(2.5 * testAmount100USDC)),
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &mainnet,
                    AmountOut:        (*hexutil.Big)(big.NewInt(0.5*testAmount100USDC - testBonderFeeUSDC)),
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &mainnet,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount100USDC - testBonderFeeUSDC)),
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &mainnet,
                    AmountOut:        (*hexutil.Big)(big.NewInt(2.5*testAmount100USDC - testBonderFeeUSDC)),
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &optimism,
                    AmountOut:        (*hexutil.Big)(big.NewInt(0.5*testAmount100USDC - testBonderFeeUSDC)),
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &optimism,
                    AmountOut:        (*hexutil.Big)(big.NewInt(testAmount100USDC - testBonderFeeUSDC)),
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &optimism,
                    AmountOut:        (*hexutil.Big)(big.NewInt(2.5*testAmount100USDC - testBonderFeeUSDC)),
                    ApprovalRequired: true,
                },
            },
        },
        {
            name: "Bridge - No Specific FromChain - No Specific ToChain",
            input: &RouteInputParams{
                testnetMode: false,
                SendType:    Bridge,
                AddrFrom:    common.HexToAddress("0x1"),
                AddrTo:      common.HexToAddress("0x2"),
                AmountIn:    (*hexutil.Big)(big.NewInt(testAmount1USDC)),
                TokenID:     pathprocessor.UsdcSymbol,

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.UsdcSymbol,
                        Decimals: 6,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &mainnet,
                    ToChain:          &optimism,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &mainnet,
                    ToChain:          &arbitrum,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &optimism,
                    ToChain:          &mainnet,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &optimism,
                    ToChain:          &arbitrum,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &mainnet,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &optimism,
                    ApprovalRequired: true,
                },
            },
        },
        {
            name: "Bridge - No Specific FromChain - Specific Single ToChain",
            input: &RouteInputParams{
                testnetMode:        false,
                SendType:           Bridge,
                AddrFrom:           common.HexToAddress("0x1"),
                AddrTo:             common.HexToAddress("0x2"),
                AmountIn:           (*hexutil.Big)(big.NewInt(testAmount1USDC)),
                TokenID:            pathprocessor.UsdcSymbol,
                DisabledToChainIDs: []uint64{walletCommon.OptimismMainnet, walletCommon.ArbitrumMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.UsdcSymbol,
                        Decimals: 6,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &optimism,
                    ToChain:          &mainnet,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &mainnet,
                    ApprovalRequired: true,
                },
            },
        },
        {
            name: "Bridge - No Specific FromChain - Specific Multiple ToChain",
            input: &RouteInputParams{
                testnetMode:        false,
                SendType:           Bridge,
                AddrFrom:           common.HexToAddress("0x1"),
                AddrTo:             common.HexToAddress("0x2"),
                AmountIn:           (*hexutil.Big)(big.NewInt(testAmount1USDC)),
                TokenID:            pathprocessor.UsdcSymbol,
                DisabledToChainIDs: []uint64{walletCommon.EthereumMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.UsdcSymbol,
                        Decimals: 6,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &mainnet,
                    ToChain:          &optimism,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &mainnet,
                    ToChain:          &arbitrum,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &optimism,
                    ToChain:          &arbitrum,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &optimism,
                    ApprovalRequired: true,
                },
            },
        },
        {
            name: "Bridge - Specific Single FromChain - No Specific ToChain",
            input: &RouteInputParams{
                testnetMode:          false,
                SendType:             Bridge,
                AddrFrom:             common.HexToAddress("0x1"),
                AddrTo:               common.HexToAddress("0x2"),
                AmountIn:             (*hexutil.Big)(big.NewInt(testAmount1USDC)),
                TokenID:              pathprocessor.UsdcSymbol,
                DisabledFromChainIDs: []uint64{walletCommon.EthereumMainnet, walletCommon.OptimismMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.UsdcSymbol,
                        Decimals: 6,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &mainnet,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &optimism,
                    ApprovalRequired: true,
                },
            },
        },
        {
            name: "Bridge - Specific Multiple FromChain - No Specific ToChain",
            input: &RouteInputParams{
                testnetMode:          false,
                SendType:             Bridge,
                AddrFrom:             common.HexToAddress("0x1"),
                AddrTo:               common.HexToAddress("0x2"),
                AmountIn:             (*hexutil.Big)(big.NewInt(testAmount1USDC)),
                TokenID:              pathprocessor.UsdcSymbol,
                DisabledFromChainIDs: []uint64{walletCommon.EthereumMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.UsdcSymbol,
                        Decimals: 6,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &optimism,
                    ToChain:          &mainnet,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &optimism,
                    ToChain:          &arbitrum,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &mainnet,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &optimism,
                    ApprovalRequired: true,
                },
            },
        },
        {
            name: "Bridge - Specific Single FromChain - Specific Single ToChain - Same Chains",
            input: &RouteInputParams{
                testnetMode:          false,
                SendType:             Bridge,
                AddrFrom:             common.HexToAddress("0x1"),
                AddrTo:               common.HexToAddress("0x2"),
                AmountIn:             (*hexutil.Big)(big.NewInt(testAmount1USDC)),
                TokenID:              pathprocessor.UsdcSymbol,
                DisabledFromChainIDs: []uint64{walletCommon.EthereumMainnet, walletCommon.OptimismMainnet},
                DisabledToChainIDs:   []uint64{walletCommon.EthereumMainnet, walletCommon.OptimismMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.UsdcSymbol,
                        Decimals: 6,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{},
        },
        {
            name: "Bridge - Specific Single FromChain - Specific Single ToChain - Different Chains",
            input: &RouteInputParams{
                testnetMode:          false,
                SendType:             Bridge,
                AddrFrom:             common.HexToAddress("0x1"),
                AddrTo:               common.HexToAddress("0x2"),
                AmountIn:             (*hexutil.Big)(big.NewInt(testAmount1USDC)),
                TokenID:              pathprocessor.UsdcSymbol,
                DisabledFromChainIDs: []uint64{walletCommon.EthereumMainnet, walletCommon.OptimismMainnet},
                DisabledToChainIDs:   []uint64{walletCommon.OptimismMainnet, walletCommon.ArbitrumMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.UsdcSymbol,
                        Decimals: 6,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &mainnet,
                    ApprovalRequired: true,
                },
            },
        },
        {
            name: "Bridge - Specific Multiple FromChain - Specific Multiple ToChain - Single Common Chain",
            input: &RouteInputParams{
                testnetMode:          false,
                SendType:             Bridge,
                AddrFrom:             common.HexToAddress("0x1"),
                AddrTo:               common.HexToAddress("0x2"),
                AmountIn:             (*hexutil.Big)(big.NewInt(testAmount1USDC)),
                TokenID:              pathprocessor.UsdcSymbol,
                DisabledFromChainIDs: []uint64{walletCommon.EthereumMainnet, walletCommon.OptimismMainnet},
                DisabledToChainIDs:   []uint64{walletCommon.EthereumMainnet, walletCommon.OptimismMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.UsdcSymbol,
                        Decimals: 6,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{},
        },
        {
            name: "Bridge - Specific Multiple FromChain - Specific Multiple ToChain - Multiple Common Chains",
            input: &RouteInputParams{
                testnetMode:          false,
                SendType:             Bridge,
                AddrFrom:             common.HexToAddress("0x1"),
                AddrTo:               common.HexToAddress("0x2"),
                AmountIn:             (*hexutil.Big)(big.NewInt(testAmount1USDC)),
                TokenID:              pathprocessor.UsdcSymbol,
                DisabledFromChainIDs: []uint64{walletCommon.OptimismMainnet},
                DisabledToChainIDs:   []uint64{walletCommon.OptimismMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.UsdcSymbol,
                        Decimals: 6,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &mainnet,
                    ToChain:          &arbitrum,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &mainnet,
                    ApprovalRequired: true,
                },
            },
        },
        {
            name: "Bridge - Specific Multiple FromChain - Specific Multiple ToChain - No Common Chains",
            input: &RouteInputParams{
                testnetMode:          false,
                SendType:             Bridge,
                AddrFrom:             common.HexToAddress("0x1"),
                AddrTo:               common.HexToAddress("0x2"),
                AmountIn:             (*hexutil.Big)(big.NewInt(testAmount1USDC)),
                TokenID:              pathprocessor.UsdcSymbol,
                DisabledFromChainIDs: []uint64{walletCommon.EthereumMainnet, walletCommon.OptimismMainnet},
                DisabledToChainIDs:   []uint64{walletCommon.OptimismMainnet, walletCommon.ArbitrumMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.UsdcSymbol,
                        Decimals: 6,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &mainnet,
                    ApprovalRequired: true,
                },
            },
        },
        {
            name: "Bridge - All FromChains Disabled - All ToChains Disabled",
            input: &RouteInputParams{
                testnetMode:          false,
                SendType:             Bridge,
                AddrFrom:             common.HexToAddress("0x1"),
                AddrTo:               common.HexToAddress("0x2"),
                AmountIn:             (*hexutil.Big)(big.NewInt(testAmount1USDC)),
                TokenID:              pathprocessor.UsdcSymbol,
                DisabledFromChainIDs: []uint64{walletCommon.EthereumMainnet, walletCommon.OptimismMainnet, walletCommon.ArbitrumMainnet},
                DisabledToChainIDs:   []uint64{walletCommon.EthereumMainnet, walletCommon.OptimismMainnet, walletCommon.ArbitrumMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.UsdcSymbol,
                        Decimals: 6,
                    },
                    tokenPrices:           testTokenPrices,
                    baseFee:               big.NewInt(testBaseFee),
                    suggestedFees:         testSuggestedFees,
                    balanceMap:            testBalanceMapPerChain,
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{},
        },
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {

            routes, err := router.SuggestedRoutesV2(context.Background(), tt.input)

            if tt.expectedError != nil {
                assert.Error(t, err)
                assert.Equal(t, tt.expectedError, err)
                assert.Nil(t, routes)
            } else {
                assert.NoError(t, err)
                assert.Equal(t, len(tt.expectedCandidates), len(routes.Candidates))

                for _, c := range routes.Candidates {
                    found := false
                    for _, expC := range tt.expectedCandidates {
                        if c.ProcessorName == expC.ProcessorName &&
                            c.FromChain.ChainID == expC.FromChain.ChainID &&
                            c.ToChain.ChainID == expC.ToChain.ChainID &&
                            c.ApprovalRequired == expC.ApprovalRequired &&
                            (expC.AmountOut == nil || c.AmountOut.ToInt().Cmp(expC.AmountOut.ToInt()) == 0) {
                            found = true
                            break
                        }
                    }

                    assert.True(t, found)
                }
            }
        })
    }
}

func TestNoBalanceForTheBestRouteRouterV2(t *testing.T) {
    router, cleanTmpDb := setupRouter(t)
    defer cleanTmpDb()

    tests := []struct {
        name               string
        input              *RouteInputParams
        expectedCandidates []*PathV2
        expectedBest       []*PathV2
        expectedError      error
    }{
        {
            name: "ERC20 transfer - Specific FromChain - Specific ToChain - Not Enough Token Balance",
            input: &RouteInputParams{
                testnetMode:          false,
                SendType:             Transfer,
                AddrFrom:             common.HexToAddress("0x1"),
                AddrTo:               common.HexToAddress("0x2"),
                AmountIn:             (*hexutil.Big)(big.NewInt(testAmount100USDC)),
                TokenID:              pathprocessor.UsdcSymbol,
                DisabledFromChainIDs: []uint64{walletCommon.EthereumMainnet, walletCommon.ArbitrumMainnet},
                DisabledToChainIDs:   []uint64{walletCommon.EthereumMainnet, walletCommon.ArbitrumMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.UsdcSymbol,
                        Decimals: 6,
                    },
                    tokenPrices:   testTokenPrices,
                    suggestedFees: testSuggestedFees,
                    balanceMap: map[string]*big.Int{
                        makeBalanceKey(walletCommon.OptimismMainnet, pathprocessor.UsdcSymbol): big.NewInt(0),
                    },
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedError: ErrNotEnoughTokenBalance,
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:         pathprocessor.ProcessorTransferName,
                    FromChain:             &optimism,
                    ToChain:               &optimism,
                    ApprovalRequired:      false,
                    requiredTokenBalance:  big.NewInt(testAmount100USDC),
                    requiredNativeBalance: big.NewInt((testBaseFee + testPriorityFeeLow) * testApprovalGasEstimation),
                },
            },
        },
        {
            name: "ERC20 transfer - Specific FromChain - Specific ToChain - Not Enough Native Balance",
            input: &RouteInputParams{
                testnetMode:          false,
                SendType:             Transfer,
                AddrFrom:             common.HexToAddress("0x1"),
                AddrTo:               common.HexToAddress("0x2"),
                AmountIn:             (*hexutil.Big)(big.NewInt(testAmount100USDC)),
                TokenID:              pathprocessor.UsdcSymbol,
                DisabledFromChainIDs: []uint64{walletCommon.EthereumMainnet, walletCommon.ArbitrumMainnet},
                DisabledToChainIDs:   []uint64{walletCommon.EthereumMainnet, walletCommon.ArbitrumMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.UsdcSymbol,
                        Decimals: 6,
                    },
                    tokenPrices:   testTokenPrices,
                    suggestedFees: testSuggestedFees,
                    balanceMap: map[string]*big.Int{
                        makeBalanceKey(walletCommon.OptimismMainnet, pathprocessor.UsdcSymbol): big.NewInt(testAmount100USDC),
                        makeBalanceKey(walletCommon.OptimismMainnet, pathprocessor.EthSymbol):  big.NewInt(0),
                    },
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedError: ErrNotEnoughNativeBalance,
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:         pathprocessor.ProcessorTransferName,
                    FromChain:             &optimism,
                    ToChain:               &optimism,
                    ApprovalRequired:      false,
                    requiredTokenBalance:  big.NewInt(testAmount100USDC),
                    requiredNativeBalance: big.NewInt((testBaseFee + testPriorityFeeLow) * testApprovalGasEstimation),
                },
            },
        },
        {
            name: "ERC20 transfer - No Specific FromChain - Specific ToChain - Not Enough Token Balance Across All Chains",
            input: &RouteInputParams{
                testnetMode:        false,
                SendType:           Transfer,
                AddrFrom:           common.HexToAddress("0x1"),
                AddrTo:             common.HexToAddress("0x2"),
                AmountIn:           (*hexutil.Big)(big.NewInt(testAmount100USDC)),
                TokenID:            pathprocessor.UsdcSymbol,
                DisabledToChainIDs: []uint64{walletCommon.EthereumMainnet, walletCommon.ArbitrumMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.UsdcSymbol,
                        Decimals: 6,
                    },
                    tokenPrices:   testTokenPrices,
                    suggestedFees: testSuggestedFees,
                    balanceMap: map[string]*big.Int{
                        makeBalanceKey(walletCommon.EthereumMainnet, pathprocessor.UsdcSymbol): big.NewInt(0),
                        makeBalanceKey(walletCommon.EthereumMainnet, pathprocessor.EthSymbol):  big.NewInt(0),
                        makeBalanceKey(walletCommon.OptimismMainnet, pathprocessor.UsdcSymbol): big.NewInt(0),
                        makeBalanceKey(walletCommon.OptimismMainnet, pathprocessor.EthSymbol):  big.NewInt(0),
                        makeBalanceKey(walletCommon.ArbitrumMainnet, pathprocessor.UsdcSymbol): big.NewInt(0),
                        makeBalanceKey(walletCommon.ArbitrumMainnet, pathprocessor.EthSymbol):  big.NewInt(0),
                    },
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedError: ErrNotEnoughTokenBalance,
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &mainnet,
                    ToChain:          &optimism,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorTransferName,
                    FromChain:        &optimism,
                    ToChain:          &optimism,
                    ApprovalRequired: false,
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &optimism,
                    ApprovalRequired: true,
                },
            },
        },
        {
            name: "ERC20 transfer - No Specific FromChain - Specific ToChain - Enough Token Balance On Arbitrum Chain But Not Enough Native Balance",
            input: &RouteInputParams{
                testnetMode:        false,
                SendType:           Transfer,
                AddrFrom:           common.HexToAddress("0x1"),
                AddrTo:             common.HexToAddress("0x2"),
                AmountIn:           (*hexutil.Big)(big.NewInt(testAmount100USDC)),
                TokenID:            pathprocessor.UsdcSymbol,
                DisabledToChainIDs: []uint64{walletCommon.EthereumMainnet, walletCommon.ArbitrumMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.UsdcSymbol,
                        Decimals: 6,
                    },
                    tokenPrices:   testTokenPrices,
                    suggestedFees: testSuggestedFees,
                    balanceMap: map[string]*big.Int{
                        makeBalanceKey(walletCommon.ArbitrumMainnet, pathprocessor.UsdcSymbol): big.NewInt(testAmount100USDC + testAmount100USDC),
                        makeBalanceKey(walletCommon.EthereumMainnet, pathprocessor.UsdcSymbol): big.NewInt(testAmount100USDC + testAmount100USDC),
                        makeBalanceKey(walletCommon.OptimismMainnet, pathprocessor.UsdcSymbol): big.NewInt(testAmount100USDC + testAmount100USDC),
                        makeBalanceKey(walletCommon.ArbitrumMainnet, pathprocessor.EthSymbol):  big.NewInt(0),
                        makeBalanceKey(walletCommon.EthereumMainnet, pathprocessor.EthSymbol):  big.NewInt(0),
                        makeBalanceKey(walletCommon.OptimismMainnet, pathprocessor.EthSymbol):  big.NewInt(0),
                    },
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedError: ErrNotEnoughNativeBalance,
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &mainnet,
                    ToChain:          &optimism,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:         pathprocessor.ProcessorTransferName,
                    FromChain:             &optimism,
                    ToChain:               &optimism,
                    ApprovalRequired:      false,
                    requiredTokenBalance:  big.NewInt(testAmount100USDC),
                    requiredNativeBalance: big.NewInt((testBaseFee + testPriorityFeeLow) * testApprovalGasEstimation),
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &optimism,
                    ApprovalRequired: true,
                },
            },
        },
        {
            name: "ERC20 transfer - No Specific FromChain - Specific ToChain - Enough Token Balance On Arbitrum Chain And Enough Native Balance On Arbitrum Chain",
            input: &RouteInputParams{
                testnetMode:        false,
                SendType:           Transfer,
                AddrFrom:           common.HexToAddress("0x1"),
                AddrTo:             common.HexToAddress("0x2"),
                AmountIn:           (*hexutil.Big)(big.NewInt(testAmount100USDC)),
                TokenID:            pathprocessor.UsdcSymbol,
                DisabledToChainIDs: []uint64{walletCommon.EthereumMainnet, walletCommon.ArbitrumMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.UsdcSymbol,
                        Decimals: 6,
                    },
                    tokenPrices:   testTokenPrices,
                    suggestedFees: testSuggestedFees,
                    balanceMap: map[string]*big.Int{
                        makeBalanceKey(walletCommon.ArbitrumMainnet, pathprocessor.UsdcSymbol): big.NewInt(testAmount100USDC + testAmount100USDC),
                        makeBalanceKey(walletCommon.ArbitrumMainnet, pathprocessor.EthSymbol):  big.NewInt(testAmount1ETHInWei),
                    },
                    estimationMap:         testEstimationMap,
                    bonderFeeMap:          testBbonderFeeMap,
                    approvalGasEstimation: testApprovalGasEstimation,
                    approvalL1Fee:         testApprovalL1Fee,
                },
            },
            expectedCandidates: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &mainnet,
                    ToChain:          &optimism,
                    ApprovalRequired: true,
                },
                {
                    ProcessorName:         pathprocessor.ProcessorTransferName,
                    FromChain:             &optimism,
                    ToChain:               &optimism,
                    ApprovalRequired:      false,
                    requiredTokenBalance:  big.NewInt(testAmount100USDC),
                    requiredNativeBalance: big.NewInt((testBaseFee + testPriorityFeeLow) * testApprovalGasEstimation),
                },
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &optimism,
                    ApprovalRequired: true,
                },
            },
            expectedBest: []*PathV2{
                {
                    ProcessorName:    pathprocessor.ProcessorBridgeHopName,
                    FromChain:        &arbitrum,
                    ToChain:          &optimism,
                    ApprovalRequired: true,
                },
            },
        },
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {

            routes, err := router.SuggestedRoutesV2(context.Background(), tt.input)

            if tt.expectedError != nil {
                assert.Error(t, err)
                assert.Equal(t, tt.expectedError, err)
                assert.NotNil(t, routes)
                assert.Equal(t, len(tt.expectedCandidates), len(routes.Candidates))
            } else {
                assert.NoError(t, err)
                assert.Equal(t, len(tt.expectedCandidates), len(routes.Candidates))
                assert.Equal(t, len(tt.expectedBest), len(routes.Best))

                for _, c := range routes.Candidates {
                    found := false
                    for _, expC := range tt.expectedCandidates {
                        if c.ProcessorName == expC.ProcessorName &&
                            c.FromChain.ChainID == expC.FromChain.ChainID &&
                            c.ToChain.ChainID == expC.ToChain.ChainID &&
                            c.ApprovalRequired == expC.ApprovalRequired &&
                            (expC.AmountOut == nil || c.AmountOut.ToInt().Cmp(expC.AmountOut.ToInt()) == 0) {
                            found = true
                            break
                        }
                    }

                    assert.True(t, found)
                }

                for _, c := range routes.Best {
                    found := false
                    for _, expC := range tt.expectedBest {
                        if c.ProcessorName == expC.ProcessorName &&
                            c.FromChain.ChainID == expC.FromChain.ChainID &&
                            c.ToChain.ChainID == expC.ToChain.ChainID &&
                            c.ApprovalRequired == expC.ApprovalRequired &&
                            (expC.AmountOut == nil || c.AmountOut.ToInt().Cmp(expC.AmountOut.ToInt()) == 0) {
                            found = true
                            break
                        }
                    }

                    assert.True(t, found)
                }
            }
        })
    }
}

func TestAmountOptions(t *testing.T) {
    router, cleanTmpDb := setupRouter(t)
    defer cleanTmpDb()

    tests := []struct {
        name                  string
        input                 *RouteInputParams
        expectedAmountOptions map[uint64][]amountOption
    }{
        {
            name: "Transfer - Single From Chain - No Locked Amount",
            input: &RouteInputParams{
                testnetMode:          false,
                SendType:             Transfer,
                AmountIn:             (*hexutil.Big)(big.NewInt(testAmount1ETHInWei)),
                TokenID:              pathprocessor.EthSymbol,
                DisabledFromChainIDs: []uint64{walletCommon.EthereumMainnet, walletCommon.ArbitrumMainnet},

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.EthSymbol,
                        Decimals: 18,
                    },
                    balanceMap: map[string]*big.Int{},
                },
            },
            expectedAmountOptions: map[uint64][]amountOption{
                walletCommon.OptimismMainnet: {
                    {
                        amount: big.NewInt(testAmount1ETHInWei),
                        locked: false,
                    },
                },
            },
        },
        {
            name: "Transfer - Single From Chain - Locked Amount To Single Chain Equal Total Amount",
            input: &RouteInputParams{
                testnetMode:          false,
                SendType:             Transfer,
                AmountIn:             (*hexutil.Big)(big.NewInt(testAmount1ETHInWei)),
                TokenID:              pathprocessor.EthSymbol,
                DisabledFromChainIDs: []uint64{walletCommon.EthereumMainnet, walletCommon.ArbitrumMainnet},
                FromLockedAmount: map[uint64]*hexutil.Big{
                    walletCommon.OptimismMainnet: (*hexutil.Big)(big.NewInt(testAmount1ETHInWei)),
                },

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.EthSymbol,
                        Decimals: 18,
                    },
                    balanceMap: map[string]*big.Int{},
                },
            },
            expectedAmountOptions: map[uint64][]amountOption{
                walletCommon.OptimismMainnet: {
                    {
                        amount: big.NewInt(testAmount1ETHInWei),
                        locked: true,
                    },
                },
            },
        },
        {
            name: "Transfer - Multiple From Chains - Locked Amount To Single Chain Is Less Than Total Amount",
            input: &RouteInputParams{
                testnetMode:          false,
                SendType:             Transfer,
                AmountIn:             (*hexutil.Big)(big.NewInt(testAmount2ETHInWei)),
                TokenID:              pathprocessor.EthSymbol,
                DisabledFromChainIDs: []uint64{walletCommon.EthereumMainnet},
                FromLockedAmount: map[uint64]*hexutil.Big{
                    walletCommon.OptimismMainnet: (*hexutil.Big)(big.NewInt(testAmount1ETHInWei)),
                },

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.EthSymbol,
                        Decimals: 18,
                    },
                    balanceMap: map[string]*big.Int{},
                },
            },
            expectedAmountOptions: map[uint64][]amountOption{
                walletCommon.OptimismMainnet: {
                    {
                        amount: big.NewInt(testAmount1ETHInWei),
                        locked: true,
                    },
                },
                walletCommon.ArbitrumMainnet: {
                    {
                        amount: big.NewInt(testAmount1ETHInWei),
                        locked: false,
                    },
                },
            },
        },
        {
            name: "Transfer - Multiple From Chains - Locked Amount To Multiple Chains",
            input: &RouteInputParams{
                testnetMode:          false,
                SendType:             Transfer,
                AmountIn:             (*hexutil.Big)(big.NewInt(testAmount2ETHInWei)),
                TokenID:              pathprocessor.EthSymbol,
                DisabledFromChainIDs: []uint64{walletCommon.EthereumMainnet},
                FromLockedAmount: map[uint64]*hexutil.Big{
                    walletCommon.OptimismMainnet: (*hexutil.Big)(big.NewInt(testAmount1ETHInWei)),
                    walletCommon.ArbitrumMainnet: (*hexutil.Big)(big.NewInt(testAmount1ETHInWei)),
                },

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.EthSymbol,
                        Decimals: 18,
                    },
                    balanceMap: map[string]*big.Int{},
                },
            },
            expectedAmountOptions: map[uint64][]amountOption{
                walletCommon.OptimismMainnet: {
                    {
                        amount: big.NewInt(testAmount1ETHInWei),
                        locked: true,
                    },
                },
                walletCommon.ArbitrumMainnet: {
                    {
                        amount: big.NewInt(testAmount1ETHInWei),
                        locked: true,
                    },
                },
            },
        },
        {
            name: "Transfer - All From Chains - Locked Amount To Multiple Chains Equal Total Amount",
            input: &RouteInputParams{
                testnetMode: false,
                SendType:    Transfer,
                AmountIn:    (*hexutil.Big)(big.NewInt(testAmount2ETHInWei)),
                TokenID:     pathprocessor.EthSymbol,
                FromLockedAmount: map[uint64]*hexutil.Big{
                    walletCommon.OptimismMainnet: (*hexutil.Big)(big.NewInt(testAmount1ETHInWei)),
                    walletCommon.ArbitrumMainnet: (*hexutil.Big)(big.NewInt(testAmount1ETHInWei)),
                },

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.EthSymbol,
                        Decimals: 18,
                    },
                    balanceMap: map[string]*big.Int{},
                },
            },
            expectedAmountOptions: map[uint64][]amountOption{
                walletCommon.OptimismMainnet: {
                    {
                        amount: big.NewInt(testAmount1ETHInWei),
                        locked: true,
                    },
                },
                walletCommon.ArbitrumMainnet: {
                    {
                        amount: big.NewInt(testAmount1ETHInWei),
                        locked: true,
                    },
                },
            },
        },
        {
            name: "Transfer - All From Chains - Locked Amount To Multiple Chains Is Less Than Total Amount",
            input: &RouteInputParams{
                testnetMode: false,
                SendType:    Transfer,
                AmountIn:    (*hexutil.Big)(big.NewInt(testAmount5ETHInWei)),
                TokenID:     pathprocessor.EthSymbol,
                FromLockedAmount: map[uint64]*hexutil.Big{
                    walletCommon.OptimismMainnet: (*hexutil.Big)(big.NewInt(testAmount1ETHInWei)),
                    walletCommon.ArbitrumMainnet: (*hexutil.Big)(big.NewInt(testAmount1ETHInWei)),
                },

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.EthSymbol,
                        Decimals: 18,
                    },
                    balanceMap: map[string]*big.Int{},
                },
            },
            expectedAmountOptions: map[uint64][]amountOption{
                walletCommon.OptimismMainnet: {
                    {
                        amount: big.NewInt(testAmount1ETHInWei),
                        locked: true,
                    },
                },
                walletCommon.ArbitrumMainnet: {
                    {
                        amount: big.NewInt(testAmount1ETHInWei),
                        locked: true,
                    },
                },
                walletCommon.EthereumMainnet: {
                    {
                        amount: big.NewInt(testAmount3ETHInWei),
                        locked: false,
                    },
                },
            },
        },
        {
            name: "Transfer - All From Chain - No Locked Amount - Enough Token Balance If All Chains Are Used",
            input: &RouteInputParams{
                testnetMode: false,
                SendType:    Transfer,
                AmountIn:    (*hexutil.Big)(big.NewInt(testAmount3ETHInWei)),
                TokenID:     pathprocessor.EthSymbol,

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.EthSymbol,
                        Decimals: 18,
                    },
                    balanceMap: map[string]*big.Int{
                        makeBalanceKey(walletCommon.EthereumMainnet, pathprocessor.EthSymbol): big.NewInt(testAmount1ETHInWei),
                        makeBalanceKey(walletCommon.OptimismMainnet, pathprocessor.EthSymbol): big.NewInt(testAmount1ETHInWei),
                        makeBalanceKey(walletCommon.ArbitrumMainnet, pathprocessor.EthSymbol): big.NewInt(testAmount1ETHInWei),
                    },
                },
            },
            expectedAmountOptions: map[uint64][]amountOption{
                walletCommon.OptimismMainnet: {
                    {
                        amount: big.NewInt(testAmount3ETHInWei),
                        locked: false,
                    },
                    {
                        amount: big.NewInt(testAmount1ETHInWei),
                        locked: false,
                    },
                },
                walletCommon.ArbitrumMainnet: {
                    {
                        amount: big.NewInt(testAmount3ETHInWei),
                        locked: false,
                    },
                    {
                        amount: big.NewInt(testAmount1ETHInWei),
                        locked: false,
                    },
                },
                walletCommon.EthereumMainnet: {
                    {
                        amount: big.NewInt(testAmount3ETHInWei),
                        locked: false,
                    },
                    {
                        amount: big.NewInt(testAmount1ETHInWei),
                        locked: false,
                    },
                },
            },
        },
        {
            name: "Transfer - All From Chain - Locked Amount To Single Chain - Enough Token Balance If All Chains Are Used",
            input: &RouteInputParams{
                testnetMode: false,
                SendType:    Transfer,
                AmountIn:    (*hexutil.Big)(big.NewInt(testAmount3ETHInWei)),
                TokenID:     pathprocessor.EthSymbol,
                FromLockedAmount: map[uint64]*hexutil.Big{
                    walletCommon.OptimismMainnet: (*hexutil.Big)(big.NewInt(testAmount0Point5ETHInWei)),
                },

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.EthSymbol,
                        Decimals: 18,
                    },
                    balanceMap: map[string]*big.Int{
                        makeBalanceKey(walletCommon.EthereumMainnet, pathprocessor.EthSymbol): big.NewInt(testAmount2ETHInWei),
                        makeBalanceKey(walletCommon.OptimismMainnet, pathprocessor.EthSymbol): big.NewInt(testAmount1ETHInWei),
                        makeBalanceKey(walletCommon.ArbitrumMainnet, pathprocessor.EthSymbol): big.NewInt(testAmount3ETHInWei),
                    },
                },
            },
            expectedAmountOptions: map[uint64][]amountOption{
                walletCommon.OptimismMainnet: {
                    {
                        amount: big.NewInt(testAmount0Point5ETHInWei),
                        locked: true,
                    },
                },
                walletCommon.ArbitrumMainnet: {
                    {
                        amount: big.NewInt(testAmount2ETHInWei + testAmount0Point5ETHInWei),
                        locked: false,
                    },
                    {
                        amount: big.NewInt(testAmount0Point5ETHInWei),
                        locked: false,
                    },
                },
                walletCommon.EthereumMainnet: {
                    {
                        amount: big.NewInt(testAmount2ETHInWei + testAmount0Point5ETHInWei),
                        locked: false,
                    },
                    {
                        amount: big.NewInt(testAmount2ETHInWei),
                        locked: false,
                    },
                },
            },
        },
        {
            name: "Transfer - All From Chain - Locked Amount To Multiple Chains - Enough Token Balance If All Chains Are Used",
            input: &RouteInputParams{
                testnetMode: false,
                SendType:    Transfer,
                AmountIn:    (*hexutil.Big)(big.NewInt(testAmount3ETHInWei)),
                TokenID:     pathprocessor.EthSymbol,
                FromLockedAmount: map[uint64]*hexutil.Big{
                    walletCommon.OptimismMainnet: (*hexutil.Big)(big.NewInt(testAmount0Point5ETHInWei)),
                    walletCommon.EthereumMainnet: (*hexutil.Big)(big.NewInt(testAmount1ETHInWei)),
                },

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.EthSymbol,
                        Decimals: 18,
                    },
                    balanceMap: map[string]*big.Int{
                        makeBalanceKey(walletCommon.EthereumMainnet, pathprocessor.EthSymbol): big.NewInt(testAmount2ETHInWei),
                        makeBalanceKey(walletCommon.OptimismMainnet, pathprocessor.EthSymbol): big.NewInt(testAmount1ETHInWei),
                        makeBalanceKey(walletCommon.ArbitrumMainnet, pathprocessor.EthSymbol): big.NewInt(testAmount3ETHInWei),
                    },
                },
            },
            expectedAmountOptions: map[uint64][]amountOption{
                walletCommon.OptimismMainnet: {
                    {
                        amount: big.NewInt(testAmount0Point5ETHInWei),
                        locked: true,
                    },
                },
                walletCommon.ArbitrumMainnet: {
                    {
                        amount: big.NewInt(testAmount1ETHInWei + testAmount0Point5ETHInWei),
                        locked: false,
                    },
                },
                walletCommon.EthereumMainnet: {
                    {
                        amount: big.NewInt(testAmount1ETHInWei),
                        locked: true,
                    },
                },
            },
        },
        {
            name: "Transfer - All From Chain - No Locked Amount - Not Enough Token Balance",
            input: &RouteInputParams{
                testnetMode: false,
                SendType:    Transfer,
                AmountIn:    (*hexutil.Big)(big.NewInt(testAmount5ETHInWei)),
                TokenID:     pathprocessor.EthSymbol,

                testsMode: true,
                testParams: &routerTestParams{
                    tokenFrom: &token.Token{
                        ChainID:  1,
                        Symbol:   pathprocessor.EthSymbol,
                        Decimals: 18,
                    },
                    balanceMap: map[string]*big.Int{
                        makeBalanceKey(walletCommon.EthereumMainnet, pathprocessor.EthSymbol): big.NewInt(testAmount1ETHInWei),
                        makeBalanceKey(walletCommon.OptimismMainnet, pathprocessor.EthSymbol): big.NewInt(testAmount1ETHInWei),
                        makeBalanceKey(walletCommon.ArbitrumMainnet, pathprocessor.EthSymbol): big.NewInt(testAmount1ETHInWei),
                    },
                },
            },
            expectedAmountOptions: map[uint64][]amountOption{
                walletCommon.OptimismMainnet: {
                    {
                        amount: big.NewInt(testAmount5ETHInWei),
                        locked: false,
                    },
                },
                walletCommon.ArbitrumMainnet: {
                    {
                        amount: big.NewInt(testAmount5ETHInWei),
                        locked: false,
                    },
                },
                walletCommon.EthereumMainnet: {
                    {
                        amount: big.NewInt(testAmount5ETHInWei),
                        locked: false,
                    },
                },
            },
        },
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {

            selectedFromChains, _, err := router.getSelectedChains(tt.input)
            assert.NoError(t, err)

            amountOptions, err := router.findOptionsForSendingAmount(tt.input, selectedFromChains, tt.input.testParams.balanceMap)
            assert.NoError(t, err)

            assert.Equal(t, len(tt.expectedAmountOptions), len(amountOptions))
            assert.True(t, amountOptionsMapsEqual(tt.expectedAmountOptions, amountOptions))
        })
    }
}