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

     1  package requests
     2  
     3  import (
     4  	"math/big"
     5  
     6  	"github.com/ethereum/go-ethereum/common"
     7  	"github.com/ethereum/go-ethereum/common/hexutil"
     8  	"github.com/status-im/status-go/errors"
     9  	"github.com/status-im/status-go/services/ens"
    10  	walletCommon "github.com/status-im/status-go/services/wallet/common"
    11  	"github.com/status-im/status-go/services/wallet/router/fees"
    12  	"github.com/status-im/status-go/services/wallet/router/pathprocessor"
    13  	"github.com/status-im/status-go/services/wallet/router/sendtype"
    14  	"github.com/status-im/status-go/services/wallet/token"
    15  )
    16  
    17  var (
    18  	ErrENSRegisterRequiresUsernameAndPubKey      = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-001"), Details: "username and public key are required for ENSRegister"}
    19  	ErrENSRegisterTestnetSTTOnly                 = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-002"), Details: "only STT is supported for ENSRegister on testnet"}
    20  	ErrENSRegisterMainnetSNTOnly                 = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-003"), Details: "only SNT is supported for ENSRegister on mainnet"}
    21  	ErrENSReleaseRequiresUsername                = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-004"), Details: "username is required for ENSRelease"}
    22  	ErrENSSetPubKeyRequiresUsernameAndPubKey     = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-005"), Details: "username and public key are required for ENSSetPubKey"}
    23  	ErrStickersBuyRequiresPackID                 = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-006"), Details: "packID is required for StickersBuy"}
    24  	ErrSwapRequiresToTokenID                     = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-007"), Details: "toTokenID is required for Swap"}
    25  	ErrSwapTokenIDMustBeDifferent                = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-008"), Details: "tokenID and toTokenID must be different"}
    26  	ErrSwapAmountInAmountOutMustBeExclusive      = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-009"), Details: "only one of amountIn or amountOut can be set"}
    27  	ErrSwapAmountInMustBePositive                = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-010"), Details: "amountIn must be positive"}
    28  	ErrSwapAmountOutMustBePositive               = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-011"), Details: "amountOut must be positive"}
    29  	ErrLockedAmountNotSupportedForNetwork        = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-012"), Details: "locked amount is not supported for the selected network"}
    30  	ErrLockedAmountNotNegative                   = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-013"), Details: "locked amount must not be negative"}
    31  	ErrLockedAmountExceedsTotalSendAmount        = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-014"), Details: "locked amount exceeds the total amount to send"}
    32  	ErrLockedAmountLessThanSendAmountAllNetworks = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-015"), Details: "locked amount is less than the total amount to send, but all networks are locked"}
    33  	ErrDisabledChainFoundAmongLockedNetworks     = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-016"), Details: "disabled chain found among locked networks"}
    34  	ErrENSSetPubKeyInvalidUsername               = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-017"), Details: "a valid username, ending in '.eth', is required for ENSSetPubKey"}
    35  	ErrLockedAmountExcludesAllSupported          = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-018"), Details: "all supported chains are excluded, routing impossible"}
    36  	ErrCannotCheckLockedAmounts                  = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-019"), Details: "cannot check locked amounts"}
    37  )
    38  
    39  type RouteInputParams struct {
    40  	Uuid                 string                  `json:"uuid"`
    41  	SendType             sendtype.SendType       `json:"sendType" validate:"required"`
    42  	AddrFrom             common.Address          `json:"addrFrom" validate:"required"`
    43  	AddrTo               common.Address          `json:"addrTo" validate:"required"`
    44  	AmountIn             *hexutil.Big            `json:"amountIn" validate:"required"`
    45  	AmountOut            *hexutil.Big            `json:"amountOut"`
    46  	TokenID              string                  `json:"tokenID" validate:"required"`
    47  	ToTokenID            string                  `json:"toTokenID"`
    48  	DisabledFromChainIDs []uint64                `json:"disabledFromChainIDs"`
    49  	DisabledToChainIDs   []uint64                `json:"disabledToChainIDs"`
    50  	GasFeeMode           fees.GasFeeMode         `json:"gasFeeMode" validate:"required"`
    51  	FromLockedAmount     map[uint64]*hexutil.Big `json:"fromLockedAmount"`
    52  	TestnetMode          bool
    53  
    54  	// For send types like EnsRegister, EnsRelease, EnsSetPubKey, StickersBuy
    55  	Username  string       `json:"username"`
    56  	PublicKey string       `json:"publicKey"`
    57  	PackID    *hexutil.Big `json:"packID"`
    58  
    59  	// TODO: Remove two fields below once we implement a better solution for tests
    60  	// Currently used for tests only
    61  	TestsMode  bool
    62  	TestParams *RouterTestParams
    63  }
    64  
    65  type RouterTestParams struct {
    66  	TokenFrom             *token.Token
    67  	TokenPrices           map[string]float64
    68  	EstimationMap         map[string]pathprocessor.Estimation // [processor-name, estimation]
    69  	BonderFeeMap          map[string]*big.Int                 // [token-symbol, bonder-fee]
    70  	SuggestedFees         *fees.SuggestedFees
    71  	BaseFee               *big.Int
    72  	BalanceMap            map[string]*big.Int // [token-symbol, balance]
    73  	ApprovalGasEstimation uint64
    74  	ApprovalL1Fee         uint64
    75  }
    76  
    77  func (i *RouteInputParams) Validate() error {
    78  	if i.SendType == sendtype.ENSRegister {
    79  		if i.Username == "" || i.PublicKey == "" {
    80  			return ErrENSRegisterRequiresUsernameAndPubKey
    81  		}
    82  		if i.TestnetMode {
    83  			if i.TokenID != pathprocessor.SttSymbol {
    84  				return ErrENSRegisterTestnetSTTOnly
    85  			}
    86  		} else {
    87  			if i.TokenID != pathprocessor.SntSymbol {
    88  				return ErrENSRegisterMainnetSNTOnly
    89  			}
    90  		}
    91  		return nil
    92  	}
    93  
    94  	if i.SendType == sendtype.ENSRelease {
    95  		if i.Username == "" {
    96  			return ErrENSReleaseRequiresUsername
    97  		}
    98  	}
    99  
   100  	if i.SendType == sendtype.ENSSetPubKey {
   101  		if i.Username == "" || i.PublicKey == "" {
   102  			return ErrENSSetPubKeyRequiresUsernameAndPubKey
   103  		}
   104  
   105  		if ens.ValidateENSUsername(i.Username) != nil {
   106  			return ErrENSSetPubKeyInvalidUsername
   107  		}
   108  	}
   109  
   110  	if i.SendType == sendtype.StickersBuy {
   111  		if i.PackID == nil {
   112  			return ErrStickersBuyRequiresPackID
   113  		}
   114  	}
   115  
   116  	if i.SendType == sendtype.Swap {
   117  		if i.ToTokenID == "" {
   118  			return ErrSwapRequiresToTokenID
   119  		}
   120  		if i.TokenID == i.ToTokenID {
   121  			return ErrSwapTokenIDMustBeDifferent
   122  		}
   123  
   124  		if i.AmountIn != nil &&
   125  			i.AmountOut != nil &&
   126  			i.AmountIn.ToInt().Cmp(pathprocessor.ZeroBigIntValue) > 0 &&
   127  			i.AmountOut.ToInt().Cmp(pathprocessor.ZeroBigIntValue) > 0 {
   128  			return ErrSwapAmountInAmountOutMustBeExclusive
   129  		}
   130  
   131  		if i.AmountIn != nil && i.AmountIn.ToInt().Sign() < 0 {
   132  			return ErrSwapAmountInMustBePositive
   133  		}
   134  
   135  		if i.AmountOut != nil && i.AmountOut.ToInt().Sign() < 0 {
   136  			return ErrSwapAmountOutMustBePositive
   137  		}
   138  	}
   139  
   140  	return i.validateFromLockedAmount()
   141  }
   142  
   143  func (i *RouteInputParams) validateFromLockedAmount() error {
   144  	if i.FromLockedAmount == nil || len(i.FromLockedAmount) == 0 {
   145  		return nil
   146  	}
   147  
   148  	var suppNetworks map[uint64]bool
   149  	if i.TestnetMode {
   150  		suppNetworks = walletCommon.CopyMapGeneric(walletCommon.SupportedTestNetworks, nil).(map[uint64]bool)
   151  	} else {
   152  		suppNetworks = walletCommon.CopyMapGeneric(walletCommon.SupportedNetworks, nil).(map[uint64]bool)
   153  	}
   154  
   155  	if suppNetworks == nil {
   156  		return ErrCannotCheckLockedAmounts
   157  	}
   158  
   159  	totalLockedAmount := big.NewInt(0)
   160  	excludedChainCount := 0
   161  
   162  	for chainID, amount := range i.FromLockedAmount {
   163  		if walletCommon.ArrayContainsElement(chainID, i.DisabledFromChainIDs) {
   164  			return ErrDisabledChainFoundAmongLockedNetworks
   165  		}
   166  
   167  		if i.TestnetMode {
   168  			if !walletCommon.SupportedTestNetworks[chainID] {
   169  				return ErrLockedAmountNotSupportedForNetwork
   170  			}
   171  		} else {
   172  			if !walletCommon.SupportedNetworks[chainID] {
   173  				return ErrLockedAmountNotSupportedForNetwork
   174  			}
   175  		}
   176  
   177  		if amount == nil || amount.ToInt().Sign() < 0 {
   178  			return ErrLockedAmountNotNegative
   179  		}
   180  
   181  		if !(amount.ToInt().Sign() > 0) {
   182  			excludedChainCount++
   183  		}
   184  		delete(suppNetworks, chainID)
   185  		totalLockedAmount = new(big.Int).Add(totalLockedAmount, amount.ToInt())
   186  	}
   187  
   188  	if (!i.TestnetMode && excludedChainCount == len(walletCommon.SupportedNetworks)) ||
   189  		(i.TestnetMode && excludedChainCount == len(walletCommon.SupportedTestNetworks)) {
   190  		return ErrLockedAmountExcludesAllSupported
   191  	}
   192  
   193  	if totalLockedAmount.Cmp(i.AmountIn.ToInt()) > 0 {
   194  		return ErrLockedAmountExceedsTotalSendAmount
   195  	}
   196  	if totalLockedAmount.Cmp(i.AmountIn.ToInt()) < 0 && len(suppNetworks) == 0 {
   197  		return ErrLockedAmountLessThanSendAmountAllNetworks
   198  	}
   199  	return nil
   200  }