github.com/status-im/status-go@v1.1.0/services/wallet/router/filter_test.go (about)

     1  package router
     2  
     3  import (
     4  	"math/big"
     5  	"testing"
     6  
     7  	"github.com/ethereum/go-ethereum/common/hexutil"
     8  
     9  	"github.com/status-im/status-go/params"
    10  	"github.com/status-im/status-go/services/wallet/router/pathprocessor"
    11  	"github.com/status-im/status-go/services/wallet/router/routes"
    12  
    13  	"github.com/stretchr/testify/assert"
    14  )
    15  
    16  var (
    17  	network1 = &params.Network{ChainID: 1}
    18  	network2 = &params.Network{ChainID: 2}
    19  	network3 = &params.Network{ChainID: 3}
    20  	network4 = &params.Network{ChainID: 4}
    21  	network5 = &params.Network{ChainID: 5}
    22  
    23  	amount0 = hexutil.Big(*big.NewInt(0))
    24  	amount1 = hexutil.Big(*big.NewInt(100))
    25  	amount2 = hexutil.Big(*big.NewInt(200))
    26  	amount3 = hexutil.Big(*big.NewInt(300))
    27  	amount4 = hexutil.Big(*big.NewInt(400))
    28  	amount5 = hexutil.Big(*big.NewInt(500))
    29  
    30  	path0 = &routes.Path{FromChain: network4, AmountIn: &amount0}
    31  
    32  	pathC1A1 = &routes.Path{FromChain: network1, AmountIn: &amount1}
    33  
    34  	pathC2A1 = &routes.Path{FromChain: network2, AmountIn: &amount1}
    35  	pathC2A2 = &routes.Path{FromChain: network2, AmountIn: &amount2}
    36  
    37  	pathC3A1 = &routes.Path{FromChain: network3, AmountIn: &amount1}
    38  	pathC3A2 = &routes.Path{FromChain: network3, AmountIn: &amount2}
    39  	pathC3A3 = &routes.Path{FromChain: network3, AmountIn: &amount3}
    40  
    41  	pathC4A1 = &routes.Path{FromChain: network4, AmountIn: &amount1}
    42  	pathC4A4 = &routes.Path{FromChain: network4, AmountIn: &amount4}
    43  
    44  	pathC5A5 = &routes.Path{FromChain: network5, AmountIn: &amount5}
    45  )
    46  
    47  func routesEqual(t *testing.T, expected, actual []routes.Route) bool {
    48  	if len(expected) != len(actual) {
    49  		return false
    50  	}
    51  	for i := range expected {
    52  		if !pathsEqual(t, expected[i], actual[i]) {
    53  			return false
    54  		}
    55  	}
    56  	return true
    57  }
    58  
    59  func pathsEqual(t *testing.T, expected, actual routes.Route) bool {
    60  	if len(expected) != len(actual) {
    61  		return false
    62  	}
    63  	for i := range expected {
    64  		if !pathEqual(t, expected[i], actual[i]) {
    65  			return false
    66  		}
    67  	}
    68  	return true
    69  }
    70  
    71  func pathEqual(t *testing.T, expected, actual *routes.Path) bool {
    72  	if expected.FromChain.ChainID != actual.FromChain.ChainID {
    73  		t.Logf("expected chain ID '%d' , actual chain ID '%d'", expected.FromChain.ChainID, actual.FromChain.ChainID)
    74  		return false
    75  	}
    76  	if expected.AmountIn.ToInt().Cmp(actual.AmountIn.ToInt()) != 0 {
    77  		t.Logf("expected AmountIn '%d' , actual AmountIn '%d'", expected.AmountIn.ToInt(), actual.AmountIn.ToInt())
    78  		return false
    79  	}
    80  	if expected.AmountInLocked != actual.AmountInLocked {
    81  		t.Logf("expected AmountInLocked '%t' , actual AmountInLocked '%t'", expected.AmountInLocked, actual.AmountInLocked)
    82  		return false
    83  	}
    84  	return true
    85  }
    86  
    87  func TestSetupRouteValidationMaps(t *testing.T) {
    88  	tests := []struct {
    89  		name             string
    90  		fromLockedAmount map[uint64]*hexutil.Big
    91  		expectedIncluded map[uint64]bool
    92  		expectedExcluded map[uint64]bool
    93  	}{
    94  		{
    95  			name: "Mixed zero and non-zero amounts",
    96  			fromLockedAmount: map[uint64]*hexutil.Big{
    97  				1: (*hexutil.Big)(pathprocessor.ZeroBigIntValue),
    98  				2: (*hexutil.Big)(big.NewInt(200)),
    99  				3: (*hexutil.Big)(pathprocessor.ZeroBigIntValue),
   100  				4: (*hexutil.Big)(big.NewInt(400)),
   101  			},
   102  			expectedIncluded: map[uint64]bool{
   103  				2: false,
   104  				4: false,
   105  			},
   106  			expectedExcluded: map[uint64]bool{
   107  				1: false,
   108  				3: false,
   109  			},
   110  		},
   111  		{
   112  			name: "All non-zero amounts",
   113  			fromLockedAmount: map[uint64]*hexutil.Big{
   114  				1: (*hexutil.Big)(big.NewInt(100)),
   115  				2: (*hexutil.Big)(big.NewInt(200)),
   116  			},
   117  			expectedIncluded: map[uint64]bool{
   118  				1: false,
   119  				2: false,
   120  			},
   121  			expectedExcluded: map[uint64]bool{},
   122  		},
   123  		{
   124  			name: "All zero amounts",
   125  			fromLockedAmount: map[uint64]*hexutil.Big{
   126  				1: (*hexutil.Big)(pathprocessor.ZeroBigIntValue),
   127  				2: (*hexutil.Big)(pathprocessor.ZeroBigIntValue),
   128  			},
   129  			expectedIncluded: map[uint64]bool{},
   130  			expectedExcluded: map[uint64]bool{
   131  				1: false,
   132  				2: false,
   133  			},
   134  		},
   135  		{
   136  			name: "Single non-zero amount",
   137  			fromLockedAmount: map[uint64]*hexutil.Big{
   138  				1: (*hexutil.Big)(big.NewInt(100)),
   139  			},
   140  			expectedIncluded: map[uint64]bool{
   141  				1: false,
   142  			},
   143  			expectedExcluded: map[uint64]bool{},
   144  		},
   145  		{
   146  			name: "Single zero amount",
   147  			fromLockedAmount: map[uint64]*hexutil.Big{
   148  				1: (*hexutil.Big)(pathprocessor.ZeroBigIntValue),
   149  			},
   150  			expectedIncluded: map[uint64]bool{},
   151  			expectedExcluded: map[uint64]bool{
   152  				1: false,
   153  			},
   154  		},
   155  		{
   156  			name:             "Empty map",
   157  			fromLockedAmount: map[uint64]*hexutil.Big{},
   158  			expectedIncluded: map[uint64]bool{},
   159  			expectedExcluded: map[uint64]bool{},
   160  		},
   161  	}
   162  
   163  	for _, tt := range tests {
   164  		t.Run(tt.name, func(t *testing.T) {
   165  			included, excluded := setupRouteValidationMaps(tt.fromLockedAmount)
   166  			assert.Equal(t, tt.expectedIncluded, included)
   167  			assert.Equal(t, tt.expectedExcluded, excluded)
   168  		})
   169  	}
   170  }
   171  
   172  func TestCalculateRestAmountIn(t *testing.T) {
   173  	tests := []struct {
   174  		name        string
   175  		route       routes.Route
   176  		excludePath *routes.Path
   177  		expected    *big.Int
   178  	}{
   179  		{
   180  			name:        "Exclude pathC1A1",
   181  			route:       routes.Route{pathC1A1, pathC2A2, pathC3A3},
   182  			excludePath: pathC1A1,
   183  			expected:    big.NewInt(500), // 200 + 300
   184  		},
   185  		{
   186  			name:        "Exclude pathC2A2",
   187  			route:       routes.Route{pathC1A1, pathC2A2, pathC3A3},
   188  			excludePath: pathC2A2,
   189  			expected:    big.NewInt(400), // 100 + 300
   190  		},
   191  		{
   192  			name:        "Exclude pathC3A3",
   193  			route:       routes.Route{pathC1A1, pathC2A2, pathC3A3},
   194  			excludePath: pathC3A3,
   195  			expected:    big.NewInt(300), // 100 + 200
   196  		},
   197  		{
   198  			name:        "Single path, exclude that path",
   199  			route:       routes.Route{pathC1A1},
   200  			excludePath: pathC1A1,
   201  			expected:    big.NewInt(0), // No other paths
   202  		},
   203  		{
   204  			name:        "Empty route",
   205  			route:       routes.Route{},
   206  			excludePath: pathC1A1,
   207  			expected:    big.NewInt(0), // No paths
   208  		},
   209  		{
   210  			name:        "Empty route, with nil exclude",
   211  			route:       routes.Route{},
   212  			excludePath: nil,
   213  			expected:    big.NewInt(0), // No paths
   214  		},
   215  	}
   216  
   217  	for _, tt := range tests {
   218  		t.Run(tt.name, func(t *testing.T) {
   219  			result := calculateRestAmountIn(tt.route, tt.excludePath)
   220  			assert.Equal(t, tt.expected, result)
   221  		})
   222  	}
   223  }
   224  
   225  func TestIsValidForNetworkCompliance(t *testing.T) {
   226  	tests := []struct {
   227  		name           string
   228  		route          routes.Route
   229  		fromIncluded   map[uint64]bool
   230  		fromExcluded   map[uint64]bool
   231  		expectedResult bool
   232  	}{
   233  		{
   234  			name:           "Route with all included chain IDs",
   235  			route:          routes.Route{pathC1A1, pathC2A2},
   236  			fromIncluded:   map[uint64]bool{1: true, 2: true},
   237  			fromExcluded:   map[uint64]bool{},
   238  			expectedResult: true,
   239  		},
   240  		{
   241  			name:           "Route with fromExcluded only",
   242  			route:          routes.Route{pathC1A1, pathC2A2},
   243  			fromIncluded:   map[uint64]bool{},
   244  			fromExcluded:   map[uint64]bool{3: false, 4: false},
   245  			expectedResult: true,
   246  		},
   247  		{
   248  			name:           "Route without excluded chain IDs",
   249  			route:          routes.Route{pathC1A1, pathC2A2},
   250  			fromIncluded:   map[uint64]bool{1: false, 2: false},
   251  			fromExcluded:   map[uint64]bool{3: false, 4: false},
   252  			expectedResult: true,
   253  		},
   254  		{
   255  			name:           "Route with an excluded chain ID",
   256  			route:          routes.Route{pathC1A1, pathC3A3},
   257  			fromIncluded:   map[uint64]bool{1: false, 2: false},
   258  			fromExcluded:   map[uint64]bool{3: false, 4: false},
   259  			expectedResult: false,
   260  		},
   261  		{
   262  			name:           "Route missing one included chain ID",
   263  			route:          routes.Route{pathC1A1},
   264  			fromIncluded:   map[uint64]bool{1: false, 2: false},
   265  			fromExcluded:   map[uint64]bool{},
   266  			expectedResult: false,
   267  		},
   268  		{
   269  			name:           "Route with no fromIncluded or fromExcluded",
   270  			route:          routes.Route{pathC1A1, pathC2A2},
   271  			fromIncluded:   map[uint64]bool{},
   272  			fromExcluded:   map[uint64]bool{},
   273  			expectedResult: true,
   274  		},
   275  		{
   276  			name:           "Empty route",
   277  			route:          routes.Route{},
   278  			fromIncluded:   map[uint64]bool{1: false, 2: false},
   279  			fromExcluded:   map[uint64]bool{3: false, 4: false},
   280  			expectedResult: false,
   281  		},
   282  	}
   283  
   284  	for _, tt := range tests {
   285  		t.Run(tt.name, func(t *testing.T) {
   286  			result := isValidForNetworkCompliance(tt.route, tt.fromIncluded, tt.fromExcluded)
   287  			assert.Equal(t, tt.expectedResult, result)
   288  		})
   289  	}
   290  }
   291  
   292  func TestHasSufficientCapacity(t *testing.T) {
   293  	tests := []struct {
   294  		name             string
   295  		route            routes.Route
   296  		amountIn         *big.Int
   297  		fromLockedAmount map[uint64]*hexutil.Big
   298  		expected         bool
   299  	}{
   300  		{
   301  			name:             "All paths meet required amount",
   302  			route:            routes.Route{pathC1A1, pathC2A2, pathC3A3},
   303  			amountIn:         big.NewInt(600),
   304  			fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2, 3: &amount3},
   305  			expected:         true,
   306  		},
   307  		// TODO: Find out what the expected behaviour for this case should be
   308  		// I expect false but the test returns true
   309  		/*
   310  			{
   311  				name:             "A path does not meet required amount",
   312  				route:            routes.Route{pathC1A1, pathC2A2, pathC3A3},
   313  				amountIn:         big.NewInt(600),
   314  				fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2, 4: &amount4},
   315  				expected:         false,
   316  			},
   317  		*/
   318  		{
   319  			name:             "No fromLockedAmount",
   320  			route:            routes.Route{pathC1A1, pathC2A2, pathC3A3},
   321  			amountIn:         big.NewInt(600),
   322  			fromLockedAmount: map[uint64]*hexutil.Big{},
   323  			expected:         true,
   324  		},
   325  		{
   326  			name:             "Single path meets required amount",
   327  			route:            routes.Route{pathC1A1},
   328  			amountIn:         big.NewInt(100),
   329  			fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1},
   330  			expected:         true,
   331  		},
   332  		{
   333  			name:             "Single path does not meet required amount",
   334  			route:            routes.Route{pathC1A1},
   335  			amountIn:         big.NewInt(200),
   336  			fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1},
   337  			expected:         false,
   338  		},
   339  		{
   340  			name:             "Path meets required amount with excess",
   341  			route:            routes.Route{pathC1A1, pathC2A2},
   342  			amountIn:         big.NewInt(250),
   343  			fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2},
   344  			expected:         true,
   345  		},
   346  		{
   347  			name:             "Path does not meet required amount due to insufficient rest",
   348  			route:            routes.Route{pathC1A1, pathC2A2, pathC4A4},
   349  			amountIn:         big.NewInt(800),
   350  			fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 4: &amount4},
   351  			expected:         false,
   352  		},
   353  		{
   354  			name:             "Empty route",
   355  			route:            routes.Route{},
   356  			amountIn:         big.NewInt(500),
   357  			fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2},
   358  			expected:         true,
   359  		},
   360  	}
   361  
   362  	for _, tt := range tests {
   363  		t.Run(tt.name, func(t *testing.T) {
   364  			result := hasSufficientCapacity(tt.route, tt.amountIn, tt.fromLockedAmount)
   365  			assert.Equal(t, tt.expected, result)
   366  		})
   367  	}
   368  }
   369  
   370  func TestFilterNetworkCompliance(t *testing.T) {
   371  	tests := []struct {
   372  		name             string
   373  		routes           []routes.Route
   374  		fromLockedAmount map[uint64]*hexutil.Big
   375  		expected         []routes.Route
   376  	}{
   377  		{
   378  			name: "Mixed routes with valid and invalid paths",
   379  			routes: []routes.Route{
   380  				{
   381  					{FromChain: network1},
   382  					{FromChain: network3},
   383  				},
   384  				{
   385  					{FromChain: network2},
   386  					{FromChain: network3},
   387  				},
   388  				{
   389  					{FromChain: network1},
   390  					{FromChain: network2},
   391  					{FromChain: network3},
   392  				},
   393  			},
   394  			fromLockedAmount: map[uint64]*hexutil.Big{
   395  				1: (*hexutil.Big)(big.NewInt(100)),
   396  				2: (*hexutil.Big)(big.NewInt(0)),
   397  			},
   398  			expected: []routes.Route{
   399  				{
   400  					{FromChain: network1},
   401  					{FromChain: network3},
   402  				},
   403  			},
   404  		},
   405  		{
   406  			name: "All valid routes",
   407  			routes: []routes.Route{
   408  				{
   409  					{FromChain: network1},
   410  					{FromChain: network3},
   411  				},
   412  				{
   413  					{FromChain: network1},
   414  					{FromChain: network4},
   415  				},
   416  			},
   417  			fromLockedAmount: map[uint64]*hexutil.Big{
   418  				1: (*hexutil.Big)(big.NewInt(100)),
   419  			},
   420  			expected: []routes.Route{
   421  				{
   422  					{FromChain: network1},
   423  					{FromChain: network3},
   424  				},
   425  				{
   426  					{FromChain: network1},
   427  					{FromChain: network4},
   428  				},
   429  			},
   430  		},
   431  		{
   432  			name: "All invalid routes",
   433  			routes: []routes.Route{
   434  				{
   435  					{FromChain: network2},
   436  					{FromChain: network3},
   437  				},
   438  				{
   439  					{FromChain: network4},
   440  					{FromChain: network5},
   441  				},
   442  			},
   443  			fromLockedAmount: map[uint64]*hexutil.Big{
   444  				1: (*hexutil.Big)(big.NewInt(100)),
   445  				2: (*hexutil.Big)(big.NewInt(0)),
   446  			},
   447  			expected: []routes.Route{},
   448  		},
   449  		{
   450  			name:   "Empty routes",
   451  			routes: []routes.Route{},
   452  			fromLockedAmount: map[uint64]*hexutil.Big{
   453  				1: (*hexutil.Big)(big.NewInt(100)),
   454  			},
   455  			expected: []routes.Route{},
   456  		},
   457  		{
   458  			name: "No locked amounts",
   459  			routes: []routes.Route{
   460  				{
   461  					{FromChain: network1},
   462  					{FromChain: network2},
   463  				},
   464  				{
   465  					{FromChain: network3},
   466  					{FromChain: network4},
   467  				},
   468  			},
   469  			fromLockedAmount: map[uint64]*hexutil.Big{},
   470  			expected: []routes.Route{
   471  				{
   472  					{FromChain: network1},
   473  					{FromChain: network2},
   474  				},
   475  				{
   476  					{FromChain: network3},
   477  					{FromChain: network4},
   478  				},
   479  			},
   480  		},
   481  		{
   482  			name: "Single route with mixed valid and invalid paths",
   483  			routes: []routes.Route{
   484  				{
   485  					{FromChain: network1},
   486  					{FromChain: network2},
   487  					{FromChain: network3},
   488  				},
   489  			},
   490  			fromLockedAmount: map[uint64]*hexutil.Big{
   491  				1: (*hexutil.Big)(big.NewInt(100)),
   492  				2: (*hexutil.Big)(big.NewInt(0)),
   493  			},
   494  			expected: []routes.Route{},
   495  		},
   496  		{
   497  			name: "Routes with duplicate chain IDs",
   498  			routes: []routes.Route{
   499  				{
   500  					{FromChain: network1},
   501  					{FromChain: network1},
   502  					{FromChain: network2},
   503  				},
   504  			},
   505  			fromLockedAmount: map[uint64]*hexutil.Big{
   506  				1: (*hexutil.Big)(big.NewInt(100)),
   507  			},
   508  			expected: []routes.Route{
   509  				{
   510  					{FromChain: network1},
   511  					{FromChain: network1},
   512  					{FromChain: network2},
   513  				},
   514  			},
   515  		},
   516  		{
   517  			name: "Minimum and maximum chain IDs",
   518  			routes: []routes.Route{
   519  				{
   520  					{FromChain: &params.Network{ChainID: 0}},
   521  					{FromChain: &params.Network{ChainID: ^uint64(0)}},
   522  				},
   523  			},
   524  			fromLockedAmount: map[uint64]*hexutil.Big{
   525  				0:          (*hexutil.Big)(big.NewInt(100)),
   526  				^uint64(0): (*hexutil.Big)(big.NewInt(100)),
   527  			},
   528  			expected: []routes.Route{
   529  				{
   530  					{FromChain: &params.Network{ChainID: 0}},
   531  					{FromChain: &params.Network{ChainID: ^uint64(0)}},
   532  				},
   533  			},
   534  		},
   535  		{
   536  			name: "Large number of routes",
   537  			routes: func() []routes.Route {
   538  				var routes1 []routes.Route
   539  				for i := 0; i < 1000; i++ {
   540  					routes1 = append(routes1, routes.Route{
   541  						{FromChain: &params.Network{ChainID: uint64(i + 1)}},
   542  						{FromChain: &params.Network{ChainID: uint64(i + 1001)}},
   543  					})
   544  				}
   545  				return routes1
   546  			}(),
   547  			fromLockedAmount: map[uint64]*hexutil.Big{
   548  				1:    (*hexutil.Big)(big.NewInt(100)),
   549  				1001: (*hexutil.Big)(big.NewInt(100)),
   550  			},
   551  			expected: func() []routes.Route {
   552  				var routes1 []routes.Route
   553  				for i := 0; i < 1; i++ {
   554  					routes1 = append(routes1, routes.Route{
   555  						{FromChain: &params.Network{ChainID: uint64(i + 1)}},
   556  						{FromChain: &params.Network{ChainID: uint64(i + 1001)}},
   557  					})
   558  				}
   559  				return routes1
   560  			}(),
   561  		},
   562  		{
   563  			name: "Routes with missing data",
   564  			routes: []routes.Route{
   565  				{
   566  					{FromChain: nil},
   567  					{FromChain: network2},
   568  				},
   569  				{
   570  					{FromChain: network1},
   571  					{FromChain: nil},
   572  				},
   573  			},
   574  			fromLockedAmount: map[uint64]*hexutil.Big{
   575  				1: (*hexutil.Big)(big.NewInt(100)),
   576  				2: (*hexutil.Big)(big.NewInt(0)),
   577  			},
   578  			expected: []routes.Route{},
   579  		},
   580  		{
   581  			name: "Consistency check",
   582  			routes: []routes.Route{
   583  				{
   584  					{FromChain: network1},
   585  					{FromChain: network2},
   586  				},
   587  				{
   588  					{FromChain: network1},
   589  					{FromChain: network3},
   590  				},
   591  			},
   592  			fromLockedAmount: map[uint64]*hexutil.Big{
   593  				1: (*hexutil.Big)(big.NewInt(100)),
   594  			},
   595  			expected: []routes.Route{
   596  				{
   597  					{FromChain: network1},
   598  					{FromChain: network2},
   599  				},
   600  				{
   601  					{FromChain: network1},
   602  					{FromChain: network3},
   603  				},
   604  			},
   605  		},
   606  		{
   607  			name: "Routes without excluded chain IDs, missing included path",
   608  			routes: []routes.Route{
   609  				{pathC1A1, pathC2A2},
   610  				{pathC2A2, pathC3A3},
   611  			},
   612  			fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2},
   613  			expected: []routes.Route{
   614  				{pathC1A1, pathC2A2},
   615  			},
   616  		},
   617  		{
   618  			name: "Routes with an excluded chain ID",
   619  			routes: []routes.Route{
   620  				{pathC1A1, pathC2A2},
   621  				{pathC2A2, pathC3A3, path0},
   622  			},
   623  			fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2, 4: &amount0},
   624  			expected: []routes.Route{
   625  				{pathC1A1, pathC2A2},
   626  			},
   627  		},
   628  		{
   629  			name: "Routes with all included chain IDs",
   630  			routes: []routes.Route{
   631  				{pathC1A1, pathC2A2, pathC3A3},
   632  			},
   633  			fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2, 3: &amount3},
   634  			expected: []routes.Route{
   635  				{pathC1A1, pathC2A2, pathC3A3},
   636  			},
   637  		},
   638  		{
   639  			name: "Routes missing one included chain ID",
   640  			routes: []routes.Route{
   641  				{pathC1A1, pathC2A2},
   642  				{pathC1A1},
   643  			},
   644  			fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2, 3: &amount3},
   645  			expected:         []routes.Route{},
   646  		},
   647  		{
   648  			name: "Routes with no fromLockedAmount",
   649  			routes: []routes.Route{
   650  				{pathC1A1, pathC2A2},
   651  				{pathC2A2, pathC3A3},
   652  			},
   653  			fromLockedAmount: map[uint64]*hexutil.Big{},
   654  			expected: []routes.Route{
   655  				{pathC1A1, pathC2A2},
   656  				{pathC2A2, pathC3A3},
   657  			},
   658  		},
   659  		{
   660  			name: "Routes with fromExcluded only",
   661  			routes: []routes.Route{
   662  				{pathC1A1, pathC2A2},
   663  				{pathC2A2, pathC3A3},
   664  			},
   665  			fromLockedAmount: map[uint64]*hexutil.Big{4: &amount0},
   666  			expected: []routes.Route{
   667  				{pathC1A1, pathC2A2},
   668  				{pathC2A2, pathC3A3},
   669  			},
   670  		},
   671  		{
   672  			name: "Routes with all excluded chain IDs",
   673  			routes: []routes.Route{
   674  				{path0, pathC1A1},
   675  				{path0, pathC2A2},
   676  			},
   677  			fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2, 3: &amount3, 4: &amount0},
   678  			expected:         []routes.Route{},
   679  		},
   680  	}
   681  
   682  	for _, tt := range tests {
   683  		t.Run(tt.name, func(t *testing.T) {
   684  			t.Logf("Original Routes: %+v\n", tt.routes)
   685  			filteredRoutes := filterNetworkCompliance(tt.routes, tt.fromLockedAmount)
   686  			t.Logf("Filtered Routes: %+v\n", filteredRoutes)
   687  			assert.Equal(t, tt.expected, filteredRoutes)
   688  		})
   689  	}
   690  }
   691  
   692  func TestFilterCapacityValidation(t *testing.T) {
   693  	tests := []struct {
   694  		name             string
   695  		routes           []routes.Route
   696  		amountIn         *big.Int
   697  		fromLockedAmount map[uint64]*hexutil.Big
   698  		expectedRoutes   []routes.Route
   699  	}{
   700  		{
   701  			name: "Sufficient capacity with multiple paths",
   702  			routes: []routes.Route{
   703  				{
   704  					{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))},
   705  					{FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(100))},
   706  					{FromChain: network3, AmountIn: (*hexutil.Big)(big.NewInt(100))},
   707  				},
   708  				{
   709  					{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))},
   710  					{FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(200))},
   711  				},
   712  				{
   713  					{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))},
   714  					{FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(200))},
   715  				},
   716  			},
   717  			amountIn: big.NewInt(250),
   718  			fromLockedAmount: map[uint64]*hexutil.Big{
   719  				1: (*hexutil.Big)(big.NewInt(50)),
   720  			},
   721  			expectedRoutes: []routes.Route{
   722  				{
   723  					{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))},
   724  					{FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(100))},
   725  					{FromChain: network3, AmountIn: (*hexutil.Big)(big.NewInt(100))},
   726  				},
   727  				{
   728  					{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))},
   729  					{FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(200))},
   730  				},
   731  			},
   732  		},
   733  		{
   734  			name: "Insufficient capacity",
   735  			routes: []routes.Route{
   736  				{
   737  					{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))},
   738  					{FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(50))},
   739  				},
   740  			},
   741  			amountIn: big.NewInt(200),
   742  			fromLockedAmount: map[uint64]*hexutil.Big{
   743  				1: (*hexutil.Big)(big.NewInt(50)),
   744  				2: (*hexutil.Big)(big.NewInt(50)),
   745  			},
   746  			expectedRoutes: []routes.Route{},
   747  		},
   748  		{
   749  			name: "Exact capacity match",
   750  			routes: []routes.Route{
   751  				{
   752  					{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))},
   753  					{FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(50))},
   754  				},
   755  			},
   756  			amountIn: big.NewInt(150),
   757  			fromLockedAmount: map[uint64]*hexutil.Big{
   758  				1: (*hexutil.Big)(big.NewInt(100)),
   759  				2: (*hexutil.Big)(big.NewInt(50)),
   760  			},
   761  			expectedRoutes: []routes.Route{
   762  				{
   763  					{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))},
   764  					{FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(50))},
   765  				},
   766  			},
   767  		},
   768  		{
   769  			name: "No locked amounts",
   770  			routes: []routes.Route{
   771  				{
   772  					{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))},
   773  					{FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(50))},
   774  				},
   775  			},
   776  			amountIn:         big.NewInt(150),
   777  			fromLockedAmount: map[uint64]*hexutil.Big{},
   778  			expectedRoutes: []routes.Route{
   779  				{
   780  					{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))},
   781  					{FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(50))},
   782  				},
   783  			},
   784  		},
   785  		{
   786  			name: "Single route with sufficient capacity",
   787  			routes: []routes.Route{
   788  				{
   789  					{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))},
   790  					{FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(100))},
   791  				},
   792  			},
   793  			amountIn: big.NewInt(150),
   794  			fromLockedAmount: map[uint64]*hexutil.Big{
   795  				1: (*hexutil.Big)(big.NewInt(50)),
   796  			},
   797  			expectedRoutes: []routes.Route{
   798  				{
   799  					{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))},
   800  					{FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(100))},
   801  				},
   802  			},
   803  		},
   804  		{
   805  			name: "Single route with inappropriately locked amount",
   806  			routes: []routes.Route{
   807  				{
   808  					{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))},
   809  				},
   810  			},
   811  			amountIn: big.NewInt(150),
   812  			fromLockedAmount: map[uint64]*hexutil.Big{
   813  				1: (*hexutil.Big)(big.NewInt(50)),
   814  			},
   815  			expectedRoutes: []routes.Route{},
   816  		},
   817  		{
   818  			name: "Single route with insufficient capacity",
   819  			routes: []routes.Route{
   820  				{
   821  					{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))},
   822  				},
   823  			},
   824  			amountIn: big.NewInt(150),
   825  			fromLockedAmount: map[uint64]*hexutil.Big{
   826  				1: (*hexutil.Big)(big.NewInt(50)),
   827  			},
   828  			expectedRoutes: []routes.Route{},
   829  		},
   830  		{
   831  			name:     "Empty routes",
   832  			routes:   []routes.Route{},
   833  			amountIn: big.NewInt(150),
   834  			fromLockedAmount: map[uint64]*hexutil.Big{
   835  				1: (*hexutil.Big)(big.NewInt(50)),
   836  			},
   837  			expectedRoutes: []routes.Route{},
   838  		},
   839  		{
   840  			name: "Partial locked amounts",
   841  			routes: []routes.Route{
   842  				{
   843  					{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))},
   844  					{FromChain: network3, AmountIn: (*hexutil.Big)(big.NewInt(100))},
   845  					{FromChain: network4, AmountIn: (*hexutil.Big)(big.NewInt(100))},
   846  				},
   847  			},
   848  			amountIn: big.NewInt(250),
   849  			fromLockedAmount: map[uint64]*hexutil.Big{
   850  				1: (*hexutil.Big)(big.NewInt(50)),
   851  				2: (*hexutil.Big)(big.NewInt(0)), // Excluded path
   852  				3: (*hexutil.Big)(big.NewInt(100)),
   853  			},
   854  			expectedRoutes: []routes.Route{
   855  				{
   856  					{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))},
   857  					{FromChain: network3, AmountIn: (*hexutil.Big)(big.NewInt(100))},
   858  					{FromChain: network4, AmountIn: (*hexutil.Big)(big.NewInt(100))},
   859  				},
   860  			},
   861  		},
   862  		{
   863  			name: "Mixed networks with sufficient capacity",
   864  			routes: []routes.Route{
   865  				{
   866  					{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))},
   867  					{FromChain: network3, AmountIn: (*hexutil.Big)(big.NewInt(200))},
   868  				},
   869  			},
   870  			amountIn: big.NewInt(300),
   871  			fromLockedAmount: map[uint64]*hexutil.Big{
   872  				1: (*hexutil.Big)(big.NewInt(100)),
   873  				3: (*hexutil.Big)(big.NewInt(200)),
   874  			},
   875  			expectedRoutes: []routes.Route{
   876  				{
   877  					{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))},
   878  					{FromChain: network3, AmountIn: (*hexutil.Big)(big.NewInt(200))},
   879  				},
   880  			},
   881  		},
   882  		{
   883  			name: "Mixed networks with insufficient capacity",
   884  			routes: []routes.Route{
   885  				{
   886  					{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))},
   887  					{FromChain: network3, AmountIn: (*hexutil.Big)(big.NewInt(100))},
   888  				},
   889  			},
   890  			amountIn: big.NewInt(250),
   891  			fromLockedAmount: map[uint64]*hexutil.Big{
   892  				1: (*hexutil.Big)(big.NewInt(50)),
   893  				3: (*hexutil.Big)(big.NewInt(100)),
   894  			},
   895  			expectedRoutes: []routes.Route{},
   896  		},
   897  	}
   898  
   899  	for _, tt := range tests {
   900  		t.Run(tt.name, func(t *testing.T) {
   901  			filteredRoutes := filterCapacityValidation(tt.routes, tt.amountIn, tt.fromLockedAmount)
   902  			if !routesEqual(t, tt.expectedRoutes, filteredRoutes) {
   903  				t.Errorf("Expected: %+v, Actual: %+v", tt.expectedRoutes, filteredRoutes)
   904  			}
   905  		})
   906  	}
   907  }
   908  
   909  func TestFilterRoutes(t *testing.T) {
   910  	tests := []struct {
   911  		name             string
   912  		routes           []routes.Route
   913  		amountIn         *big.Int
   914  		fromLockedAmount map[uint64]*hexutil.Big
   915  		expectedRoutes   []routes.Route
   916  	}{
   917  		{
   918  			name: "Empty fromLockedAmount and routes don't match amountIn",
   919  			routes: []routes.Route{
   920  				{pathC1A1, pathC2A2},
   921  				{pathC3A3, pathC4A4},
   922  			},
   923  			amountIn:         big.NewInt(150),
   924  			fromLockedAmount: map[uint64]*hexutil.Big{},
   925  			expectedRoutes:   []routes.Route{},
   926  		},
   927  		{
   928  			name: "Empty fromLockedAmount and sigle route match amountIn",
   929  			routes: []routes.Route{
   930  				{pathC1A1, pathC2A2},
   931  				{pathC3A3, pathC4A4},
   932  			},
   933  			amountIn:         big.NewInt(300),
   934  			fromLockedAmount: map[uint64]*hexutil.Big{},
   935  			expectedRoutes: []routes.Route{
   936  				{pathC1A1, pathC2A2},
   937  			},
   938  		},
   939  		{
   940  			name: "Empty fromLockedAmount and more routes match amountIn",
   941  			routes: []routes.Route{
   942  				{pathC1A1, pathC2A2},
   943  				{pathC3A3, pathC4A4},
   944  				{pathC1A1, pathC2A1, pathC3A1},
   945  			},
   946  			amountIn:         big.NewInt(300),
   947  			fromLockedAmount: map[uint64]*hexutil.Big{},
   948  			expectedRoutes: []routes.Route{
   949  				{pathC1A1, pathC2A2},
   950  				{pathC1A1, pathC2A1, pathC3A1},
   951  			},
   952  		},
   953  		{
   954  			name: "All paths appear in fromLockedAmount but not within a single route",
   955  			routes: []routes.Route{
   956  				{pathC1A1, pathC3A3},
   957  				{pathC2A2, pathC4A4},
   958  			},
   959  			amountIn: big.NewInt(500),
   960  			fromLockedAmount: map[uint64]*hexutil.Big{
   961  				1: &amount1,
   962  				2: &amount2,
   963  				3: &amount3,
   964  				4: &amount4,
   965  			},
   966  			expectedRoutes: []routes.Route{},
   967  		},
   968  		{
   969  			name: "Mixed valid and invalid routes I",
   970  			routes: []routes.Route{
   971  				{pathC1A1, pathC2A2},
   972  				{pathC2A2, pathC3A3},
   973  				{pathC1A1, pathC4A4},
   974  				{pathC1A1, pathC2A1, pathC3A1},
   975  			},
   976  			amountIn: big.NewInt(300),
   977  			fromLockedAmount: map[uint64]*hexutil.Big{
   978  				1: &amount1,
   979  				2: &amount2,
   980  			},
   981  			expectedRoutes: []routes.Route{
   982  				{pathC1A1, pathC2A2},
   983  			},
   984  		},
   985  		{
   986  			name: "Mixed valid and invalid routes II",
   987  			routes: []routes.Route{
   988  				{pathC1A1, pathC2A2},
   989  				{pathC2A2, pathC3A3},
   990  				{pathC1A1, pathC4A4},
   991  				{pathC1A1, pathC2A1, pathC3A1},
   992  			},
   993  			amountIn: big.NewInt(300),
   994  			fromLockedAmount: map[uint64]*hexutil.Big{
   995  				1: &amount1,
   996  			},
   997  			expectedRoutes: []routes.Route{
   998  				{pathC1A1, pathC2A2},
   999  				{pathC1A1, pathC2A1, pathC3A1},
  1000  			},
  1001  		},
  1002  		{
  1003  			name: "All invalid routes",
  1004  			routes: []routes.Route{
  1005  				{pathC2A2, pathC3A3},
  1006  				{pathC4A4, pathC5A5},
  1007  			},
  1008  			amountIn: big.NewInt(300),
  1009  			fromLockedAmount: map[uint64]*hexutil.Big{
  1010  				1: &amount1,
  1011  			},
  1012  			expectedRoutes: []routes.Route{},
  1013  		},
  1014  		{
  1015  			name: "Single valid route",
  1016  			routes: []routes.Route{
  1017  				{pathC1A1, pathC3A3},
  1018  				{pathC2A2, pathC3A3},
  1019  			},
  1020  			amountIn: big.NewInt(400),
  1021  			fromLockedAmount: map[uint64]*hexutil.Big{
  1022  				1: &amount1,
  1023  				3: &amount3,
  1024  			},
  1025  			expectedRoutes: []routes.Route{
  1026  				{pathC1A1, pathC3A3},
  1027  			},
  1028  		},
  1029  		{
  1030  			name: "Route with mixed valid and invalid paths I",
  1031  			routes: []routes.Route{
  1032  				{pathC1A1, pathC2A2, pathC3A3},
  1033  			},
  1034  			amountIn: big.NewInt(300),
  1035  			fromLockedAmount: map[uint64]*hexutil.Big{
  1036  				1: &amount1,
  1037  				2: &amount0, // This path should be filtered out due to being excluded via a zero amount
  1038  			},
  1039  			expectedRoutes: []routes.Route{},
  1040  		},
  1041  		{
  1042  			name: "Route with mixed valid and invalid paths II",
  1043  			routes: []routes.Route{
  1044  				{pathC1A1, pathC3A3},
  1045  			},
  1046  			amountIn: big.NewInt(400),
  1047  			fromLockedAmount: map[uint64]*hexutil.Big{
  1048  				1: &amount1,
  1049  				2: &amount0, // This path should be filtered out due to being excluded via a zero amount, 0 value locked means this chain is disabled
  1050  			},
  1051  			expectedRoutes: []routes.Route{
  1052  				{pathC1A1, pathC3A3},
  1053  			},
  1054  		},
  1055  		{
  1056  			name: "Route with mixed valid and invalid paths III",
  1057  			routes: []routes.Route{
  1058  				{pathC1A1, pathC3A3},
  1059  				{pathC1A1, pathC3A2, pathC4A1},
  1060  			},
  1061  			amountIn: big.NewInt(400),
  1062  			fromLockedAmount: map[uint64]*hexutil.Big{
  1063  				1: &amount1,
  1064  				2: &amount0, // This path should be filtered out due to being excluded via a zero amount, 0 value locked means this chain is disabled
  1065  			},
  1066  			expectedRoutes: []routes.Route{
  1067  				{pathC1A1, pathC3A3},
  1068  				{pathC1A1, pathC3A2, pathC4A1},
  1069  			},
  1070  		},
  1071  	}
  1072  
  1073  	for _, tt := range tests {
  1074  		t.Run(tt.name, func(t *testing.T) {
  1075  			t.Logf("Original Routes: %+v\n", tt.routes)
  1076  			filteredRoutes := filterRoutes(tt.routes, tt.amountIn, tt.fromLockedAmount)
  1077  			t.Logf("Filtered Routes: %+v\n", filteredRoutes)
  1078  			assert.Equal(t, tt.expectedRoutes, filteredRoutes)
  1079  		})
  1080  	}
  1081  }