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:
parent
85fba77b7d
commit
9b9a91f654
|
@ -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 {
|
||||||
|
|
|
@ -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"}
|
||||||
)
|
)
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Loading…
Reference in New Issue