fix_: the last known route with positive balance returned if no route without balance/bonder error
The Router algorithm is checking all the routes in order to find one the user has a balance to execute it even that one is not the cheapest one. But if the user has some balances, but not enough to execute any of suggested routes, we we're returning the last route which was checked, with the changes from this commit we will return the last route for which the user has some balance, but not enough.
This commit is contained in:
parent
89d6c55d3e
commit
cf1a6631f8
|
@ -1021,54 +1021,60 @@ func (r *Router) resolveCandidates(ctx context.Context, input *RouteInputParams,
|
|||
return candidates, processorErrors, nil
|
||||
}
|
||||
|
||||
func (r *Router) checkBalancesForTheBestRoute(ctx context.Context, bestRoute []*PathV2, input *RouteInputParams, balanceMap map[string]*big.Int) (err error) {
|
||||
func (r *Router) checkBalancesForTheBestRoute(ctx context.Context, bestRoute []*PathV2, input *RouteInputParams, balanceMap map[string]*big.Int) (hasPositiveBalance bool, err error) {
|
||||
balanceMapCopy := copyMapGeneric(balanceMap, func(v interface{}) interface{} {
|
||||
return new(big.Int).Set(v.(*big.Int))
|
||||
}).(map[string]*big.Int)
|
||||
if balanceMapCopy == nil {
|
||||
return ErrCannotCheckBalance
|
||||
return false, ErrCannotCheckBalance
|
||||
}
|
||||
|
||||
// check the best route for the required balances
|
||||
for _, path := range bestRoute {
|
||||
tokenKey := makeBalanceKey(path.FromChain.ChainID, path.FromToken.Symbol)
|
||||
if tokenBalance, ok := balanceMapCopy[tokenKey]; ok {
|
||||
if tokenBalance.Cmp(pathprocessor.ZeroBigIntValue) > 0 {
|
||||
hasPositiveBalance = true
|
||||
}
|
||||
}
|
||||
|
||||
if path.ProcessorName == pathprocessor.ProcessorBridgeHopName {
|
||||
if path.TxBonderFees.ToInt().Cmp(path.AmountOut.ToInt()) > 0 {
|
||||
return ErrLowAmountInForHopBridge
|
||||
return hasPositiveBalance, ErrLowAmountInForHopBridge
|
||||
}
|
||||
}
|
||||
|
||||
if path.requiredTokenBalance != nil && path.requiredTokenBalance.Cmp(pathprocessor.ZeroBigIntValue) > 0 {
|
||||
key := makeBalanceKey(path.FromChain.ChainID, path.FromToken.Symbol)
|
||||
if tokenBalance, ok := balanceMapCopy[key]; ok {
|
||||
if tokenBalance, ok := balanceMapCopy[tokenKey]; ok {
|
||||
if tokenBalance.Cmp(path.requiredTokenBalance) == -1 {
|
||||
err := &errors.ErrorResponse{
|
||||
Code: ErrNotEnoughTokenBalance.Code,
|
||||
Details: fmt.Sprintf(ErrNotEnoughTokenBalance.Details, path.FromToken.Symbol, path.FromChain.ChainID),
|
||||
}
|
||||
return err
|
||||
return hasPositiveBalance, err
|
||||
}
|
||||
balanceMapCopy[key].Sub(tokenBalance, path.requiredTokenBalance)
|
||||
balanceMapCopy[tokenKey].Sub(tokenBalance, path.requiredTokenBalance)
|
||||
} else {
|
||||
return ErrTokenNotFound
|
||||
return hasPositiveBalance, ErrTokenNotFound
|
||||
}
|
||||
}
|
||||
|
||||
key := makeBalanceKey(path.FromChain.ChainID, pathprocessor.EthSymbol)
|
||||
if nativeBalance, ok := balanceMapCopy[key]; ok {
|
||||
ethKey := makeBalanceKey(path.FromChain.ChainID, pathprocessor.EthSymbol)
|
||||
if nativeBalance, ok := balanceMapCopy[ethKey]; ok {
|
||||
if nativeBalance.Cmp(path.requiredNativeBalance) == -1 {
|
||||
err := &errors.ErrorResponse{
|
||||
Code: ErrNotEnoughNativeBalance.Code,
|
||||
Details: fmt.Sprintf(ErrNotEnoughNativeBalance.Details, pathprocessor.EthSymbol, path.FromChain.ChainID),
|
||||
}
|
||||
return err
|
||||
return hasPositiveBalance, err
|
||||
}
|
||||
balanceMapCopy[key].Sub(nativeBalance, path.requiredNativeBalance)
|
||||
balanceMapCopy[ethKey].Sub(nativeBalance, path.requiredNativeBalance)
|
||||
} else {
|
||||
return ErrNativeTokenNotFound
|
||||
return hasPositiveBalance, ErrNativeTokenNotFound
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return hasPositiveBalance, nil
|
||||
}
|
||||
|
||||
func removeBestRouteFromAllRouters(allRoutes [][]*PathV2, best []*PathV2) [][]*PathV2 {
|
||||
|
@ -1147,45 +1153,63 @@ func (r *Router) resolveRoutes(ctx context.Context, input *RouteInputParams, can
|
|||
}
|
||||
}()
|
||||
|
||||
for len(allRoutes) > 0 {
|
||||
best := findBestV2(allRoutes, tokenPrice, nativeTokenPrice)
|
||||
var (
|
||||
bestRoute []*PathV2
|
||||
lastBestRouteWithPositiveBalance []*PathV2
|
||||
lastBestRouteErr error
|
||||
)
|
||||
|
||||
for len(allRoutes) > 0 {
|
||||
bestRoute = findBestV2(allRoutes, tokenPrice, nativeTokenPrice)
|
||||
var hasPositiveBalance bool
|
||||
hasPositiveBalance, err = r.checkBalancesForTheBestRoute(ctx, bestRoute, input, balanceMap)
|
||||
|
||||
err := r.checkBalancesForTheBestRoute(ctx, best, input, balanceMap)
|
||||
if err != nil {
|
||||
// If it's about transfer or bridge and there is more routes, but on the best (cheapest) one there is not enugh balance
|
||||
// we shold check other routes even though there are not the cheapest ones
|
||||
if (input.SendType == Transfer ||
|
||||
input.SendType == Bridge) &&
|
||||
len(allRoutes) > 1 {
|
||||
if input.SendType == Transfer ||
|
||||
input.SendType == Bridge {
|
||||
if hasPositiveBalance {
|
||||
lastBestRouteWithPositiveBalance = bestRoute
|
||||
lastBestRouteErr = err
|
||||
}
|
||||
|
||||
allRoutes = removeBestRouteFromAllRouters(allRoutes, best)
|
||||
continue
|
||||
} else {
|
||||
suggestedRoutes.Best = best
|
||||
return suggestedRoutes, errors.CreateErrorResponseFromError(err)
|
||||
if len(allRoutes) > 1 {
|
||||
allRoutes = removeBestRouteFromAllRouters(allRoutes, bestRoute)
|
||||
continue
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(best) > 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)
|
||||
for _, path := range best {
|
||||
if path.subtractFees && path.FromToken.IsNative() {
|
||||
path.AmountIn.ToInt().Sub(path.AmountIn.ToInt(), path.TxFee.ToInt())
|
||||
if path.TxL1Fee.ToInt().Cmp(pathprocessor.ZeroBigIntValue) > 0 {
|
||||
path.AmountIn.ToInt().Sub(path.AmountIn.ToInt(), path.TxL1Fee.ToInt())
|
||||
}
|
||||
if path.ApprovalRequired {
|
||||
path.AmountIn.ToInt().Sub(path.AmountIn.ToInt(), path.ApprovalFee.ToInt())
|
||||
if path.ApprovalL1Fee.ToInt().Cmp(pathprocessor.ZeroBigIntValue) > 0 {
|
||||
path.AmountIn.ToInt().Sub(path.AmountIn.ToInt(), path.ApprovalL1Fee.ToInt())
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// if none of the routes have positive balance, we should return the last best route with positive balance
|
||||
if err != nil && lastBestRouteWithPositiveBalance != nil {
|
||||
bestRoute = lastBestRouteWithPositiveBalance
|
||||
err = lastBestRouteErr
|
||||
}
|
||||
|
||||
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)
|
||||
for _, path := range bestRoute {
|
||||
if path.subtractFees && path.FromToken.IsNative() {
|
||||
path.AmountIn.ToInt().Sub(path.AmountIn.ToInt(), path.TxFee.ToInt())
|
||||
if path.TxL1Fee.ToInt().Cmp(pathprocessor.ZeroBigIntValue) > 0 {
|
||||
path.AmountIn.ToInt().Sub(path.AmountIn.ToInt(), path.TxL1Fee.ToInt())
|
||||
}
|
||||
if path.ApprovalRequired {
|
||||
path.AmountIn.ToInt().Sub(path.AmountIn.ToInt(), path.ApprovalFee.ToInt())
|
||||
if path.ApprovalL1Fee.ToInt().Cmp(pathprocessor.ZeroBigIntValue) > 0 {
|
||||
path.AmountIn.ToInt().Sub(path.AmountIn.ToInt(), path.ApprovalL1Fee.ToInt())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
suggestedRoutes.Best = best
|
||||
break
|
||||
}
|
||||
suggestedRoutes.Best = bestRoute
|
||||
|
||||
return suggestedRoutes, nil
|
||||
return suggestedRoutes, err
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue