chore_: router response moved to wallet responses location

Following the approach we did for keeping requests at the same location, these changes introduce
new location for responses. `SuggestedRoutesResponse` is moved there and renamed to
`RouterSuggestedRoutes` and code is updated accordingly.

New type `Route` defined (since a single route is composed of zero or more paths).

Types `Route`, `Path`, `Graph` and `Node` belong to a new `routs` package now.
This commit is contained in:
Sale Djenic 2024-08-28 14:06:50 +02:00 committed by saledjenic
parent 00559692bc
commit 1bb9cbc573
10 changed files with 342 additions and 321 deletions

View File

@ -0,0 +1,15 @@
package responses
import (
"github.com/status-im/status-go/errors"
"github.com/status-im/status-go/services/wallet/router/routes"
)
type RouterSuggestedRoutes struct {
Uuid string `json:"Uuid"`
Best routes.Route `json:"Best,omitempty"`
Candidates routes.Route `json:"Candidates,omitempty"`
TokenPrice *float64 `json:"TokenPrice,omitempty"`
NativeChainTokenPrice *float64 `json:"NativeChainTokenPrice,omitempty"`
ErrorResponse *errors.ErrorResponse `json:"ErrorResponse,omitempty"`
}

View File

@ -2,9 +2,10 @@ package router
import ( import (
"github.com/status-im/status-go/services/wallet/common" "github.com/status-im/status-go/services/wallet/common"
"github.com/status-im/status-go/services/wallet/router/routes"
) )
func removeBestRouteFromAllRouters(allRoutes [][]*Path, best []*Path) [][]*Path { func removeBestRouteFromAllRouters(allRoutes []routes.Route, best routes.Route) []routes.Route {
for i := len(allRoutes) - 1; i >= 0; i-- { for i := len(allRoutes) - 1; i >= 0; i-- {
route := allRoutes[i] route := allRoutes[i]
routeFound := true routeFound := true
@ -45,7 +46,7 @@ func getChainPriority(chainID uint64) int {
} }
} }
func getRoutePriority(route []*Path) int { func getRoutePriority(route routes.Route) int {
priority := 0 priority := 0
for _, path := range route { for _, path := range route {
priority += getChainPriority(path.FromChain.ChainID) priority += getChainPriority(path.FromChain.ChainID)

View File

@ -6,6 +6,7 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/status-im/status-go/services/wallet/common" "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/router/pathprocessor"
"github.com/status-im/status-go/services/wallet/router/routes"
"go.uber.org/zap" "go.uber.org/zap"
) )
@ -20,7 +21,7 @@ func init() {
} }
} }
func filterRoutes(routes [][]*Path, amountIn *big.Int, fromLockedAmount map[uint64]*hexutil.Big) [][]*Path { func filterRoutes(routes []routes.Route, amountIn *big.Int, fromLockedAmount map[uint64]*hexutil.Big) []routes.Route {
for i := len(routes) - 1; i >= 0; i-- { for i := len(routes) - 1; i >= 0; i-- {
routeAmount := big.NewInt(0) routeAmount := big.NewInt(0)
for _, p := range routes[i] { for _, p := range routes[i] {
@ -43,15 +44,15 @@ func filterRoutes(routes [][]*Path, amountIn *big.Int, fromLockedAmount map[uint
} }
// filterNetworkCompliance performs the first level of filtering based on network inclusion/exclusion criteria. // filterNetworkCompliance performs the first level of filtering based on network inclusion/exclusion criteria.
func filterNetworkCompliance(routes [][]*Path, fromLockedAmount map[uint64]*hexutil.Big) [][]*Path { func filterNetworkCompliance(allRoutes []routes.Route, fromLockedAmount map[uint64]*hexutil.Big) []routes.Route {
filteredRoutes := make([][]*Path, 0) filteredRoutes := make([]routes.Route, 0)
if routes == nil || fromLockedAmount == nil { if allRoutes == nil || fromLockedAmount == nil {
return filteredRoutes return filteredRoutes
} }
fromIncluded, fromExcluded := setupRouteValidationMaps(fromLockedAmount) fromIncluded, fromExcluded := setupRouteValidationMaps(fromLockedAmount)
for _, route := range routes { for _, route := range allRoutes {
if route == nil { if route == nil {
continue continue
} }
@ -65,7 +66,7 @@ func filterNetworkCompliance(routes [][]*Path, fromLockedAmount map[uint64]*hexu
} }
// isValidForNetworkCompliance checks if a route complies with network inclusion/exclusion criteria. // isValidForNetworkCompliance checks if a route complies with network inclusion/exclusion criteria.
func isValidForNetworkCompliance(route []*Path, fromIncluded, fromExcluded map[uint64]bool) bool { func isValidForNetworkCompliance(route routes.Route, fromIncluded, fromExcluded map[uint64]bool) bool {
logger.Debug("Initial inclusion/exclusion maps", logger.Debug("Initial inclusion/exclusion maps",
zap.Any("fromIncluded", fromIncluded), zap.Any("fromIncluded", fromIncluded),
zap.Any("fromExcluded", fromExcluded), zap.Any("fromExcluded", fromExcluded),
@ -117,10 +118,10 @@ func setupRouteValidationMaps(fromLockedAmount map[uint64]*hexutil.Big) (map[uin
} }
// filterCapacityValidation performs the second level of filtering based on amount and capacity validation. // filterCapacityValidation performs the second level of filtering based on amount and capacity validation.
func filterCapacityValidation(routes [][]*Path, amountIn *big.Int, fromLockedAmount map[uint64]*hexutil.Big) [][]*Path { func filterCapacityValidation(allRoutes []routes.Route, amountIn *big.Int, fromLockedAmount map[uint64]*hexutil.Big) []routes.Route {
filteredRoutes := make([][]*Path, 0) filteredRoutes := make([]routes.Route, 0)
for _, route := range routes { for _, route := range allRoutes {
if hasSufficientCapacity(route, amountIn, fromLockedAmount) { if hasSufficientCapacity(route, amountIn, fromLockedAmount) {
filteredRoutes = append(filteredRoutes, route) filteredRoutes = append(filteredRoutes, route)
} }
@ -129,7 +130,7 @@ func filterCapacityValidation(routes [][]*Path, amountIn *big.Int, fromLockedAmo
} }
// hasSufficientCapacity checks if a route has sufficient capacity to handle the required amount. // hasSufficientCapacity checks if a route has sufficient capacity to handle the required amount.
func hasSufficientCapacity(route []*Path, amountIn *big.Int, fromLockedAmount map[uint64]*hexutil.Big) bool { func hasSufficientCapacity(route routes.Route, amountIn *big.Int, fromLockedAmount map[uint64]*hexutil.Big) bool {
for _, path := range route { for _, path := range route {
if amount, ok := fromLockedAmount[path.FromChain.ChainID]; ok { if amount, ok := fromLockedAmount[path.FromChain.ChainID]; ok {
if path.AmountIn.ToInt().Cmp(amount.ToInt()) != 0 { if path.AmountIn.ToInt().Cmp(amount.ToInt()) != 0 {
@ -153,7 +154,7 @@ func hasSufficientCapacity(route []*Path, amountIn *big.Int, fromLockedAmount ma
} }
// calculateRestAmountIn calculates the remaining amount in for the route excluding the specified path // calculateRestAmountIn calculates the remaining amount in for the route excluding the specified path
func calculateRestAmountIn(route []*Path, excludePath *Path) *big.Int { func calculateRestAmountIn(route routes.Route, excludePath *routes.Path) *big.Int {
restAmountIn := big.NewInt(0) restAmountIn := big.NewInt(0)
for _, path := range route { for _, path := range route {
if path != excludePath { if path != excludePath {

View File

@ -8,6 +8,7 @@ import (
"github.com/status-im/status-go/params" "github.com/status-im/status-go/params"
"github.com/status-im/status-go/services/wallet/router/pathprocessor" "github.com/status-im/status-go/services/wallet/router/pathprocessor"
"github.com/status-im/status-go/services/wallet/router/routes"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -26,24 +27,24 @@ var (
amount4 = hexutil.Big(*big.NewInt(400)) amount4 = hexutil.Big(*big.NewInt(400))
amount5 = hexutil.Big(*big.NewInt(500)) amount5 = hexutil.Big(*big.NewInt(500))
path0 = &Path{FromChain: network4, AmountIn: &amount0} path0 = &routes.Path{FromChain: network4, AmountIn: &amount0}
pathC1A1 = &Path{FromChain: network1, AmountIn: &amount1} pathC1A1 = &routes.Path{FromChain: network1, AmountIn: &amount1}
pathC2A1 = &Path{FromChain: network2, AmountIn: &amount1} pathC2A1 = &routes.Path{FromChain: network2, AmountIn: &amount1}
pathC2A2 = &Path{FromChain: network2, AmountIn: &amount2} pathC2A2 = &routes.Path{FromChain: network2, AmountIn: &amount2}
pathC3A1 = &Path{FromChain: network3, AmountIn: &amount1} pathC3A1 = &routes.Path{FromChain: network3, AmountIn: &amount1}
pathC3A2 = &Path{FromChain: network3, AmountIn: &amount2} pathC3A2 = &routes.Path{FromChain: network3, AmountIn: &amount2}
pathC3A3 = &Path{FromChain: network3, AmountIn: &amount3} pathC3A3 = &routes.Path{FromChain: network3, AmountIn: &amount3}
pathC4A1 = &Path{FromChain: network4, AmountIn: &amount1} pathC4A1 = &routes.Path{FromChain: network4, AmountIn: &amount1}
pathC4A4 = &Path{FromChain: network4, AmountIn: &amount4} pathC4A4 = &routes.Path{FromChain: network4, AmountIn: &amount4}
pathC5A5 = &Path{FromChain: network5, AmountIn: &amount5} pathC5A5 = &routes.Path{FromChain: network5, AmountIn: &amount5}
) )
func routesEqual(t *testing.T, expected, actual [][]*Path) bool { func routesEqual(t *testing.T, expected, actual []routes.Route) bool {
if len(expected) != len(actual) { if len(expected) != len(actual) {
return false return false
} }
@ -55,7 +56,7 @@ func routesEqual(t *testing.T, expected, actual [][]*Path) bool {
return true return true
} }
func pathsEqual(t *testing.T, expected, actual []*Path) bool { func pathsEqual(t *testing.T, expected, actual routes.Route) bool {
if len(expected) != len(actual) { if len(expected) != len(actual) {
return false return false
} }
@ -67,7 +68,7 @@ func pathsEqual(t *testing.T, expected, actual []*Path) bool {
return true return true
} }
func pathEqual(t *testing.T, expected, actual *Path) bool { func pathEqual(t *testing.T, expected, actual *routes.Path) bool {
if expected.FromChain.ChainID != actual.FromChain.ChainID { if expected.FromChain.ChainID != actual.FromChain.ChainID {
t.Logf("expected chain ID '%d' , actual chain ID '%d'", expected.FromChain.ChainID, actual.FromChain.ChainID) t.Logf("expected chain ID '%d' , actual chain ID '%d'", expected.FromChain.ChainID, actual.FromChain.ChainID)
return false return false
@ -171,43 +172,43 @@ func TestSetupRouteValidationMaps(t *testing.T) {
func TestCalculateRestAmountIn(t *testing.T) { func TestCalculateRestAmountIn(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
route []*Path route routes.Route
excludePath *Path excludePath *routes.Path
expected *big.Int expected *big.Int
}{ }{
{ {
name: "Exclude pathC1A1", name: "Exclude pathC1A1",
route: []*Path{pathC1A1, pathC2A2, pathC3A3}, route: routes.Route{pathC1A1, pathC2A2, pathC3A3},
excludePath: pathC1A1, excludePath: pathC1A1,
expected: big.NewInt(500), // 200 + 300 expected: big.NewInt(500), // 200 + 300
}, },
{ {
name: "Exclude pathC2A2", name: "Exclude pathC2A2",
route: []*Path{pathC1A1, pathC2A2, pathC3A3}, route: routes.Route{pathC1A1, pathC2A2, pathC3A3},
excludePath: pathC2A2, excludePath: pathC2A2,
expected: big.NewInt(400), // 100 + 300 expected: big.NewInt(400), // 100 + 300
}, },
{ {
name: "Exclude pathC3A3", name: "Exclude pathC3A3",
route: []*Path{pathC1A1, pathC2A2, pathC3A3}, route: routes.Route{pathC1A1, pathC2A2, pathC3A3},
excludePath: pathC3A3, excludePath: pathC3A3,
expected: big.NewInt(300), // 100 + 200 expected: big.NewInt(300), // 100 + 200
}, },
{ {
name: "Single path, exclude that path", name: "Single path, exclude that path",
route: []*Path{pathC1A1}, route: routes.Route{pathC1A1},
excludePath: pathC1A1, excludePath: pathC1A1,
expected: big.NewInt(0), // No other paths expected: big.NewInt(0), // No other paths
}, },
{ {
name: "Empty route", name: "Empty route",
route: []*Path{}, route: routes.Route{},
excludePath: pathC1A1, excludePath: pathC1A1,
expected: big.NewInt(0), // No paths expected: big.NewInt(0), // No paths
}, },
{ {
name: "Empty route, with nil exclude", name: "Empty route, with nil exclude",
route: []*Path{}, route: routes.Route{},
excludePath: nil, excludePath: nil,
expected: big.NewInt(0), // No paths expected: big.NewInt(0), // No paths
}, },
@ -224,56 +225,56 @@ func TestCalculateRestAmountIn(t *testing.T) {
func TestIsValidForNetworkCompliance(t *testing.T) { func TestIsValidForNetworkCompliance(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
route []*Path route routes.Route
fromIncluded map[uint64]bool fromIncluded map[uint64]bool
fromExcluded map[uint64]bool fromExcluded map[uint64]bool
expectedResult bool expectedResult bool
}{ }{
{ {
name: "Route with all included chain IDs", name: "Route with all included chain IDs",
route: []*Path{pathC1A1, pathC2A2}, route: routes.Route{pathC1A1, pathC2A2},
fromIncluded: map[uint64]bool{1: true, 2: true}, fromIncluded: map[uint64]bool{1: true, 2: true},
fromExcluded: map[uint64]bool{}, fromExcluded: map[uint64]bool{},
expectedResult: true, expectedResult: true,
}, },
{ {
name: "Route with fromExcluded only", name: "Route with fromExcluded only",
route: []*Path{pathC1A1, pathC2A2}, route: routes.Route{pathC1A1, pathC2A2},
fromIncluded: map[uint64]bool{}, fromIncluded: map[uint64]bool{},
fromExcluded: map[uint64]bool{3: false, 4: false}, fromExcluded: map[uint64]bool{3: false, 4: false},
expectedResult: true, expectedResult: true,
}, },
{ {
name: "Route without excluded chain IDs", name: "Route without excluded chain IDs",
route: []*Path{pathC1A1, pathC2A2}, route: routes.Route{pathC1A1, pathC2A2},
fromIncluded: map[uint64]bool{1: false, 2: false}, fromIncluded: map[uint64]bool{1: false, 2: false},
fromExcluded: map[uint64]bool{3: false, 4: false}, fromExcluded: map[uint64]bool{3: false, 4: false},
expectedResult: true, expectedResult: true,
}, },
{ {
name: "Route with an excluded chain ID", name: "Route with an excluded chain ID",
route: []*Path{pathC1A1, pathC3A3}, route: routes.Route{pathC1A1, pathC3A3},
fromIncluded: map[uint64]bool{1: false, 2: false}, fromIncluded: map[uint64]bool{1: false, 2: false},
fromExcluded: map[uint64]bool{3: false, 4: false}, fromExcluded: map[uint64]bool{3: false, 4: false},
expectedResult: false, expectedResult: false,
}, },
{ {
name: "Route missing one included chain ID", name: "Route missing one included chain ID",
route: []*Path{pathC1A1}, route: routes.Route{pathC1A1},
fromIncluded: map[uint64]bool{1: false, 2: false}, fromIncluded: map[uint64]bool{1: false, 2: false},
fromExcluded: map[uint64]bool{}, fromExcluded: map[uint64]bool{},
expectedResult: false, expectedResult: false,
}, },
{ {
name: "Route with no fromIncluded or fromExcluded", name: "Route with no fromIncluded or fromExcluded",
route: []*Path{pathC1A1, pathC2A2}, route: routes.Route{pathC1A1, pathC2A2},
fromIncluded: map[uint64]bool{}, fromIncluded: map[uint64]bool{},
fromExcluded: map[uint64]bool{}, fromExcluded: map[uint64]bool{},
expectedResult: true, expectedResult: true,
}, },
{ {
name: "Empty route", name: "Empty route",
route: []*Path{}, route: routes.Route{},
fromIncluded: map[uint64]bool{1: false, 2: false}, fromIncluded: map[uint64]bool{1: false, 2: false},
fromExcluded: map[uint64]bool{3: false, 4: false}, fromExcluded: map[uint64]bool{3: false, 4: false},
expectedResult: false, expectedResult: false,
@ -291,14 +292,14 @@ func TestIsValidForNetworkCompliance(t *testing.T) {
func TestHasSufficientCapacity(t *testing.T) { func TestHasSufficientCapacity(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
route []*Path route routes.Route
amountIn *big.Int amountIn *big.Int
fromLockedAmount map[uint64]*hexutil.Big fromLockedAmount map[uint64]*hexutil.Big
expected bool expected bool
}{ }{
{ {
name: "All paths meet required amount", name: "All paths meet required amount",
route: []*Path{pathC1A1, pathC2A2, pathC3A3}, route: routes.Route{pathC1A1, pathC2A2, pathC3A3},
amountIn: big.NewInt(600), amountIn: big.NewInt(600),
fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2, 3: &amount3}, fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2, 3: &amount3},
expected: true, expected: true,
@ -308,7 +309,7 @@ func TestHasSufficientCapacity(t *testing.T) {
/* /*
{ {
name: "A path does not meet required amount", name: "A path does not meet required amount",
route: []*Path{pathC1A1, pathC2A2, pathC3A3}, route: routes.Route{pathC1A1, pathC2A2, pathC3A3},
amountIn: big.NewInt(600), amountIn: big.NewInt(600),
fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2, 4: &amount4}, fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2, 4: &amount4},
expected: false, expected: false,
@ -316,42 +317,42 @@ func TestHasSufficientCapacity(t *testing.T) {
*/ */
{ {
name: "No fromLockedAmount", name: "No fromLockedAmount",
route: []*Path{pathC1A1, pathC2A2, pathC3A3}, route: routes.Route{pathC1A1, pathC2A2, pathC3A3},
amountIn: big.NewInt(600), amountIn: big.NewInt(600),
fromLockedAmount: map[uint64]*hexutil.Big{}, fromLockedAmount: map[uint64]*hexutil.Big{},
expected: true, expected: true,
}, },
{ {
name: "Single path meets required amount", name: "Single path meets required amount",
route: []*Path{pathC1A1}, route: routes.Route{pathC1A1},
amountIn: big.NewInt(100), amountIn: big.NewInt(100),
fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1}, fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1},
expected: true, expected: true,
}, },
{ {
name: "Single path does not meet required amount", name: "Single path does not meet required amount",
route: []*Path{pathC1A1}, route: routes.Route{pathC1A1},
amountIn: big.NewInt(200), amountIn: big.NewInt(200),
fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1}, fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1},
expected: false, expected: false,
}, },
{ {
name: "Path meets required amount with excess", name: "Path meets required amount with excess",
route: []*Path{pathC1A1, pathC2A2}, route: routes.Route{pathC1A1, pathC2A2},
amountIn: big.NewInt(250), amountIn: big.NewInt(250),
fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2}, fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2},
expected: true, expected: true,
}, },
{ {
name: "Path does not meet required amount due to insufficient rest", name: "Path does not meet required amount due to insufficient rest",
route: []*Path{pathC1A1, pathC2A2, pathC4A4}, route: routes.Route{pathC1A1, pathC2A2, pathC4A4},
amountIn: big.NewInt(800), amountIn: big.NewInt(800),
fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 4: &amount4}, fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 4: &amount4},
expected: false, expected: false,
}, },
{ {
name: "Empty route", name: "Empty route",
route: []*Path{}, route: routes.Route{},
amountIn: big.NewInt(500), amountIn: big.NewInt(500),
fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2}, fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2},
expected: true, expected: true,
@ -369,13 +370,13 @@ func TestHasSufficientCapacity(t *testing.T) {
func TestFilterNetworkCompliance(t *testing.T) { func TestFilterNetworkCompliance(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
routes [][]*Path routes []routes.Route
fromLockedAmount map[uint64]*hexutil.Big fromLockedAmount map[uint64]*hexutil.Big
expected [][]*Path expected []routes.Route
}{ }{
{ {
name: "Mixed routes with valid and invalid paths", name: "Mixed routes with valid and invalid paths",
routes: [][]*Path{ routes: []routes.Route{
{ {
{FromChain: network1}, {FromChain: network1},
{FromChain: network3}, {FromChain: network3},
@ -394,7 +395,7 @@ func TestFilterNetworkCompliance(t *testing.T) {
1: (*hexutil.Big)(big.NewInt(100)), 1: (*hexutil.Big)(big.NewInt(100)),
2: (*hexutil.Big)(big.NewInt(0)), 2: (*hexutil.Big)(big.NewInt(0)),
}, },
expected: [][]*Path{ expected: []routes.Route{
{ {
{FromChain: network1}, {FromChain: network1},
{FromChain: network3}, {FromChain: network3},
@ -403,7 +404,7 @@ func TestFilterNetworkCompliance(t *testing.T) {
}, },
{ {
name: "All valid routes", name: "All valid routes",
routes: [][]*Path{ routes: []routes.Route{
{ {
{FromChain: network1}, {FromChain: network1},
{FromChain: network3}, {FromChain: network3},
@ -416,7 +417,7 @@ func TestFilterNetworkCompliance(t *testing.T) {
fromLockedAmount: map[uint64]*hexutil.Big{ fromLockedAmount: map[uint64]*hexutil.Big{
1: (*hexutil.Big)(big.NewInt(100)), 1: (*hexutil.Big)(big.NewInt(100)),
}, },
expected: [][]*Path{ expected: []routes.Route{
{ {
{FromChain: network1}, {FromChain: network1},
{FromChain: network3}, {FromChain: network3},
@ -429,7 +430,7 @@ func TestFilterNetworkCompliance(t *testing.T) {
}, },
{ {
name: "All invalid routes", name: "All invalid routes",
routes: [][]*Path{ routes: []routes.Route{
{ {
{FromChain: network2}, {FromChain: network2},
{FromChain: network3}, {FromChain: network3},
@ -443,19 +444,19 @@ func TestFilterNetworkCompliance(t *testing.T) {
1: (*hexutil.Big)(big.NewInt(100)), 1: (*hexutil.Big)(big.NewInt(100)),
2: (*hexutil.Big)(big.NewInt(0)), 2: (*hexutil.Big)(big.NewInt(0)),
}, },
expected: [][]*Path{}, expected: []routes.Route{},
}, },
{ {
name: "Empty routes", name: "Empty routes",
routes: [][]*Path{}, routes: []routes.Route{},
fromLockedAmount: map[uint64]*hexutil.Big{ fromLockedAmount: map[uint64]*hexutil.Big{
1: (*hexutil.Big)(big.NewInt(100)), 1: (*hexutil.Big)(big.NewInt(100)),
}, },
expected: [][]*Path{}, expected: []routes.Route{},
}, },
{ {
name: "No locked amounts", name: "No locked amounts",
routes: [][]*Path{ routes: []routes.Route{
{ {
{FromChain: network1}, {FromChain: network1},
{FromChain: network2}, {FromChain: network2},
@ -466,7 +467,7 @@ func TestFilterNetworkCompliance(t *testing.T) {
}, },
}, },
fromLockedAmount: map[uint64]*hexutil.Big{}, fromLockedAmount: map[uint64]*hexutil.Big{},
expected: [][]*Path{ expected: []routes.Route{
{ {
{FromChain: network1}, {FromChain: network1},
{FromChain: network2}, {FromChain: network2},
@ -479,7 +480,7 @@ func TestFilterNetworkCompliance(t *testing.T) {
}, },
{ {
name: "Single route with mixed valid and invalid paths", name: "Single route with mixed valid and invalid paths",
routes: [][]*Path{ routes: []routes.Route{
{ {
{FromChain: network1}, {FromChain: network1},
{FromChain: network2}, {FromChain: network2},
@ -490,11 +491,11 @@ func TestFilterNetworkCompliance(t *testing.T) {
1: (*hexutil.Big)(big.NewInt(100)), 1: (*hexutil.Big)(big.NewInt(100)),
2: (*hexutil.Big)(big.NewInt(0)), 2: (*hexutil.Big)(big.NewInt(0)),
}, },
expected: [][]*Path{}, expected: []routes.Route{},
}, },
{ {
name: "Routes with duplicate chain IDs", name: "Routes with duplicate chain IDs",
routes: [][]*Path{ routes: []routes.Route{
{ {
{FromChain: network1}, {FromChain: network1},
{FromChain: network1}, {FromChain: network1},
@ -504,7 +505,7 @@ func TestFilterNetworkCompliance(t *testing.T) {
fromLockedAmount: map[uint64]*hexutil.Big{ fromLockedAmount: map[uint64]*hexutil.Big{
1: (*hexutil.Big)(big.NewInt(100)), 1: (*hexutil.Big)(big.NewInt(100)),
}, },
expected: [][]*Path{ expected: []routes.Route{
{ {
{FromChain: network1}, {FromChain: network1},
{FromChain: network1}, {FromChain: network1},
@ -514,7 +515,7 @@ func TestFilterNetworkCompliance(t *testing.T) {
}, },
{ {
name: "Minimum and maximum chain IDs", name: "Minimum and maximum chain IDs",
routes: [][]*Path{ routes: []routes.Route{
{ {
{FromChain: &params.Network{ChainID: 0}}, {FromChain: &params.Network{ChainID: 0}},
{FromChain: &params.Network{ChainID: ^uint64(0)}}, {FromChain: &params.Network{ChainID: ^uint64(0)}},
@ -524,7 +525,7 @@ func TestFilterNetworkCompliance(t *testing.T) {
0: (*hexutil.Big)(big.NewInt(100)), 0: (*hexutil.Big)(big.NewInt(100)),
^uint64(0): (*hexutil.Big)(big.NewInt(100)), ^uint64(0): (*hexutil.Big)(big.NewInt(100)),
}, },
expected: [][]*Path{ expected: []routes.Route{
{ {
{FromChain: &params.Network{ChainID: 0}}, {FromChain: &params.Network{ChainID: 0}},
{FromChain: &params.Network{ChainID: ^uint64(0)}}, {FromChain: &params.Network{ChainID: ^uint64(0)}},
@ -533,34 +534,34 @@ func TestFilterNetworkCompliance(t *testing.T) {
}, },
{ {
name: "Large number of routes", name: "Large number of routes",
routes: func() [][]*Path { routes: func() []routes.Route {
var routes [][]*Path var routes1 []routes.Route
for i := 0; i < 1000; i++ { for i := 0; i < 1000; i++ {
routes = append(routes, []*Path{ routes1 = append(routes1, routes.Route{
{FromChain: &params.Network{ChainID: uint64(i + 1)}}, {FromChain: &params.Network{ChainID: uint64(i + 1)}},
{FromChain: &params.Network{ChainID: uint64(i + 1001)}}, {FromChain: &params.Network{ChainID: uint64(i + 1001)}},
}) })
} }
return routes return routes1
}(), }(),
fromLockedAmount: map[uint64]*hexutil.Big{ fromLockedAmount: map[uint64]*hexutil.Big{
1: (*hexutil.Big)(big.NewInt(100)), 1: (*hexutil.Big)(big.NewInt(100)),
1001: (*hexutil.Big)(big.NewInt(100)), 1001: (*hexutil.Big)(big.NewInt(100)),
}, },
expected: func() [][]*Path { expected: func() []routes.Route {
var routes [][]*Path var routes1 []routes.Route
for i := 0; i < 1; i++ { for i := 0; i < 1; i++ {
routes = append(routes, []*Path{ routes1 = append(routes1, routes.Route{
{FromChain: &params.Network{ChainID: uint64(i + 1)}}, {FromChain: &params.Network{ChainID: uint64(i + 1)}},
{FromChain: &params.Network{ChainID: uint64(i + 1001)}}, {FromChain: &params.Network{ChainID: uint64(i + 1001)}},
}) })
} }
return routes return routes1
}(), }(),
}, },
{ {
name: "Routes with missing data", name: "Routes with missing data",
routes: [][]*Path{ routes: []routes.Route{
{ {
{FromChain: nil}, {FromChain: nil},
{FromChain: network2}, {FromChain: network2},
@ -574,11 +575,11 @@ func TestFilterNetworkCompliance(t *testing.T) {
1: (*hexutil.Big)(big.NewInt(100)), 1: (*hexutil.Big)(big.NewInt(100)),
2: (*hexutil.Big)(big.NewInt(0)), 2: (*hexutil.Big)(big.NewInt(0)),
}, },
expected: [][]*Path{}, expected: []routes.Route{},
}, },
{ {
name: "Consistency check", name: "Consistency check",
routes: [][]*Path{ routes: []routes.Route{
{ {
{FromChain: network1}, {FromChain: network1},
{FromChain: network2}, {FromChain: network2},
@ -591,7 +592,7 @@ func TestFilterNetworkCompliance(t *testing.T) {
fromLockedAmount: map[uint64]*hexutil.Big{ fromLockedAmount: map[uint64]*hexutil.Big{
1: (*hexutil.Big)(big.NewInt(100)), 1: (*hexutil.Big)(big.NewInt(100)),
}, },
expected: [][]*Path{ expected: []routes.Route{
{ {
{FromChain: network1}, {FromChain: network1},
{FromChain: network2}, {FromChain: network2},
@ -604,77 +605,77 @@ func TestFilterNetworkCompliance(t *testing.T) {
}, },
{ {
name: "Routes without excluded chain IDs, missing included path", name: "Routes without excluded chain IDs, missing included path",
routes: [][]*Path{ routes: []routes.Route{
{pathC1A1, pathC2A2}, {pathC1A1, pathC2A2},
{pathC2A2, pathC3A3}, {pathC2A2, pathC3A3},
}, },
fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2}, fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2},
expected: [][]*Path{ expected: []routes.Route{
{pathC1A1, pathC2A2}, {pathC1A1, pathC2A2},
}, },
}, },
{ {
name: "Routes with an excluded chain ID", name: "Routes with an excluded chain ID",
routes: [][]*Path{ routes: []routes.Route{
{pathC1A1, pathC2A2}, {pathC1A1, pathC2A2},
{pathC2A2, pathC3A3, path0}, {pathC2A2, pathC3A3, path0},
}, },
fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2, 4: &amount0}, fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2, 4: &amount0},
expected: [][]*Path{ expected: []routes.Route{
{pathC1A1, pathC2A2}, {pathC1A1, pathC2A2},
}, },
}, },
{ {
name: "Routes with all included chain IDs", name: "Routes with all included chain IDs",
routes: [][]*Path{ routes: []routes.Route{
{pathC1A1, pathC2A2, pathC3A3}, {pathC1A1, pathC2A2, pathC3A3},
}, },
fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2, 3: &amount3}, fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2, 3: &amount3},
expected: [][]*Path{ expected: []routes.Route{
{pathC1A1, pathC2A2, pathC3A3}, {pathC1A1, pathC2A2, pathC3A3},
}, },
}, },
{ {
name: "Routes missing one included chain ID", name: "Routes missing one included chain ID",
routes: [][]*Path{ routes: []routes.Route{
{pathC1A1, pathC2A2}, {pathC1A1, pathC2A2},
{pathC1A1}, {pathC1A1},
}, },
fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2, 3: &amount3}, fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2, 3: &amount3},
expected: [][]*Path{}, expected: []routes.Route{},
}, },
{ {
name: "Routes with no fromLockedAmount", name: "Routes with no fromLockedAmount",
routes: [][]*Path{ routes: []routes.Route{
{pathC1A1, pathC2A2}, {pathC1A1, pathC2A2},
{pathC2A2, pathC3A3}, {pathC2A2, pathC3A3},
}, },
fromLockedAmount: map[uint64]*hexutil.Big{}, fromLockedAmount: map[uint64]*hexutil.Big{},
expected: [][]*Path{ expected: []routes.Route{
{pathC1A1, pathC2A2}, {pathC1A1, pathC2A2},
{pathC2A2, pathC3A3}, {pathC2A2, pathC3A3},
}, },
}, },
{ {
name: "Routes with fromExcluded only", name: "Routes with fromExcluded only",
routes: [][]*Path{ routes: []routes.Route{
{pathC1A1, pathC2A2}, {pathC1A1, pathC2A2},
{pathC2A2, pathC3A3}, {pathC2A2, pathC3A3},
}, },
fromLockedAmount: map[uint64]*hexutil.Big{4: &amount0}, fromLockedAmount: map[uint64]*hexutil.Big{4: &amount0},
expected: [][]*Path{ expected: []routes.Route{
{pathC1A1, pathC2A2}, {pathC1A1, pathC2A2},
{pathC2A2, pathC3A3}, {pathC2A2, pathC3A3},
}, },
}, },
{ {
name: "Routes with all excluded chain IDs", name: "Routes with all excluded chain IDs",
routes: [][]*Path{ routes: []routes.Route{
{path0, pathC1A1}, {path0, pathC1A1},
{path0, pathC2A2}, {path0, pathC2A2},
}, },
fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2, 3: &amount3, 4: &amount0}, fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2, 3: &amount3, 4: &amount0},
expected: [][]*Path{}, expected: []routes.Route{},
}, },
} }
@ -691,14 +692,14 @@ func TestFilterNetworkCompliance(t *testing.T) {
func TestFilterCapacityValidation(t *testing.T) { func TestFilterCapacityValidation(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
routes [][]*Path routes []routes.Route
amountIn *big.Int amountIn *big.Int
fromLockedAmount map[uint64]*hexutil.Big fromLockedAmount map[uint64]*hexutil.Big
expectedRoutes [][]*Path expectedRoutes []routes.Route
}{ }{
{ {
name: "Sufficient capacity with multiple paths", name: "Sufficient capacity with multiple paths",
routes: [][]*Path{ routes: []routes.Route{
{ {
{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))}, {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))},
{FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(100))}, {FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(100))},
@ -717,7 +718,7 @@ func TestFilterCapacityValidation(t *testing.T) {
fromLockedAmount: map[uint64]*hexutil.Big{ fromLockedAmount: map[uint64]*hexutil.Big{
1: (*hexutil.Big)(big.NewInt(50)), 1: (*hexutil.Big)(big.NewInt(50)),
}, },
expectedRoutes: [][]*Path{ expectedRoutes: []routes.Route{
{ {
{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))}, {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))},
{FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(100))}, {FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(100))},
@ -731,7 +732,7 @@ func TestFilterCapacityValidation(t *testing.T) {
}, },
{ {
name: "Insufficient capacity", name: "Insufficient capacity",
routes: [][]*Path{ routes: []routes.Route{
{ {
{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))}, {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))},
{FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(50))}, {FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(50))},
@ -742,11 +743,11 @@ func TestFilterCapacityValidation(t *testing.T) {
1: (*hexutil.Big)(big.NewInt(50)), 1: (*hexutil.Big)(big.NewInt(50)),
2: (*hexutil.Big)(big.NewInt(50)), 2: (*hexutil.Big)(big.NewInt(50)),
}, },
expectedRoutes: [][]*Path{}, expectedRoutes: []routes.Route{},
}, },
{ {
name: "Exact capacity match", name: "Exact capacity match",
routes: [][]*Path{ routes: []routes.Route{
{ {
{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))}, {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))},
{FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(50))}, {FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(50))},
@ -757,7 +758,7 @@ func TestFilterCapacityValidation(t *testing.T) {
1: (*hexutil.Big)(big.NewInt(100)), 1: (*hexutil.Big)(big.NewInt(100)),
2: (*hexutil.Big)(big.NewInt(50)), 2: (*hexutil.Big)(big.NewInt(50)),
}, },
expectedRoutes: [][]*Path{ expectedRoutes: []routes.Route{
{ {
{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))}, {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))},
{FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(50))}, {FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(50))},
@ -766,7 +767,7 @@ func TestFilterCapacityValidation(t *testing.T) {
}, },
{ {
name: "No locked amounts", name: "No locked amounts",
routes: [][]*Path{ routes: []routes.Route{
{ {
{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))}, {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))},
{FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(50))}, {FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(50))},
@ -774,7 +775,7 @@ func TestFilterCapacityValidation(t *testing.T) {
}, },
amountIn: big.NewInt(150), amountIn: big.NewInt(150),
fromLockedAmount: map[uint64]*hexutil.Big{}, fromLockedAmount: map[uint64]*hexutil.Big{},
expectedRoutes: [][]*Path{ expectedRoutes: []routes.Route{
{ {
{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))}, {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))},
{FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(50))}, {FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(50))},
@ -783,7 +784,7 @@ func TestFilterCapacityValidation(t *testing.T) {
}, },
{ {
name: "Single route with sufficient capacity", name: "Single route with sufficient capacity",
routes: [][]*Path{ routes: []routes.Route{
{ {
{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))}, {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))},
{FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(100))}, {FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(100))},
@ -793,7 +794,7 @@ func TestFilterCapacityValidation(t *testing.T) {
fromLockedAmount: map[uint64]*hexutil.Big{ fromLockedAmount: map[uint64]*hexutil.Big{
1: (*hexutil.Big)(big.NewInt(50)), 1: (*hexutil.Big)(big.NewInt(50)),
}, },
expectedRoutes: [][]*Path{ expectedRoutes: []routes.Route{
{ {
{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))}, {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))},
{FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(100))}, {FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(100))},
@ -802,7 +803,7 @@ func TestFilterCapacityValidation(t *testing.T) {
}, },
{ {
name: "Single route with inappropriately locked amount", name: "Single route with inappropriately locked amount",
routes: [][]*Path{ routes: []routes.Route{
{ {
{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))}, {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))},
}, },
@ -811,11 +812,11 @@ func TestFilterCapacityValidation(t *testing.T) {
fromLockedAmount: map[uint64]*hexutil.Big{ fromLockedAmount: map[uint64]*hexutil.Big{
1: (*hexutil.Big)(big.NewInt(50)), 1: (*hexutil.Big)(big.NewInt(50)),
}, },
expectedRoutes: [][]*Path{}, expectedRoutes: []routes.Route{},
}, },
{ {
name: "Single route with insufficient capacity", name: "Single route with insufficient capacity",
routes: [][]*Path{ routes: []routes.Route{
{ {
{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))}, {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))},
}, },
@ -824,20 +825,20 @@ func TestFilterCapacityValidation(t *testing.T) {
fromLockedAmount: map[uint64]*hexutil.Big{ fromLockedAmount: map[uint64]*hexutil.Big{
1: (*hexutil.Big)(big.NewInt(50)), 1: (*hexutil.Big)(big.NewInt(50)),
}, },
expectedRoutes: [][]*Path{}, expectedRoutes: []routes.Route{},
}, },
{ {
name: "Empty routes", name: "Empty routes",
routes: [][]*Path{}, routes: []routes.Route{},
amountIn: big.NewInt(150), amountIn: big.NewInt(150),
fromLockedAmount: map[uint64]*hexutil.Big{ fromLockedAmount: map[uint64]*hexutil.Big{
1: (*hexutil.Big)(big.NewInt(50)), 1: (*hexutil.Big)(big.NewInt(50)),
}, },
expectedRoutes: [][]*Path{}, expectedRoutes: []routes.Route{},
}, },
{ {
name: "Partial locked amounts", name: "Partial locked amounts",
routes: [][]*Path{ routes: []routes.Route{
{ {
{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))}, {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))},
{FromChain: network3, AmountIn: (*hexutil.Big)(big.NewInt(100))}, {FromChain: network3, AmountIn: (*hexutil.Big)(big.NewInt(100))},
@ -850,7 +851,7 @@ func TestFilterCapacityValidation(t *testing.T) {
2: (*hexutil.Big)(big.NewInt(0)), // Excluded path 2: (*hexutil.Big)(big.NewInt(0)), // Excluded path
3: (*hexutil.Big)(big.NewInt(100)), 3: (*hexutil.Big)(big.NewInt(100)),
}, },
expectedRoutes: [][]*Path{ expectedRoutes: []routes.Route{
{ {
{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))}, {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))},
{FromChain: network3, AmountIn: (*hexutil.Big)(big.NewInt(100))}, {FromChain: network3, AmountIn: (*hexutil.Big)(big.NewInt(100))},
@ -860,7 +861,7 @@ func TestFilterCapacityValidation(t *testing.T) {
}, },
{ {
name: "Mixed networks with sufficient capacity", name: "Mixed networks with sufficient capacity",
routes: [][]*Path{ routes: []routes.Route{
{ {
{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))}, {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))},
{FromChain: network3, AmountIn: (*hexutil.Big)(big.NewInt(200))}, {FromChain: network3, AmountIn: (*hexutil.Big)(big.NewInt(200))},
@ -871,7 +872,7 @@ func TestFilterCapacityValidation(t *testing.T) {
1: (*hexutil.Big)(big.NewInt(100)), 1: (*hexutil.Big)(big.NewInt(100)),
3: (*hexutil.Big)(big.NewInt(200)), 3: (*hexutil.Big)(big.NewInt(200)),
}, },
expectedRoutes: [][]*Path{ expectedRoutes: []routes.Route{
{ {
{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))}, {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))},
{FromChain: network3, AmountIn: (*hexutil.Big)(big.NewInt(200))}, {FromChain: network3, AmountIn: (*hexutil.Big)(big.NewInt(200))},
@ -880,7 +881,7 @@ func TestFilterCapacityValidation(t *testing.T) {
}, },
{ {
name: "Mixed networks with insufficient capacity", name: "Mixed networks with insufficient capacity",
routes: [][]*Path{ routes: []routes.Route{
{ {
{FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))}, {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))},
{FromChain: network3, AmountIn: (*hexutil.Big)(big.NewInt(100))}, {FromChain: network3, AmountIn: (*hexutil.Big)(big.NewInt(100))},
@ -891,7 +892,7 @@ func TestFilterCapacityValidation(t *testing.T) {
1: (*hexutil.Big)(big.NewInt(50)), 1: (*hexutil.Big)(big.NewInt(50)),
3: (*hexutil.Big)(big.NewInt(100)), 3: (*hexutil.Big)(big.NewInt(100)),
}, },
expectedRoutes: [][]*Path{}, expectedRoutes: []routes.Route{},
}, },
} }
@ -908,50 +909,50 @@ func TestFilterCapacityValidation(t *testing.T) {
func TestFilterRoutes(t *testing.T) { func TestFilterRoutes(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
routes [][]*Path routes []routes.Route
amountIn *big.Int amountIn *big.Int
fromLockedAmount map[uint64]*hexutil.Big fromLockedAmount map[uint64]*hexutil.Big
expectedRoutes [][]*Path expectedRoutes []routes.Route
}{ }{
{ {
name: "Empty fromLockedAmount and routes don't match amountIn", name: "Empty fromLockedAmount and routes don't match amountIn",
routes: [][]*Path{ routes: []routes.Route{
{pathC1A1, pathC2A2}, {pathC1A1, pathC2A2},
{pathC3A3, pathC4A4}, {pathC3A3, pathC4A4},
}, },
amountIn: big.NewInt(150), amountIn: big.NewInt(150),
fromLockedAmount: map[uint64]*hexutil.Big{}, fromLockedAmount: map[uint64]*hexutil.Big{},
expectedRoutes: [][]*Path{}, expectedRoutes: []routes.Route{},
}, },
{ {
name: "Empty fromLockedAmount and sigle route match amountIn", name: "Empty fromLockedAmount and sigle route match amountIn",
routes: [][]*Path{ routes: []routes.Route{
{pathC1A1, pathC2A2}, {pathC1A1, pathC2A2},
{pathC3A3, pathC4A4}, {pathC3A3, pathC4A4},
}, },
amountIn: big.NewInt(300), amountIn: big.NewInt(300),
fromLockedAmount: map[uint64]*hexutil.Big{}, fromLockedAmount: map[uint64]*hexutil.Big{},
expectedRoutes: [][]*Path{ expectedRoutes: []routes.Route{
{pathC1A1, pathC2A2}, {pathC1A1, pathC2A2},
}, },
}, },
{ {
name: "Empty fromLockedAmount and more routes match amountIn", name: "Empty fromLockedAmount and more routes match amountIn",
routes: [][]*Path{ routes: []routes.Route{
{pathC1A1, pathC2A2}, {pathC1A1, pathC2A2},
{pathC3A3, pathC4A4}, {pathC3A3, pathC4A4},
{pathC1A1, pathC2A1, pathC3A1}, {pathC1A1, pathC2A1, pathC3A1},
}, },
amountIn: big.NewInt(300), amountIn: big.NewInt(300),
fromLockedAmount: map[uint64]*hexutil.Big{}, fromLockedAmount: map[uint64]*hexutil.Big{},
expectedRoutes: [][]*Path{ expectedRoutes: []routes.Route{
{pathC1A1, pathC2A2}, {pathC1A1, pathC2A2},
{pathC1A1, pathC2A1, pathC3A1}, {pathC1A1, pathC2A1, pathC3A1},
}, },
}, },
{ {
name: "All paths appear in fromLockedAmount but not within a single route", name: "All paths appear in fromLockedAmount but not within a single route",
routes: [][]*Path{ routes: []routes.Route{
{pathC1A1, pathC3A3}, {pathC1A1, pathC3A3},
{pathC2A2, pathC4A4}, {pathC2A2, pathC4A4},
}, },
@ -962,11 +963,11 @@ func TestFilterRoutes(t *testing.T) {
3: &amount3, 3: &amount3,
4: &amount4, 4: &amount4,
}, },
expectedRoutes: [][]*Path{}, expectedRoutes: []routes.Route{},
}, },
{ {
name: "Mixed valid and invalid routes I", name: "Mixed valid and invalid routes I",
routes: [][]*Path{ routes: []routes.Route{
{pathC1A1, pathC2A2}, {pathC1A1, pathC2A2},
{pathC2A2, pathC3A3}, {pathC2A2, pathC3A3},
{pathC1A1, pathC4A4}, {pathC1A1, pathC4A4},
@ -977,13 +978,13 @@ func TestFilterRoutes(t *testing.T) {
1: &amount1, 1: &amount1,
2: &amount2, 2: &amount2,
}, },
expectedRoutes: [][]*Path{ expectedRoutes: []routes.Route{
{pathC1A1, pathC2A2}, {pathC1A1, pathC2A2},
}, },
}, },
{ {
name: "Mixed valid and invalid routes II", name: "Mixed valid and invalid routes II",
routes: [][]*Path{ routes: []routes.Route{
{pathC1A1, pathC2A2}, {pathC1A1, pathC2A2},
{pathC2A2, pathC3A3}, {pathC2A2, pathC3A3},
{pathC1A1, pathC4A4}, {pathC1A1, pathC4A4},
@ -993,14 +994,14 @@ func TestFilterRoutes(t *testing.T) {
fromLockedAmount: map[uint64]*hexutil.Big{ fromLockedAmount: map[uint64]*hexutil.Big{
1: &amount1, 1: &amount1,
}, },
expectedRoutes: [][]*Path{ expectedRoutes: []routes.Route{
{pathC1A1, pathC2A2}, {pathC1A1, pathC2A2},
{pathC1A1, pathC2A1, pathC3A1}, {pathC1A1, pathC2A1, pathC3A1},
}, },
}, },
{ {
name: "All invalid routes", name: "All invalid routes",
routes: [][]*Path{ routes: []routes.Route{
{pathC2A2, pathC3A3}, {pathC2A2, pathC3A3},
{pathC4A4, pathC5A5}, {pathC4A4, pathC5A5},
}, },
@ -1008,11 +1009,11 @@ func TestFilterRoutes(t *testing.T) {
fromLockedAmount: map[uint64]*hexutil.Big{ fromLockedAmount: map[uint64]*hexutil.Big{
1: &amount1, 1: &amount1,
}, },
expectedRoutes: [][]*Path{}, expectedRoutes: []routes.Route{},
}, },
{ {
name: "Single valid route", name: "Single valid route",
routes: [][]*Path{ routes: []routes.Route{
{pathC1A1, pathC3A3}, {pathC1A1, pathC3A3},
{pathC2A2, pathC3A3}, {pathC2A2, pathC3A3},
}, },
@ -1021,13 +1022,13 @@ func TestFilterRoutes(t *testing.T) {
1: &amount1, 1: &amount1,
3: &amount3, 3: &amount3,
}, },
expectedRoutes: [][]*Path{ expectedRoutes: []routes.Route{
{pathC1A1, pathC3A3}, {pathC1A1, pathC3A3},
}, },
}, },
{ {
name: "Route with mixed valid and invalid paths I", name: "Route with mixed valid and invalid paths I",
routes: [][]*Path{ routes: []routes.Route{
{pathC1A1, pathC2A2, pathC3A3}, {pathC1A1, pathC2A2, pathC3A3},
}, },
amountIn: big.NewInt(300), amountIn: big.NewInt(300),
@ -1035,11 +1036,11 @@ func TestFilterRoutes(t *testing.T) {
1: &amount1, 1: &amount1,
2: &amount0, // This path should be filtered out due to being excluded via a zero amount 2: &amount0, // This path should be filtered out due to being excluded via a zero amount
}, },
expectedRoutes: [][]*Path{}, expectedRoutes: []routes.Route{},
}, },
{ {
name: "Route with mixed valid and invalid paths II", name: "Route with mixed valid and invalid paths II",
routes: [][]*Path{ routes: []routes.Route{
{pathC1A1, pathC3A3}, {pathC1A1, pathC3A3},
}, },
amountIn: big.NewInt(400), amountIn: big.NewInt(400),
@ -1047,13 +1048,13 @@ func TestFilterRoutes(t *testing.T) {
1: &amount1, 1: &amount1,
2: &amount0, // This path should be filtered out due to being excluded via a zero amount, 0 value locked means this chain is disabled 2: &amount0, // This path should be filtered out due to being excluded via a zero amount, 0 value locked means this chain is disabled
}, },
expectedRoutes: [][]*Path{ expectedRoutes: []routes.Route{
{pathC1A1, pathC3A3}, {pathC1A1, pathC3A3},
}, },
}, },
{ {
name: "Route with mixed valid and invalid paths III", name: "Route with mixed valid and invalid paths III",
routes: [][]*Path{ routes: []routes.Route{
{pathC1A1, pathC3A3}, {pathC1A1, pathC3A3},
{pathC1A1, pathC3A2, pathC4A1}, {pathC1A1, pathC3A2, pathC4A1},
}, },
@ -1062,7 +1063,7 @@ func TestFilterRoutes(t *testing.T) {
1: &amount1, 1: &amount1,
2: &amount0, // This path should be filtered out due to being excluded via a zero amount, 0 value locked means this chain is disabled 2: &amount0, // This path should be filtered out due to being excluded via a zero amount, 0 value locked means this chain is disabled
}, },
expectedRoutes: [][]*Path{ expectedRoutes: []routes.Route{
{pathC1A1, pathC3A3}, {pathC1A1, pathC3A3},
{pathC1A1, pathC3A2, pathC4A1}, {pathC1A1, pathC3A2, pathC4A1},
}, },

View File

@ -21,8 +21,10 @@ import (
walletCommon "github.com/status-im/status-go/services/wallet/common" walletCommon "github.com/status-im/status-go/services/wallet/common"
"github.com/status-im/status-go/services/wallet/market" "github.com/status-im/status-go/services/wallet/market"
"github.com/status-im/status-go/services/wallet/requests" "github.com/status-im/status-go/services/wallet/requests"
"github.com/status-im/status-go/services/wallet/responses"
"github.com/status-im/status-go/services/wallet/router/fees" "github.com/status-im/status-go/services/wallet/router/fees"
"github.com/status-im/status-go/services/wallet/router/pathprocessor" "github.com/status-im/status-go/services/wallet/router/pathprocessor"
"github.com/status-im/status-go/services/wallet/router/routes"
"github.com/status-im/status-go/services/wallet/router/sendtype" "github.com/status-im/status-go/services/wallet/router/sendtype"
"github.com/status-im/status-go/services/wallet/token" "github.com/status-im/status-go/services/wallet/token"
walletToken "github.com/status-im/status-go/services/wallet/token" walletToken "github.com/status-im/status-go/services/wallet/token"
@ -54,21 +56,12 @@ type ProcessorError struct {
type SuggestedRoutes struct { type SuggestedRoutes struct {
Uuid string Uuid string
Best []*Path Best routes.Route
Candidates []*Path Candidates routes.Route
TokenPrice float64 TokenPrice float64
NativeChainTokenPrice float64 NativeChainTokenPrice float64
} }
type SuggestedRoutesResponse struct {
Uuid string `json:"Uuid"`
Best []*Path `json:"Best,omitempty"`
Candidates []*Path `json:"Candidates,omitempty"`
TokenPrice *float64 `json:"TokenPrice,omitempty"`
NativeChainTokenPrice *float64 `json:"NativeChainTokenPrice,omitempty"`
ErrorResponse *errors.ErrorResponse `json:"ErrorResponse,omitempty"`
}
type Router struct { type Router struct {
rpcClient *rpc.Client rpcClient *rpc.Client
tokenManager *token.Manager tokenManager *token.Manager
@ -121,11 +114,11 @@ func (r *Router) GetPathProcessors() map[string]pathprocessor.PathProcessor {
func newSuggestedRoutes( func newSuggestedRoutes(
uuid string, uuid string,
amountIn *big.Int, amountIn *big.Int,
candidates []*Path, candidates routes.Route,
fromLockedAmount map[uint64]*hexutil.Big, fromLockedAmount map[uint64]*hexutil.Big,
tokenPrice float64, tokenPrice float64,
nativeChainTokenPrice float64, nativeChainTokenPrice float64,
) (*SuggestedRoutes, [][]*Path) { ) (*SuggestedRoutes, []routes.Route) {
suggestedRoutes := &SuggestedRoutes{ suggestedRoutes := &SuggestedRoutes{
Uuid: uuid, Uuid: uuid,
Candidates: candidates, Candidates: candidates,
@ -136,11 +129,11 @@ func newSuggestedRoutes(
return suggestedRoutes, nil return suggestedRoutes, nil
} }
node := &Node{ node := &routes.Node{
Path: nil, Path: nil,
Children: buildGraph(amountIn, candidates, 0, []uint64{}), Children: routes.BuildGraph(amountIn, candidates, 0, []uint64{}),
} }
allRoutes := node.buildAllRoutes() allRoutes := node.BuildAllRoutes()
allRoutes = filterRoutes(allRoutes, amountIn, fromLockedAmount) allRoutes = filterRoutes(allRoutes, amountIn, fromLockedAmount)
if len(allRoutes) > 0 { if len(allRoutes) > 0 {
@ -158,7 +151,7 @@ func (r *Router) SuggestedRoutesAsync(input *requests.RouteInputParams) {
r.scheduler.Enqueue(routerTask, func(ctx context.Context) (interface{}, error) { r.scheduler.Enqueue(routerTask, func(ctx context.Context) (interface{}, error) {
return r.SuggestedRoutes(ctx, input) return r.SuggestedRoutes(ctx, input)
}, func(result interface{}, taskType async.TaskType, err error) { }, func(result interface{}, taskType async.TaskType, err error) {
routesResponse := SuggestedRoutesResponse{ routesResponse := responses.RouterSuggestedRoutes{
Uuid: input.Uuid, Uuid: input.Uuid,
} }
@ -511,7 +504,7 @@ func (r *Router) getSelectedChains(input *requests.RouteInputParams) (selectedFr
} }
func (r *Router) resolveCandidates(ctx context.Context, input *requests.RouteInputParams, selectedFromChains []*params.Network, func (r *Router) resolveCandidates(ctx context.Context, input *requests.RouteInputParams, selectedFromChains []*params.Network,
selectedToChains []*params.Network, balanceMap map[string]*big.Int) (candidates []*Path, processorErrors []*ProcessorError, err error) { selectedToChains []*params.Network, balanceMap map[string]*big.Int) (candidates routes.Route, processorErrors []*ProcessorError, err error) {
var ( var (
testsMode = input.TestsMode && input.TestParams != nil testsMode = input.TestsMode && input.TestParams != nil
group = async.NewAtomicGroup(ctx) group = async.NewAtomicGroup(ctx)
@ -533,7 +526,7 @@ func (r *Router) resolveCandidates(ctx context.Context, input *requests.RouteInp
}) })
} }
appendPathFn := func(path *Path) { appendPathFn := func(path *routes.Path) {
mu.Lock() mu.Lock()
defer mu.Unlock() defer mu.Unlock()
candidates = append(candidates, path) candidates = append(candidates, path)
@ -731,7 +724,7 @@ func (r *Router) resolveCandidates(ctx context.Context, input *requests.RouteInp
requiredNativeBalance.Add(requiredNativeBalance, ethTotalFees) requiredNativeBalance.Add(requiredNativeBalance, ethTotalFees)
} }
appendPathFn(&Path{ appendPathFn(&routes.Path{
ProcessorName: pProcessor.Name(), ProcessorName: pProcessor.Name(),
FromChain: network, FromChain: network,
ToChain: dest, ToChain: dest,
@ -767,9 +760,9 @@ func (r *Router) resolveCandidates(ctx context.Context, input *requests.RouteInp
EstimatedTime: estimatedTime, EstimatedTime: estimatedTime,
subtractFees: amountOption.subtractFees, SubtractFees: amountOption.subtractFees,
requiredTokenBalance: requiredTokenBalance, RequiredTokenBalance: requiredTokenBalance,
requiredNativeBalance: requiredNativeBalance, RequiredNativeBalance: requiredNativeBalance,
}) })
} }
} }
@ -788,7 +781,7 @@ func (r *Router) resolveCandidates(ctx context.Context, input *requests.RouteInp
return candidates, processorErrors, nil return candidates, processorErrors, nil
} }
func (r *Router) checkBalancesForTheBestRoute(ctx context.Context, bestRoute []*Path, balanceMap map[string]*big.Int) (hasPositiveBalance bool, err error) { func (r *Router) checkBalancesForTheBestRoute(ctx context.Context, bestRoute routes.Route, balanceMap map[string]*big.Int) (hasPositiveBalance bool, err error) {
balanceMapCopy := walletCommon.CopyMapGeneric(balanceMap, func(v interface{}) interface{} { balanceMapCopy := walletCommon.CopyMapGeneric(balanceMap, func(v interface{}) interface{} {
return new(big.Int).Set(v.(*big.Int)) return new(big.Int).Set(v.(*big.Int))
}).(map[string]*big.Int) }).(map[string]*big.Int)
@ -811,16 +804,16 @@ func (r *Router) checkBalancesForTheBestRoute(ctx context.Context, bestRoute []*
} }
} }
if path.requiredTokenBalance != nil && path.requiredTokenBalance.Cmp(pathprocessor.ZeroBigIntValue) > 0 { if path.RequiredTokenBalance != nil && path.RequiredTokenBalance.Cmp(pathprocessor.ZeroBigIntValue) > 0 {
if tokenBalance, ok := balanceMapCopy[tokenKey]; ok { if tokenBalance, ok := balanceMapCopy[tokenKey]; ok {
if tokenBalance.Cmp(path.requiredTokenBalance) == -1 { if tokenBalance.Cmp(path.RequiredTokenBalance) == -1 {
err := &errors.ErrorResponse{ err := &errors.ErrorResponse{
Code: ErrNotEnoughTokenBalance.Code, Code: ErrNotEnoughTokenBalance.Code,
Details: fmt.Sprintf(ErrNotEnoughTokenBalance.Details, path.FromToken.Symbol, path.FromChain.ChainID), Details: fmt.Sprintf(ErrNotEnoughTokenBalance.Details, path.FromToken.Symbol, path.FromChain.ChainID),
} }
return hasPositiveBalance, err return hasPositiveBalance, err
} }
balanceMapCopy[tokenKey].Sub(tokenBalance, path.requiredTokenBalance) balanceMapCopy[tokenKey].Sub(tokenBalance, path.RequiredTokenBalance)
} else { } else {
return hasPositiveBalance, ErrTokenNotFound return hasPositiveBalance, ErrTokenNotFound
} }
@ -828,14 +821,14 @@ func (r *Router) checkBalancesForTheBestRoute(ctx context.Context, bestRoute []*
ethKey := makeBalanceKey(path.FromChain.ChainID, pathprocessor.EthSymbol) ethKey := makeBalanceKey(path.FromChain.ChainID, pathprocessor.EthSymbol)
if nativeBalance, ok := balanceMapCopy[ethKey]; ok { if nativeBalance, ok := balanceMapCopy[ethKey]; ok {
if nativeBalance.Cmp(path.requiredNativeBalance) == -1 { if nativeBalance.Cmp(path.RequiredNativeBalance) == -1 {
err := &errors.ErrorResponse{ err := &errors.ErrorResponse{
Code: ErrNotEnoughNativeBalance.Code, Code: ErrNotEnoughNativeBalance.Code,
Details: fmt.Sprintf(ErrNotEnoughNativeBalance.Details, pathprocessor.EthSymbol, path.FromChain.ChainID), Details: fmt.Sprintf(ErrNotEnoughNativeBalance.Details, pathprocessor.EthSymbol, path.FromChain.ChainID),
} }
return hasPositiveBalance, err return hasPositiveBalance, err
} }
balanceMapCopy[ethKey].Sub(nativeBalance, path.requiredNativeBalance) balanceMapCopy[ethKey].Sub(nativeBalance, path.RequiredNativeBalance)
} else { } else {
return hasPositiveBalance, ErrNativeTokenNotFound return hasPositiveBalance, ErrNativeTokenNotFound
} }
@ -844,7 +837,7 @@ func (r *Router) checkBalancesForTheBestRoute(ctx context.Context, bestRoute []*
return hasPositiveBalance, nil return hasPositiveBalance, nil
} }
func (r *Router) resolveRoutes(ctx context.Context, input *requests.RouteInputParams, candidates []*Path, balanceMap map[string]*big.Int) (suggestedRoutes *SuggestedRoutes, err error) { func (r *Router) resolveRoutes(ctx context.Context, input *requests.RouteInputParams, candidates routes.Route, balanceMap map[string]*big.Int) (suggestedRoutes *SuggestedRoutes, err error) {
var prices map[string]float64 var prices map[string]float64
if input.TestsMode { if input.TestsMode {
prices = input.TestParams.TokenPrices prices = input.TestParams.TokenPrices
@ -858,7 +851,7 @@ func (r *Router) resolveRoutes(ctx context.Context, input *requests.RouteInputPa
tokenPrice := prices[input.TokenID] tokenPrice := prices[input.TokenID]
nativeTokenPrice := prices[pathprocessor.EthSymbol] nativeTokenPrice := prices[pathprocessor.EthSymbol]
var allRoutes [][]*Path var allRoutes []routes.Route
suggestedRoutes, allRoutes = newSuggestedRoutes(input.Uuid, input.AmountIn.ToInt(), candidates, input.FromLockedAmount, tokenPrice, nativeTokenPrice) suggestedRoutes, allRoutes = newSuggestedRoutes(input.Uuid, input.AmountIn.ToInt(), candidates, input.FromLockedAmount, tokenPrice, nativeTokenPrice)
defer func() { defer func() {
@ -872,13 +865,13 @@ func (r *Router) resolveRoutes(ctx context.Context, input *requests.RouteInputPa
}() }()
var ( var (
bestRoute []*Path bestRoute routes.Route
lastBestRouteWithPositiveBalance []*Path lastBestRouteWithPositiveBalance routes.Route
lastBestRouteErr error lastBestRouteErr error
) )
for len(allRoutes) > 0 { for len(allRoutes) > 0 {
bestRoute = findBest(allRoutes, tokenPrice, nativeTokenPrice) bestRoute = routes.FindBestRoute(allRoutes, tokenPrice, nativeTokenPrice)
var hasPositiveBalance bool var hasPositiveBalance bool
hasPositiveBalance, err = r.checkBalancesForTheBestRoute(ctx, bestRoute, balanceMap) hasPositiveBalance, err = r.checkBalancesForTheBestRoute(ctx, bestRoute, balanceMap)
@ -913,7 +906,7 @@ func (r *Router) resolveRoutes(ctx context.Context, input *requests.RouteInputPa
if len(bestRoute) > 0 { if len(bestRoute) > 0 {
// At this point we have to do the final check and update the amountIn (subtracting fees) if complete balance is going to be sent for native token (ETH) // At this point we have to do the final check and update the amountIn (subtracting fees) if complete balance is going to be sent for native token (ETH)
for _, path := range bestRoute { for _, path := range bestRoute {
if path.subtractFees && path.FromToken.IsNative() { if path.SubtractFees && path.FromToken.IsNative() {
path.AmountIn.ToInt().Sub(path.AmountIn.ToInt(), path.TxFee.ToInt()) path.AmountIn.ToInt().Sub(path.AmountIn.ToInt(), path.TxFee.ToInt())
if path.TxL1Fee.ToInt().Cmp(pathprocessor.ZeroBigIntValue) > 0 { if path.TxL1Fee.ToInt().Cmp(pathprocessor.ZeroBigIntValue) > 0 {
path.AmountIn.ToInt().Sub(path.AmountIn.ToInt(), path.TxL1Fee.ToInt()) path.AmountIn.ToInt().Sub(path.AmountIn.ToInt(), path.TxL1Fee.ToInt())

View File

@ -10,7 +10,9 @@ import (
"github.com/status-im/status-go/appdatabase" "github.com/status-im/status-go/appdatabase"
"github.com/status-im/status-go/params" "github.com/status-im/status-go/params"
"github.com/status-im/status-go/rpc" "github.com/status-im/status-go/rpc"
"github.com/status-im/status-go/services/wallet/responses"
"github.com/status-im/status-go/services/wallet/router/pathprocessor" "github.com/status-im/status-go/services/wallet/router/pathprocessor"
"github.com/status-im/status-go/services/wallet/router/routes"
"github.com/status-im/status-go/signal" "github.com/status-im/status-go/signal"
"github.com/status-im/status-go/t/helpers" "github.com/status-im/status-go/t/helpers"
@ -58,7 +60,7 @@ func amountOptionsMapsEqual(map1, map2 map[uint64][]amountOption) bool {
return true return true
} }
func assertPathsEqual(t *testing.T, expected, actual []*Path) { func assertPathsEqual(t *testing.T, expected, actual routes.Route) {
assert.Equal(t, len(expected), len(actual)) assert.Equal(t, len(expected), len(actual))
if len(expected) == 0 { if len(expected) == 0 {
return return
@ -124,19 +126,19 @@ func setupRouter(t *testing.T) (*Router, func()) {
return router, cleanTmpDb return router, cleanTmpDb
} }
type suggestedRoutesResponseEnvelope struct { type routerSuggestedRoutesEnvelope struct {
Type string `json:"type"` Type string `json:"type"`
Routes SuggestedRoutesResponse `json:"event"` Routes responses.RouterSuggestedRoutes `json:"event"`
} }
func setupSignalHandler(t *testing.T) (chan SuggestedRoutesResponse, func()) { func setupSignalHandler(t *testing.T) (chan responses.RouterSuggestedRoutes, func()) {
suggestedRoutesCh := make(chan SuggestedRoutesResponse) suggestedRoutesCh := make(chan responses.RouterSuggestedRoutes)
signalHandler := signal.MobileSignalHandler(func(data []byte) { signalHandler := signal.MobileSignalHandler(func(data []byte) {
var envelope signal.Envelope var envelope signal.Envelope
err := json.Unmarshal(data, &envelope) err := json.Unmarshal(data, &envelope)
assert.NoError(t, err) assert.NoError(t, err)
if envelope.Type == string(signal.SuggestedRoutes) { if envelope.Type == string(signal.SuggestedRoutes) {
var response suggestedRoutesResponseEnvelope var response routerSuggestedRoutesEnvelope
err := json.Unmarshal(data, &response) err := json.Unmarshal(data, &response)
assert.NoError(t, err) assert.NoError(t, err)

View File

@ -15,6 +15,7 @@ import (
"github.com/status-im/status-go/services/wallet/requests" "github.com/status-im/status-go/services/wallet/requests"
"github.com/status-im/status-go/services/wallet/router/fees" "github.com/status-im/status-go/services/wallet/router/fees"
"github.com/status-im/status-go/services/wallet/router/pathprocessor" "github.com/status-im/status-go/services/wallet/router/pathprocessor"
"github.com/status-im/status-go/services/wallet/router/routes"
"github.com/status-im/status-go/services/wallet/router/sendtype" "github.com/status-im/status-go/services/wallet/router/sendtype"
"github.com/status-im/status-go/services/wallet/token" "github.com/status-im/status-go/services/wallet/token"
) )
@ -205,7 +206,7 @@ var defaultNetworks = []params.Network{
type normalTestParams struct { type normalTestParams struct {
name string name string
input *requests.RouteInputParams input *requests.RouteInputParams
expectedCandidates []*Path expectedCandidates routes.Route
expectedError *errors.ErrorResponse expectedError *errors.ErrorResponse
} }
@ -249,7 +250,7 @@ func getNormalTestParamsList() []normalTestParams {
Code: errors.GenericErrorCode, Code: errors.GenericErrorCode,
Details: fmt.Sprintf("failed with 50000000 gas: insufficient funds for gas * price + value: address %s", common.HexToAddress("0x1")), Details: fmt.Sprintf("failed with 50000000 gas: insufficient funds for gas * price + value: address %s", common.HexToAddress("0x1")),
}, },
expectedCandidates: []*Path{}, expectedCandidates: routes.Route{},
}, },
{ {
name: "ETH transfer - No Specific FromChain - No Specific ToChain - 0 AmountIn", name: "ETH transfer - No Specific FromChain - No Specific ToChain - 0 AmountIn",
@ -278,7 +279,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorTransferName, ProcessorName: pathprocessor.ProcessorTransferName,
FromChain: &mainnet, FromChain: &mainnet,
@ -326,7 +327,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorTransferName, ProcessorName: pathprocessor.ProcessorTransferName,
FromChain: &mainnet, FromChain: &mainnet,
@ -412,7 +413,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorTransferName, ProcessorName: pathprocessor.ProcessorTransferName,
FromChain: &mainnet, FromChain: &mainnet,
@ -462,7 +463,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorTransferName, ProcessorName: pathprocessor.ProcessorTransferName,
FromChain: &optimism, FromChain: &optimism,
@ -531,7 +532,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorTransferName, ProcessorName: pathprocessor.ProcessorTransferName,
FromChain: &arbitrum, FromChain: &arbitrum,
@ -581,7 +582,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorTransferName, ProcessorName: pathprocessor.ProcessorTransferName,
FromChain: &optimism, FromChain: &optimism,
@ -650,7 +651,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorTransferName, ProcessorName: pathprocessor.ProcessorTransferName,
FromChain: &arbitrum, FromChain: &arbitrum,
@ -689,7 +690,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorBridgeHopName, ProcessorName: pathprocessor.ProcessorBridgeHopName,
FromChain: &arbitrum, FromChain: &arbitrum,
@ -728,7 +729,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorTransferName, ProcessorName: pathprocessor.ProcessorTransferName,
FromChain: &arbitrum, FromChain: &arbitrum,
@ -767,7 +768,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorTransferName, ProcessorName: pathprocessor.ProcessorTransferName,
FromChain: &mainnet, FromChain: &mainnet,
@ -824,7 +825,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorBridgeHopName, ProcessorName: pathprocessor.ProcessorBridgeHopName,
FromChain: &arbitrum, FromChain: &arbitrum,
@ -865,7 +866,7 @@ func getNormalTestParamsList() []normalTestParams {
}, },
}, },
expectedError: ErrNoBestRouteFound, expectedError: ErrNoBestRouteFound,
expectedCandidates: []*Path{}, expectedCandidates: routes.Route{},
}, },
{ {
name: "ETH transfer - No Specific FromChain - No Specific ToChain - Single Chain LockedAmount", name: "ETH transfer - No Specific FromChain - No Specific ToChain - Single Chain LockedAmount",
@ -898,7 +899,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorTransferName, ProcessorName: pathprocessor.ProcessorTransferName,
FromChain: &mainnet, FromChain: &mainnet,
@ -997,7 +998,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorTransferName, ProcessorName: pathprocessor.ProcessorTransferName,
FromChain: &optimism, FromChain: &optimism,
@ -1053,7 +1054,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorTransferName, ProcessorName: pathprocessor.ProcessorTransferName,
FromChain: &mainnet, FromChain: &mainnet,
@ -1152,7 +1153,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorTransferName, ProcessorName: pathprocessor.ProcessorTransferName,
FromChain: &mainnet, FromChain: &mainnet,
@ -1252,7 +1253,7 @@ func getNormalTestParamsList() []normalTestParams {
}, },
}, },
expectedError: requests.ErrLockedAmountLessThanSendAmountAllNetworks, expectedError: requests.ErrLockedAmountLessThanSendAmountAllNetworks,
expectedCandidates: []*Path{}, expectedCandidates: routes.Route{},
}, },
{ {
name: "ETH transfer - No Specific FromChain - No Specific ToChain - LockedAmount exceeds sending amount", name: "ETH transfer - No Specific FromChain - No Specific ToChain - LockedAmount exceeds sending amount",
@ -1287,7 +1288,7 @@ func getNormalTestParamsList() []normalTestParams {
}, },
}, },
expectedError: requests.ErrLockedAmountExceedsTotalSendAmount, expectedError: requests.ErrLockedAmountExceedsTotalSendAmount,
expectedCandidates: []*Path{}, expectedCandidates: routes.Route{},
}, },
{ {
name: "ERC20 transfer - No Specific FromChain - No Specific ToChain", name: "ERC20 transfer - No Specific FromChain - No Specific ToChain",
@ -1317,7 +1318,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorTransferName, ProcessorName: pathprocessor.ProcessorTransferName,
FromChain: &mainnet, FromChain: &mainnet,
@ -1403,7 +1404,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorTransferName, ProcessorName: pathprocessor.ProcessorTransferName,
FromChain: &mainnet, FromChain: &mainnet,
@ -1453,7 +1454,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorTransferName, ProcessorName: pathprocessor.ProcessorTransferName,
FromChain: &optimism, FromChain: &optimism,
@ -1521,7 +1522,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorTransferName, ProcessorName: pathprocessor.ProcessorTransferName,
FromChain: &arbitrum, FromChain: &arbitrum,
@ -1571,7 +1572,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorTransferName, ProcessorName: pathprocessor.ProcessorTransferName,
FromChain: &optimism, FromChain: &optimism,
@ -1640,7 +1641,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorTransferName, ProcessorName: pathprocessor.ProcessorTransferName,
FromChain: &arbitrum, FromChain: &arbitrum,
@ -1679,7 +1680,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorBridgeHopName, ProcessorName: pathprocessor.ProcessorBridgeHopName,
FromChain: &arbitrum, FromChain: &arbitrum,
@ -1718,7 +1719,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorTransferName, ProcessorName: pathprocessor.ProcessorTransferName,
FromChain: &arbitrum, FromChain: &arbitrum,
@ -1757,7 +1758,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorTransferName, ProcessorName: pathprocessor.ProcessorTransferName,
FromChain: &mainnet, FromChain: &mainnet,
@ -1814,7 +1815,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorBridgeHopName, ProcessorName: pathprocessor.ProcessorBridgeHopName,
FromChain: &arbitrum, FromChain: &arbitrum,
@ -1854,7 +1855,7 @@ func getNormalTestParamsList() []normalTestParams {
}, },
}, },
expectedError: ErrNoBestRouteFound, expectedError: ErrNoBestRouteFound,
expectedCandidates: []*Path{}, expectedCandidates: routes.Route{},
}, },
{ {
name: "ERC20 transfer - All FromChains - No Locked Amount - Enough Token Balance Across All Chains", name: "ERC20 transfer - All FromChains - No Locked Amount - Enough Token Balance Across All Chains",
@ -1884,7 +1885,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorTransferName, ProcessorName: pathprocessor.ProcessorTransferName,
FromChain: &mainnet, FromChain: &mainnet,
@ -2083,7 +2084,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorBridgeHopName, ProcessorName: pathprocessor.ProcessorBridgeHopName,
FromChain: &mainnet, FromChain: &mainnet,
@ -2151,7 +2152,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorBridgeHopName, ProcessorName: pathprocessor.ProcessorBridgeHopName,
FromChain: &optimism, FromChain: &optimism,
@ -2195,7 +2196,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorBridgeHopName, ProcessorName: pathprocessor.ProcessorBridgeHopName,
FromChain: &mainnet, FromChain: &mainnet,
@ -2251,7 +2252,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorBridgeHopName, ProcessorName: pathprocessor.ProcessorBridgeHopName,
FromChain: &arbitrum, FromChain: &arbitrum,
@ -2295,7 +2296,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorBridgeHopName, ProcessorName: pathprocessor.ProcessorBridgeHopName,
FromChain: &optimism, FromChain: &optimism,
@ -2353,7 +2354,7 @@ func getNormalTestParamsList() []normalTestParams {
}, },
}, },
expectedError: ErrNoBestRouteFound, expectedError: ErrNoBestRouteFound,
expectedCandidates: []*Path{}, expectedCandidates: routes.Route{},
}, },
{ {
name: "Bridge - Specific Single FromChain - Specific Single ToChain - Different Chains", name: "Bridge - Specific Single FromChain - Specific Single ToChain - Different Chains",
@ -2385,7 +2386,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorBridgeHopName, ProcessorName: pathprocessor.ProcessorBridgeHopName,
FromChain: &arbitrum, FromChain: &arbitrum,
@ -2425,7 +2426,7 @@ func getNormalTestParamsList() []normalTestParams {
}, },
}, },
expectedError: ErrNoBestRouteFound, expectedError: ErrNoBestRouteFound,
expectedCandidates: []*Path{}, expectedCandidates: routes.Route{},
}, },
{ {
name: "Bridge - Specific Multiple FromChain - Specific Multiple ToChain - Multiple Common Chains", name: "Bridge - Specific Multiple FromChain - Specific Multiple ToChain - Multiple Common Chains",
@ -2457,7 +2458,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorBridgeHopName, ProcessorName: pathprocessor.ProcessorBridgeHopName,
FromChain: &mainnet, FromChain: &mainnet,
@ -2502,7 +2503,7 @@ func getNormalTestParamsList() []normalTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorBridgeHopName, ProcessorName: pathprocessor.ProcessorBridgeHopName,
FromChain: &arbitrum, FromChain: &arbitrum,
@ -2542,7 +2543,7 @@ func getNormalTestParamsList() []normalTestParams {
}, },
}, },
expectedError: ErrNoBestRouteFound, expectedError: ErrNoBestRouteFound,
expectedCandidates: []*Path{}, expectedCandidates: routes.Route{},
}, },
{ {
name: "ETH transfer - Not Enough Native Balance", name: "ETH transfer - Not Enough Native Balance",
@ -2577,7 +2578,7 @@ func getNormalTestParamsList() []normalTestParams {
Code: ErrNotEnoughNativeBalance.Code, Code: ErrNotEnoughNativeBalance.Code,
Details: fmt.Sprintf(ErrNotEnoughNativeBalance.Details, pathprocessor.EthSymbol, walletCommon.EthereumMainnet), Details: fmt.Sprintf(ErrNotEnoughNativeBalance.Details, pathprocessor.EthSymbol, walletCommon.EthereumMainnet),
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorBridgeHopName, ProcessorName: pathprocessor.ProcessorBridgeHopName,
FromChain: &mainnet, FromChain: &mainnet,
@ -2619,7 +2620,7 @@ func getNormalTestParamsList() []normalTestParams {
Code: ErrNotEnoughTokenBalance.Code, Code: ErrNotEnoughTokenBalance.Code,
Details: fmt.Sprintf(ErrNotEnoughTokenBalance.Details, pathprocessor.UsdcSymbol, walletCommon.EthereumMainnet), Details: fmt.Sprintf(ErrNotEnoughTokenBalance.Details, pathprocessor.UsdcSymbol, walletCommon.EthereumMainnet),
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorBridgeHopName, ProcessorName: pathprocessor.ProcessorBridgeHopName,
FromChain: &mainnet, FromChain: &mainnet,
@ -2659,7 +2660,7 @@ func getNormalTestParamsList() []normalTestParams {
}, },
}, },
expectedError: ErrLowAmountInForHopBridge, expectedError: ErrLowAmountInForHopBridge,
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorBridgeHopName, ProcessorName: pathprocessor.ProcessorBridgeHopName,
FromChain: &arbitrum, FromChain: &arbitrum,
@ -2674,8 +2675,8 @@ func getNormalTestParamsList() []normalTestParams {
type noBalanceTestParams struct { type noBalanceTestParams struct {
name string name string
input *requests.RouteInputParams input *requests.RouteInputParams
expectedCandidates []*Path expectedCandidates routes.Route
expectedBest []*Path expectedBest routes.Route
expectedError *errors.ErrorResponse expectedError *errors.ErrorResponse
} }
@ -2750,14 +2751,14 @@ func getNoBalanceTestParamsList() []noBalanceTestParams {
Code: ErrNotEnoughNativeBalance.Code, Code: ErrNotEnoughNativeBalance.Code,
Details: fmt.Sprintf(ErrNotEnoughNativeBalance.Details, pathprocessor.EthSymbol, walletCommon.OptimismMainnet), Details: fmt.Sprintf(ErrNotEnoughNativeBalance.Details, pathprocessor.EthSymbol, walletCommon.OptimismMainnet),
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorTransferName, ProcessorName: pathprocessor.ProcessorTransferName,
FromChain: &optimism, FromChain: &optimism,
ToChain: &optimism, ToChain: &optimism,
ApprovalRequired: false, ApprovalRequired: false,
requiredTokenBalance: big.NewInt(testAmount100USDC), RequiredTokenBalance: big.NewInt(testAmount100USDC),
requiredNativeBalance: big.NewInt((testBaseFee + testPriorityFeeLow) * testApprovalGasEstimation), RequiredNativeBalance: big.NewInt((testBaseFee + testPriorityFeeLow) * testApprovalGasEstimation),
}, },
}, },
}, },
@ -2837,7 +2838,7 @@ func getNoBalanceTestParamsList() []noBalanceTestParams {
Code: ErrNotEnoughNativeBalance.Code, Code: ErrNotEnoughNativeBalance.Code,
Details: fmt.Sprintf(ErrNotEnoughNativeBalance.Details, pathprocessor.EthSymbol, walletCommon.ArbitrumMainnet), Details: fmt.Sprintf(ErrNotEnoughNativeBalance.Details, pathprocessor.EthSymbol, walletCommon.ArbitrumMainnet),
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorBridgeHopName, ProcessorName: pathprocessor.ProcessorBridgeHopName,
FromChain: &mainnet, FromChain: &mainnet,
@ -2849,8 +2850,8 @@ func getNoBalanceTestParamsList() []noBalanceTestParams {
FromChain: &optimism, FromChain: &optimism,
ToChain: &optimism, ToChain: &optimism,
ApprovalRequired: false, ApprovalRequired: false,
requiredTokenBalance: big.NewInt(testAmount100USDC), RequiredTokenBalance: big.NewInt(testAmount100USDC),
requiredNativeBalance: big.NewInt((testBaseFee + testPriorityFeeLow) * testApprovalGasEstimation), RequiredNativeBalance: big.NewInt((testBaseFee + testPriorityFeeLow) * testApprovalGasEstimation),
}, },
{ {
ProcessorName: pathprocessor.ProcessorBridgeHopName, ProcessorName: pathprocessor.ProcessorBridgeHopName,
@ -2891,7 +2892,7 @@ func getNoBalanceTestParamsList() []noBalanceTestParams {
ApprovalL1Fee: testApprovalL1Fee, ApprovalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedCandidates: []*Path{ expectedCandidates: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorBridgeHopName, ProcessorName: pathprocessor.ProcessorBridgeHopName,
FromChain: &mainnet, FromChain: &mainnet,
@ -2903,8 +2904,8 @@ func getNoBalanceTestParamsList() []noBalanceTestParams {
FromChain: &optimism, FromChain: &optimism,
ToChain: &optimism, ToChain: &optimism,
ApprovalRequired: false, ApprovalRequired: false,
requiredTokenBalance: big.NewInt(testAmount100USDC), RequiredTokenBalance: big.NewInt(testAmount100USDC),
requiredNativeBalance: big.NewInt((testBaseFee + testPriorityFeeLow) * testApprovalGasEstimation), RequiredNativeBalance: big.NewInt((testBaseFee + testPriorityFeeLow) * testApprovalGasEstimation),
}, },
{ {
ProcessorName: pathprocessor.ProcessorBridgeHopName, ProcessorName: pathprocessor.ProcessorBridgeHopName,
@ -2913,7 +2914,7 @@ func getNoBalanceTestParamsList() []noBalanceTestParams {
ApprovalRequired: true, ApprovalRequired: true,
}, },
}, },
expectedBest: []*Path{ expectedBest: routes.Route{
{ {
ProcessorName: pathprocessor.ProcessorBridgeHopName, ProcessorName: pathprocessor.ProcessorBridgeHopName,
FromChain: &arbitrum, FromChain: &arbitrum,

View File

@ -1,4 +1,4 @@
package router package routes
import ( import (
"math" "math"
@ -8,80 +8,10 @@ import (
"github.com/status-im/status-go/services/wallet/router/pathprocessor" "github.com/status-im/status-go/services/wallet/router/pathprocessor"
) )
type Graph []*Node type Route []*Path
type Node struct { func FindBestRoute(routes []Route, tokenPrice float64, nativeTokenPrice float64) Route {
Path *Path var best Route
Children Graph
}
func newNode(path *Path) *Node {
return &Node{Path: path, Children: make(Graph, 0)}
}
func buildGraph(AmountIn *big.Int, routes []*Path, level int, sourceChainIDs []uint64) Graph {
graph := make(Graph, 0)
for _, route := range routes {
found := false
for _, chainID := range sourceChainIDs {
if chainID == route.FromChain.ChainID {
found = true
break
}
}
if found {
continue
}
node := newNode(route)
newRoutes := make([]*Path, 0)
for _, r := range routes {
if route.Equal(r) {
continue
}
newRoutes = append(newRoutes, r)
}
newAmountIn := new(big.Int).Sub(AmountIn, route.AmountIn.ToInt())
if newAmountIn.Sign() > 0 {
newSourceChainIDs := make([]uint64, len(sourceChainIDs))
copy(newSourceChainIDs, sourceChainIDs)
newSourceChainIDs = append(newSourceChainIDs, route.FromChain.ChainID)
node.Children = buildGraph(newAmountIn, newRoutes, level+1, newSourceChainIDs)
if len(node.Children) == 0 {
continue
}
}
graph = append(graph, node)
}
return graph
}
func (n Node) buildAllRoutes() [][]*Path {
res := make([][]*Path, 0)
if len(n.Children) == 0 && n.Path != nil {
res = append(res, []*Path{n.Path})
}
for _, node := range n.Children {
for _, route := range node.buildAllRoutes() {
extendedRoute := route
if n.Path != nil {
extendedRoute = append([]*Path{n.Path}, route...)
}
res = append(res, extendedRoute)
}
}
return res
}
func findBest(routes [][]*Path, tokenPrice float64, nativeTokenPrice float64) []*Path {
var best []*Path
bestCost := big.NewFloat(math.Inf(1)) bestCost := big.NewFloat(math.Inf(1))
for _, route := range routes { for _, route := range routes {
currentCost := big.NewFloat(0) currentCost := big.NewFloat(0)

View File

@ -0,0 +1,77 @@
package routes
import (
"math/big"
)
type Graph []*Node
type Node struct {
Path *Path
Children Graph
}
func newNode(path *Path) *Node {
return &Node{Path: path, Children: make(Graph, 0)}
}
func BuildGraph(AmountIn *big.Int, route Route, level int, sourceChainIDs []uint64) Graph {
graph := make(Graph, 0)
for _, path := range route {
found := false
for _, chainID := range sourceChainIDs {
if chainID == path.FromChain.ChainID {
found = true
break
}
}
if found {
continue
}
node := newNode(path)
newRoute := make(Route, 0)
for _, p := range route {
if path.Equal(p) {
continue
}
newRoute = append(newRoute, p)
}
newAmountIn := new(big.Int).Sub(AmountIn, path.AmountIn.ToInt())
if newAmountIn.Sign() > 0 {
newSourceChainIDs := make([]uint64, len(sourceChainIDs))
copy(newSourceChainIDs, sourceChainIDs)
newSourceChainIDs = append(newSourceChainIDs, path.FromChain.ChainID)
node.Children = BuildGraph(newAmountIn, newRoute, level+1, newSourceChainIDs)
if len(node.Children) == 0 {
continue
}
}
graph = append(graph, node)
}
return graph
}
func (n Node) BuildAllRoutes() []Route {
res := make([]Route, 0)
if len(n.Children) == 0 && n.Path != nil {
res = append(res, Route{n.Path})
}
for _, node := range n.Children {
for _, route := range node.BuildAllRoutes() {
extendedRoute := route
if n.Path != nil {
extendedRoute = append(Route{n.Path}, route...)
}
res = append(res, extendedRoute)
}
}
return res
}

View File

@ -1,4 +1,4 @@
package router package routes
import ( import (
"math/big" "math/big"
@ -46,9 +46,9 @@ type Path struct {
EstimatedTime fees.TransactionEstimation EstimatedTime fees.TransactionEstimation
requiredTokenBalance *big.Int // (in selected token) RequiredTokenBalance *big.Int // (in selected token)
requiredNativeBalance *big.Int // (in ETH WEI) RequiredNativeBalance *big.Int // (in ETH WEI)
subtractFees bool SubtractFees bool
} }
func (p *Path) Equal(o *Path) bool { func (p *Path) Equal(o *Path) bool {