chore_: map insufficient funds error

This commit maps this kind of errors
`status-proxy-0.error: failed with 50011064 gas: insufficient funds for gas * price + value: address 0x4eeB09cf0076F840b38511D808464eE48efD4305 have 0 want 10000000000000`
to this form:
`status-proxy-0.error: failed with 50011064 gas: insufficient funds for gas * price + value: address 0x4eeB09cf0076F840b38511D808464eE48efD4305`

which means that we don't want to display to a user details of how much they have and how much is needed for fees, cause those data are very often
misleading, referring mostly to "how much user has".

New error added in case there is no positive balances across all enabled chains.
This commit is contained in:
Sale Djenic 2024-08-23 16:01:49 +02:00 committed by saledjenic
parent 85fba77b7d
commit 9b9a91f654
19 changed files with 190 additions and 88 deletions

View File

@ -27,6 +27,28 @@ func IsErrorResponse(err error) bool {
return ok return ok
} }
// ErrorCodeFromError returns the ErrorCode from an error.
func ErrorCodeFromError(err error) ErrorCode {
if err == nil {
return GenericErrorCode
}
if errResp, ok := err.(*ErrorResponse); ok {
return errResp.Code
}
return GenericErrorCode
}
// DetailsFromError returns the details from an error.
func DetailsFromError(err error) string {
if err == nil {
return ""
}
if errResp, ok := err.(*ErrorResponse); ok {
return errResp.Details
}
return err.Error()
}
// CreateErrorResponseFromError creates an ErrorResponse from a generic error. // CreateErrorResponseFromError creates an ErrorResponse from a generic error.
func CreateErrorResponseFromError(err error) error { func CreateErrorResponseFromError(err error) error {
if err == nil { if err == nil {

View File

@ -32,4 +32,5 @@ var (
ErrCannotCheckBalance = &errors.ErrorResponse{Code: errors.ErrorCode("WR-024"), Details: "cannot check balance"} ErrCannotCheckBalance = &errors.ErrorResponse{Code: errors.ErrorCode("WR-024"), Details: "cannot check balance"}
ErrCannotCheckLockedAmounts = &errors.ErrorResponse{Code: errors.ErrorCode("WR-025"), Details: "cannot check locked amounts"} ErrCannotCheckLockedAmounts = &errors.ErrorResponse{Code: errors.ErrorCode("WR-025"), Details: "cannot check locked amounts"}
ErrLowAmountInForHopBridge = &errors.ErrorResponse{Code: errors.ErrorCode("WR-026"), Details: "bonder fee greater than estimated received, a higher amount is needed to cover fees"} ErrLowAmountInForHopBridge = &errors.ErrorResponse{Code: errors.ErrorCode("WR-026"), Details: "bonder fee greater than estimated received, a higher amount is needed to cover fees"}
ErrNoPositiveBalance = &errors.ErrorResponse{Code: errors.ErrorCode("WR-027"), Details: "no positive balance"}
) )

View File

@ -56,8 +56,13 @@ type ProcessorInputParams struct {
// for testing purposes // for testing purposes
TestsMode bool TestsMode bool
TestEstimationMap map[string]uint64 // [brifge-name, estimated-value] TestEstimationMap map[string]Estimation // [bridge-name, estimation]
TestBonderFeeMap map[string]*big.Int // [token-symbol, bonder-fee] TestBonderFeeMap map[string]*big.Int // [token-symbol, bonder-fee]
TestApprovalGasEstimation uint64 TestApprovalGasEstimation uint64
TestApprovalL1Fee uint64 TestApprovalL1Fee uint64
} }
type Estimation struct {
Value uint64
Err error
}

View File

@ -236,7 +236,7 @@ func (s *CelerBridgeProcessor) EstimateGas(params ProcessorInputParams) (uint64,
if params.TestsMode { if params.TestsMode {
if params.TestEstimationMap != nil { if params.TestEstimationMap != nil {
if val, ok := params.TestEstimationMap[s.Name()]; ok { if val, ok := params.TestEstimationMap[s.Name()]; ok {
return val, nil return val.Value, val.Err
} }
} }
return 0, ErrNoEstimationFound return 0, ErrNoEstimationFound

View File

@ -228,7 +228,7 @@ func (h *HopBridgeProcessor) EstimateGas(params ProcessorInputParams) (uint64, e
if params.TestsMode { if params.TestsMode {
if params.TestEstimationMap != nil { if params.TestEstimationMap != nil {
if val, ok := params.TestEstimationMap[h.Name()]; ok { if val, ok := params.TestEstimationMap[h.Name()]; ok {
return val, nil return val.Value, val.Err
} }
} }
return 0, ErrNoEstimationFound return 0, ErrNoEstimationFound

View File

@ -65,7 +65,7 @@ func (s *ENSPublicKeyProcessor) EstimateGas(params ProcessorInputParams) (uint64
if params.TestsMode { if params.TestsMode {
if params.TestEstimationMap != nil { if params.TestEstimationMap != nil {
if val, ok := params.TestEstimationMap[s.Name()]; ok { if val, ok := params.TestEstimationMap[s.Name()]; ok {
return val, nil return val.Value, val.Err
} }
} }
return 0, ErrNoEstimationFound return 0, ErrNoEstimationFound

View File

@ -101,7 +101,7 @@ func (s *ENSRegisterProcessor) EstimateGas(params ProcessorInputParams) (uint64,
if params.TestsMode { if params.TestsMode {
if params.TestEstimationMap != nil { if params.TestEstimationMap != nil {
if val, ok := params.TestEstimationMap[s.Name()]; ok { if val, ok := params.TestEstimationMap[s.Name()]; ok {
return val, nil return val.Value, val.Err
} }
} }
return 0, ErrNoEstimationFound return 0, ErrNoEstimationFound

View File

@ -64,7 +64,7 @@ func (s *ENSReleaseProcessor) EstimateGas(params ProcessorInputParams) (uint64,
if params.TestsMode { if params.TestsMode {
if params.TestEstimationMap != nil { if params.TestEstimationMap != nil {
if val, ok := params.TestEstimationMap[s.Name()]; ok { if val, ok := params.TestEstimationMap[s.Name()]; ok {
return val, nil return val.Value, val.Err
} }
} }
return 0, ErrNoEstimationFound return 0, ErrNoEstimationFound

View File

@ -75,7 +75,7 @@ func (s *ERC1155Processor) EstimateGas(params ProcessorInputParams) (uint64, err
if params.TestsMode { if params.TestsMode {
if params.TestEstimationMap != nil { if params.TestEstimationMap != nil {
if val, ok := params.TestEstimationMap[s.Name()]; ok { if val, ok := params.TestEstimationMap[s.Name()]; ok {
return val, nil return val.Value, val.Err
} }
} }
return 0, ErrNoEstimationFound return 0, ErrNoEstimationFound

View File

@ -112,7 +112,7 @@ func (s *ERC721Processor) EstimateGas(params ProcessorInputParams) (uint64, erro
if params.TestsMode { if params.TestsMode {
if params.TestEstimationMap != nil { if params.TestEstimationMap != nil {
if val, ok := params.TestEstimationMap[s.Name()]; ok { if val, ok := params.TestEstimationMap[s.Name()]; ok {
return val, nil return val.Value, val.Err
} }
} }
return 0, ErrNoEstimationFound return 0, ErrNoEstimationFound

View File

@ -93,7 +93,7 @@ func (s *StickersBuyProcessor) EstimateGas(params ProcessorInputParams) (uint64,
if params.TestsMode { if params.TestsMode {
if params.TestEstimationMap != nil { if params.TestEstimationMap != nil {
if val, ok := params.TestEstimationMap[s.Name()]; ok { if val, ok := params.TestEstimationMap[s.Name()]; ok {
return val, nil return val.Value, val.Err
} }
} }
return 0, ErrNoEstimationFound return 0, ErrNoEstimationFound

View File

@ -173,7 +173,7 @@ func (s *SwapParaswapProcessor) EstimateGas(params ProcessorInputParams) (uint64
if params.TestsMode { if params.TestsMode {
if params.TestEstimationMap != nil { if params.TestEstimationMap != nil {
if val, ok := params.TestEstimationMap[s.Name()]; ok { if val, ok := params.TestEstimationMap[s.Name()]; ok {
return val, nil return val.Value, val.Err
} }
} }
return 0, ErrNoEstimationFound return 0, ErrNoEstimationFound

View File

@ -47,10 +47,10 @@ var optimism = params.Network{
RelatedChainID: walletCommon.OptimismMainnet, RelatedChainID: walletCommon.OptimismMainnet,
} }
var testEstimationMap = map[string]uint64{ var testEstimationMap = map[string]Estimation{
ProcessorTransferName: uint64(1000), ProcessorTransferName: {uint64(1000), nil},
ProcessorBridgeHopName: uint64(5000), ProcessorBridgeHopName: {uint64(5000), nil},
ProcessorSwapParaswapName: uint64(2000), ProcessorSwapParaswapName: {uint64(2000), nil},
} }
type expectedResult struct { type expectedResult struct {
@ -329,17 +329,17 @@ func TestPathProcessors(t *testing.T) {
assert.Greater(t, estimatedGas, uint64(0)) assert.Greater(t, estimatedGas, uint64(0))
input := tt.input input := tt.input
input.TestEstimationMap = map[string]uint64{ input.TestEstimationMap = map[string]Estimation{
"randomName": 10000, "randomName": {10000, nil},
} }
estimatedGas, err = processor.EstimateGas(input) estimatedGas, err = processor.EstimateGas(input)
assert.Error(t, err) assert.Error(t, err)
assert.Equal(t, err, ErrNoEstimationFound) assert.Equal(t, ErrNoEstimationFound, err)
assert.Equal(t, uint64(0), estimatedGas) assert.Equal(t, uint64(0), estimatedGas)
} else { } else {
estimatedGas, err := processor.EstimateGas(tt.input) estimatedGas, err := processor.EstimateGas(tt.input)
assert.Error(t, err) assert.Error(t, err)
assert.Equal(t, err, ErrNoEstimationFound) assert.Equal(t, ErrNoEstimationFound, err)
assert.Equal(t, uint64(0), estimatedGas) assert.Equal(t, uint64(0), estimatedGas)
} }
}) })

View File

@ -69,7 +69,7 @@ func (s *TransferProcessor) EstimateGas(params ProcessorInputParams) (uint64, er
if params.TestsMode { if params.TestsMode {
if params.TestEstimationMap != nil { if params.TestEstimationMap != nil {
if val, ok := params.TestEstimationMap[s.Name()]; ok { if val, ok := params.TestEstimationMap[s.Name()]; ok {
return val, nil return val.Value, val.Err
} }
} }
return 0, ErrNoEstimationFound return 0, ErrNoEstimationFound

View File

@ -308,16 +308,35 @@ func (r *Router) GetPathProcessors() map[string]pathprocessor.PathProcessor {
return r.pathProcessors return r.pathProcessors
} }
func containsNetworkChainID(chainID uint64, chainIDs []uint64) bool { func arrayContainsElement[T comparable](el T, arr []T) bool {
for _, cID := range chainIDs { for _, e := range arr {
if cID == chainID { if e == el {
return true return true
} }
} }
return false return false
} }
func arraysWithSameElements[T comparable](ar1 []T, ar2 []T, isEqual func(T, T) bool) bool {
if len(ar1) != len(ar2) {
return false
}
for _, el := range ar1 {
if !arrayContainsElement(el, ar2) {
return false
}
}
return true
}
func sameSingleChainTransfer(fromChains []*params.Network, toChains []*params.Network) bool {
return len(fromChains) == 1 &&
len(toChains) == 1 &&
arraysWithSameElements(fromChains, toChains, func(a, b *params.Network) bool {
return a.ChainID == b.ChainID
})
}
type Router struct { type Router struct {
rpcClient *rpc.Client rpcClient *rpc.Client
tokenManager *token.Manager tokenManager *token.Manager
@ -484,7 +503,7 @@ func (r *Router) SuggestedRoutes(
continue continue
} }
if containsNetworkChainID(network.ChainID, disabledFromChainIDs) { if arrayContainsElement(network.ChainID, disabledFromChainIDs) {
continue continue
} }
@ -568,10 +587,10 @@ func (r *Router) SuggestedRoutes(
continue continue
} }
if len(preferedChainIDs) > 0 && !containsNetworkChainID(dest.ChainID, preferedChainIDs) { if len(preferedChainIDs) > 0 && !arrayContainsElement(dest.ChainID, preferedChainIDs) {
continue continue
} }
if containsNetworkChainID(dest.ChainID, disabledToChainIDs) { if arrayContainsElement(dest.ChainID, disabledToChainIDs) {
continue continue
} }

View File

@ -133,6 +133,10 @@ func (s SendType) canUseProcessor(p pathprocessor.PathProcessor) bool {
} }
} }
func (s SendType) simpleTransfer(p pathprocessor.PathProcessor) bool {
return s == Transfer && p.Name() == pathprocessor.ProcessorTransferName
}
func (s SendType) processZeroAmountInProcessor(amountIn *big.Int, amountOut *big.Int, processorName string) bool { func (s SendType) processZeroAmountInProcessor(amountIn *big.Int, amountOut *big.Int, processorName string) bool {
if amountIn.Cmp(pathprocessor.ZeroBigIntValue) == 0 { if amountIn.Cmp(pathprocessor.ZeroBigIntValue) == 0 {
if s == Transfer { if s == Transfer {

View File

@ -6,6 +6,7 @@ import (
"math" "math"
"math/big" "math/big"
"sort" "sort"
"strings"
"sync" "sync"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -21,6 +22,10 @@ import (
"github.com/status-im/status-go/signal" "github.com/status-im/status-go/signal"
) )
const (
hexAddressLength = 42
)
var ( var (
routerTask = async.TaskType{ routerTask = async.TaskType{
ID: 1, ID: 1,
@ -71,8 +76,8 @@ type RouteInputParams struct {
type routerTestParams struct { type routerTestParams struct {
tokenFrom *walletToken.Token tokenFrom *walletToken.Token
tokenPrices map[string]float64 tokenPrices map[string]float64
estimationMap map[string]uint64 // [processor-name, estimated-value] estimationMap map[string]pathprocessor.Estimation // [processor-name, estimation]
bonderFeeMap map[string]*big.Int // [token-symbol, bonder-fee] bonderFeeMap map[string]*big.Int // [token-symbol, bonder-fee]
suggestedFees *SuggestedFees suggestedFees *SuggestedFees
baseFee *big.Int baseFee *big.Int
balanceMap map[string]*big.Int // [token-symbol, balance] balanceMap map[string]*big.Int // [token-symbol, balance]
@ -407,7 +412,7 @@ func validateFromLockedAmount(input *RouteInputParams) error {
excludedChainCount := 0 excludedChainCount := 0
for chainID, amount := range input.FromLockedAmount { for chainID, amount := range input.FromLockedAmount {
if containsNetworkChainID(chainID, input.DisabledFromChainIDs) { if arrayContainsElement(chainID, input.DisabledFromChainIDs) {
return ErrDisabledChainFoundAmongLockedNetworks return ErrDisabledChainFoundAmongLockedNetworks
} }
@ -500,8 +505,21 @@ func (r *Router) SuggestedRoutesV2(ctx context.Context, input *RouteInputParams)
balanceMap, err := r.getBalanceMapForTokenOnChains(ctx, input, selectedFromChains) balanceMap, err := r.getBalanceMapForTokenOnChains(ctx, input, selectedFromChains)
// return only if there are no balances, otherwise try to resolve the candidates for chains we know the balances for // return only if there are no balances, otherwise try to resolve the candidates for chains we know the balances for
if len(balanceMap) == 0 && err != nil { if len(balanceMap) == 0 {
return nil, errors.CreateErrorResponseFromError(err) if err != nil {
return nil, errors.CreateErrorResponseFromError(err)
}
} else {
noBalanceOnAnyChain := true
for _, value := range balanceMap {
if value.Cmp(pathprocessor.ZeroBigIntValue) > 0 {
noBalanceOnAnyChain = false
break
}
}
if noBalanceOnAnyChain {
return nil, ErrNoPositiveBalance
}
} }
candidates, processorErrors, err := r.resolveCandidates(ctx, input, selectedFromChains, selectedToChains, balanceMap) candidates, processorErrors, err := r.resolveCandidates(ctx, input, selectedFromChains, selectedToChains, balanceMap)
@ -530,7 +548,23 @@ func (r *Router) SuggestedRoutesV2(ctx context.Context, input *RouteInputParams)
} }
} }
return suggestedRoutes, err mapError := func(err error) error {
if err == nil {
return nil
}
pattern := "insufficient funds for gas * price + value: address "
addressIndex := strings.Index(errors.DetailsFromError(err), pattern)
if addressIndex != -1 {
addressIndex += len(pattern) + hexAddressLength
return errors.CreateErrorResponseFromError(&errors.ErrorResponse{
Code: errors.ErrorCodeFromError(err),
Details: errors.DetailsFromError(err)[:addressIndex],
})
}
return err
}
// map some errors to more user-friendly messages
return suggestedRoutes, mapError(err)
} }
// getBalanceMapForTokenOnChains returns the balance map for passed address, where the key is in format "chainID-tokenSymbol" and // getBalanceMapForTokenOnChains returns the balance map for passed address, where the key is in format "chainID-tokenSymbol" and
@ -584,6 +618,10 @@ func (r *Router) getBalanceMapForTokenOnChains(ctx context.Context, input *Route
balanceMap[makeBalanceKey(chain.ChainID, token.Symbol)] = tokenBalance balanceMap[makeBalanceKey(chain.ChainID, token.Symbol)] = tokenBalance
} }
if token.IsNative() {
continue
}
// add native token balance for the chain // add native token balance for the chain
nativeBalance, err := r.getBalance(ctx, chain.ChainID, nativeToken, input.AddrFrom) nativeBalance, err := r.getBalance(ctx, chain.ChainID, nativeToken, input.AddrFrom)
if err != nil { if err != nil {
@ -756,11 +794,11 @@ func (r *Router) getSelectedChains(input *RouteInputParams) (selectedFromChains
continue continue
} }
if !containsNetworkChainID(network.ChainID, input.DisabledFromChainIDs) { if !arrayContainsElement(network.ChainID, input.DisabledFromChainIDs) {
selectedFromChains = append(selectedFromChains, network) selectedFromChains = append(selectedFromChains, network)
} }
if !containsNetworkChainID(network.ChainID, input.DisabledToChainIDs) { if !arrayContainsElement(network.ChainID, input.DisabledToChainIDs) {
selectedToChains = append(selectedToChains, network) selectedToChains = append(selectedToChains, network)
} }
} }
@ -781,8 +819,8 @@ func (r *Router) resolveCandidates(ctx context.Context, input *RouteInputParams,
return nil, nil, errors.CreateErrorResponseFromError(err) return nil, nil, errors.CreateErrorResponseFromError(err)
} }
appendProcessorErrorFn := func(processorName string, err error) { appendProcessorErrorFn := func(processorName string, sendType SendType, fromChainID uint64, toChainID uint64, amount *big.Int, err error) {
log.Error("routerv2.resolveCandidates error", "processor", processorName, "err", err) log.Error("routerv2.resolveCandidates error", "processor", processorName, "sendType", sendType, "fromChainId: ", fromChainID, "toChainId", toChainID, "amount", amount, "err", err)
mu.Lock() mu.Lock()
defer mu.Unlock() defer mu.Unlock()
processorErrors = append(processorErrors, &ProcessorError{ processorErrors = append(processorErrors, &ProcessorError{
@ -855,6 +893,11 @@ func (r *Router) resolveCandidates(ctx context.Context, input *RouteInputParams,
continue continue
} }
// if just a single from and to chain is selected for transfer, we can skip the bridge as potential path
if !input.SendType.simpleTransfer(pProcessor) && sameSingleChainTransfer(selectedFromChains, selectedToChains) {
continue
}
if !input.SendType.processZeroAmountInProcessor(amountOption.amount, input.AmountOut.ToInt(), pProcessor.Name()) { if !input.SendType.processZeroAmountInProcessor(amountOption.amount, input.AmountOut.ToInt(), pProcessor.Name()) {
continue continue
} }
@ -893,7 +936,7 @@ func (r *Router) resolveCandidates(ctx context.Context, input *RouteInputParams,
can, err := pProcessor.AvailableFor(processorInputParams) can, err := pProcessor.AvailableFor(processorInputParams)
if err != nil { if err != nil {
appendProcessorErrorFn(pProcessor.Name(), err) appendProcessorErrorFn(pProcessor.Name(), input.SendType, processorInputParams.FromChain.ChainID, processorInputParams.ToChain.ChainID, processorInputParams.AmountIn, err)
continue continue
} }
if !can { if !can {
@ -902,24 +945,24 @@ func (r *Router) resolveCandidates(ctx context.Context, input *RouteInputParams,
bonderFees, tokenFees, err := pProcessor.CalculateFees(processorInputParams) bonderFees, tokenFees, err := pProcessor.CalculateFees(processorInputParams)
if err != nil { if err != nil {
appendProcessorErrorFn(pProcessor.Name(), err) appendProcessorErrorFn(pProcessor.Name(), input.SendType, processorInputParams.FromChain.ChainID, processorInputParams.ToChain.ChainID, processorInputParams.AmountIn, err)
continue continue
} }
gasLimit, err := pProcessor.EstimateGas(processorInputParams) gasLimit, err := pProcessor.EstimateGas(processorInputParams)
if err != nil { if err != nil {
appendProcessorErrorFn(pProcessor.Name(), err) appendProcessorErrorFn(pProcessor.Name(), input.SendType, processorInputParams.FromChain.ChainID, processorInputParams.ToChain.ChainID, processorInputParams.AmountIn, err)
continue continue
} }
approvalContractAddress, err := pProcessor.GetContractAddress(processorInputParams) approvalContractAddress, err := pProcessor.GetContractAddress(processorInputParams)
if err != nil { if err != nil {
appendProcessorErrorFn(pProcessor.Name(), err) appendProcessorErrorFn(pProcessor.Name(), input.SendType, processorInputParams.FromChain.ChainID, processorInputParams.ToChain.ChainID, processorInputParams.AmountIn, err)
continue continue
} }
approvalRequired, approvalAmountRequired, approvalGasLimit, l1ApprovalFee, err := r.requireApproval(ctx, input.SendType, &approvalContractAddress, processorInputParams) approvalRequired, approvalAmountRequired, approvalGasLimit, l1ApprovalFee, err := r.requireApproval(ctx, input.SendType, &approvalContractAddress, processorInputParams)
if err != nil { if err != nil {
appendProcessorErrorFn(pProcessor.Name(), err) appendProcessorErrorFn(pProcessor.Name(), input.SendType, processorInputParams.FromChain.ChainID, processorInputParams.ToChain.ChainID, processorInputParams.AmountIn, err)
continue continue
} }
@ -936,7 +979,7 @@ func (r *Router) resolveCandidates(ctx context.Context, input *RouteInputParams,
amountOut, err := pProcessor.CalculateAmountOut(processorInputParams) amountOut, err := pProcessor.CalculateAmountOut(processorInputParams)
if err != nil { if err != nil {
appendProcessorErrorFn(pProcessor.Name(), err) appendProcessorErrorFn(pProcessor.Name(), input.SendType, processorInputParams.FromChain.ChainID, processorInputParams.ToChain.ChainID, processorInputParams.AmountIn, err)
continue continue
} }
@ -1041,7 +1084,7 @@ func (r *Router) resolveCandidates(ctx context.Context, input *RouteInputParams,
return candidates, processorErrors, nil return candidates, processorErrors, nil
} }
func (r *Router) checkBalancesForTheBestRoute(ctx context.Context, bestRoute []*PathV2, input *RouteInputParams, balanceMap map[string]*big.Int) (hasPositiveBalance bool, err error) { func (r *Router) checkBalancesForTheBestRoute(ctx context.Context, bestRoute []*PathV2, balanceMap map[string]*big.Int) (hasPositiveBalance bool, err error) {
balanceMapCopy := copyMapGeneric(balanceMap, func(v interface{}) interface{} { balanceMapCopy := 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)
@ -1182,7 +1225,7 @@ func (r *Router) resolveRoutes(ctx context.Context, input *RouteInputParams, can
for len(allRoutes) > 0 { for len(allRoutes) > 0 {
bestRoute = findBestV2(allRoutes, tokenPrice, nativeTokenPrice) bestRoute = findBestV2(allRoutes, tokenPrice, nativeTokenPrice)
var hasPositiveBalance bool var hasPositiveBalance bool
hasPositiveBalance, err = r.checkBalancesForTheBestRoute(ctx, bestRoute, input, balanceMap) hasPositiveBalance, err = r.checkBalancesForTheBestRoute(ctx, bestRoute, balanceMap)
if err != nil { 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 // If it's about transfer or bridge and there is more routes, but on the best (cheapest) one there is not enugh balance

View File

@ -216,8 +216,12 @@ func TestNoBalanceForTheBestRouteRouterV2(t *testing.T) {
if tt.expectedError != nil { if tt.expectedError != nil {
assert.Error(t, err) assert.Error(t, err)
assert.Equal(t, tt.expectedError.Error(), err.Error()) assert.Equal(t, tt.expectedError.Error(), err.Error())
assert.NotNil(t, routes) if tt.expectedError == ErrNoPositiveBalance {
assertPathsEqual(t, tt.expectedCandidates, routes.Candidates) assert.Nil(t, routes)
} else {
assert.NotNil(t, routes)
assertPathsEqual(t, tt.expectedCandidates, routes.Candidates)
}
} else { } else {
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, len(tt.expectedCandidates), len(routes.Candidates)) assert.Equal(t, len(tt.expectedCandidates), len(routes.Candidates))

View File

@ -45,9 +45,9 @@ const (
) )
var ( var (
testEstimationMap = map[string]uint64{ testEstimationMap = map[string]pathprocessor.Estimation{
pathprocessor.ProcessorTransferName: uint64(1000), pathprocessor.ProcessorTransferName: {Value: uint64(1000), Err: nil},
pathprocessor.ProcessorBridgeHopName: uint64(5000), pathprocessor.ProcessorBridgeHopName: {Value: uint64(5000), Err: nil},
} }
testBbonderFeeMap = map[string]*big.Int{ testBbonderFeeMap = map[string]*big.Int{
@ -208,6 +208,46 @@ type normalTestParams struct {
func getNormalTestParamsList() []normalTestParams { func getNormalTestParamsList() []normalTestParams {
return []normalTestParams{ return []normalTestParams{
{
name: "ETH transfer - Insufficient Funds",
input: &RouteInputParams{
testnetMode: false,
Uuid: uuid.NewString(),
SendType: Transfer,
AddrFrom: common.HexToAddress("0x1"),
AddrTo: common.HexToAddress("0x2"),
AmountIn: (*hexutil.Big)(big.NewInt(testAmount1ETHInWei)),
TokenID: pathprocessor.EthSymbol,
DisabledFromChainIDs: []uint64{walletCommon.OptimismMainnet, walletCommon.ArbitrumMainnet},
DisabledToChainIDs: []uint64{walletCommon.OptimismMainnet, walletCommon.ArbitrumMainnet},
testsMode: true,
testParams: &routerTestParams{
tokenFrom: &token.Token{
ChainID: 1,
Symbol: pathprocessor.EthSymbol,
Decimals: 18,
},
tokenPrices: testTokenPrices,
suggestedFees: testSuggestedFees,
balanceMap: testBalanceMapPerChain,
estimationMap: map[string]pathprocessor.Estimation{
pathprocessor.ProcessorTransferName: {
Value: uint64(0),
Err: fmt.Errorf("failed with 50000000 gas: insufficient funds for gas * price + value: address %s have 68251537427723 want 100000000000000", common.HexToAddress("0x1")),
},
},
bonderFeeMap: testBbonderFeeMap,
approvalGasEstimation: testApprovalGasEstimation,
approvalL1Fee: testApprovalL1Fee,
},
},
expectedError: &errors.ErrorResponse{
Code: errors.GenericErrorCode,
Details: fmt.Sprintf("failed with 50000000 gas: insufficient funds for gas * price + value: address %s", common.HexToAddress("0x1")),
},
expectedCandidates: []*PathV2{},
},
{ {
name: "ETH transfer - No Specific FromChain - No Specific ToChain - 0 AmountIn", name: "ETH transfer - No Specific FromChain - No Specific ToChain - 0 AmountIn",
input: &RouteInputParams{ input: &RouteInputParams{
@ -2669,20 +2709,7 @@ func getNoBalanceTestParamsList() []noBalanceTestParams {
approvalL1Fee: testApprovalL1Fee, approvalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedError: &errors.ErrorResponse{ expectedError: ErrNoPositiveBalance,
Code: ErrNotEnoughTokenBalance.Code,
Details: fmt.Sprintf(ErrNotEnoughTokenBalance.Details, pathprocessor.UsdcSymbol, walletCommon.OptimismMainnet),
},
expectedCandidates: []*PathV2{
{
ProcessorName: pathprocessor.ProcessorTransferName,
FromChain: &optimism,
ToChain: &optimism,
ApprovalRequired: false,
requiredTokenBalance: big.NewInt(testAmount100USDC),
requiredNativeBalance: big.NewInt((testBaseFee + testPriorityFeeLow) * testApprovalGasEstimation),
},
},
}, },
{ {
name: "ERC20 transfer - Specific FromChain - Specific ToChain - Not Enough Native Balance", name: "ERC20 transfer - Specific FromChain - Specific ToChain - Not Enough Native Balance",
@ -2766,30 +2793,7 @@ func getNoBalanceTestParamsList() []noBalanceTestParams {
approvalL1Fee: testApprovalL1Fee, approvalL1Fee: testApprovalL1Fee,
}, },
}, },
expectedError: &errors.ErrorResponse{ expectedError: ErrNoPositiveBalance,
Code: ErrNotEnoughTokenBalance.Code,
Details: fmt.Sprintf(ErrNotEnoughTokenBalance.Details, pathprocessor.UsdcSymbol, walletCommon.ArbitrumMainnet),
},
expectedCandidates: []*PathV2{
{
ProcessorName: pathprocessor.ProcessorBridgeHopName,
FromChain: &mainnet,
ToChain: &optimism,
ApprovalRequired: true,
},
{
ProcessorName: pathprocessor.ProcessorTransferName,
FromChain: &optimism,
ToChain: &optimism,
ApprovalRequired: false,
},
{
ProcessorName: pathprocessor.ProcessorBridgeHopName,
FromChain: &arbitrum,
ToChain: &optimism,
ApprovalRequired: true,
},
},
}, },
{ {
name: "ERC20 transfer - No Specific FromChain - Specific ToChain - Enough Token Balance On Arbitrum Chain But Not Enough Native Balance", name: "ERC20 transfer - No Specific FromChain - Specific ToChain - Enough Token Balance On Arbitrum Chain But Not Enough Native Balance",