chore!: router code organization improvements

It's a breaking change due to errors' changes, the list of mapped old error codes:
- `"WR-001"` is now `"WRR-001"`
- `"WR-002"` is now `"WRR-002"`
- `"WR-003"` is now `"WRR-003"`
- `"WR-004"` is now `"WRR-004"`
- `"WR-005"` is now `"WRR-005"`
- `"WR-006"` is now `"WRR-006"`
- `"WR-007"` is now `"WRR-007"`
- `"WR-008"` is now `"WRR-008"`
- `"WR-009"` is now `"WRR-009"`
- `"WR-010"` is now `"WRR-010"`
- `"WR-011"` is now `"WRR-011"`
- `"WR-012"` is now `"WRR-012"`
- `"WR-013"` is now `"WRR-013"`
- `"WR-014"` is now `"WRR-014"`
- `"WR-015"` is now `"WRR-015"`
- `"WR-019"` is now `"WRR-016"`
- `"WR-020"` is now `"WRR-017"`
- `"WR-021"` is now `"WRR-018"`
- `"WR-025"` is now `"WRR-019"`

- `"WR-016"` is now `"WR-001"`
- `"WR-017"` is now `"WR-002"`
- `"WR-018"` is now `"WR-003"`
- `"WR-022"` is now `"WR-004"`
- `"WR-023"` is now `"WR-005"`
- `"WR-024"` is now `"WR-006"`
- `"WR-026"` is now `"WR-007"`
- `"WR-027"` is now `"WR-008"`

Other changes:
- `RouteInputParams` type moved to `requests` package and code updated accordingly
- `SuggestedFees` type moved to a new `fees` package and code updated accordingly
- `SendType` type moved to a new `sendtype` package and code updated accordingly
- the following functions moved to `common` package
  - `ArrayContainsElement`
  - `ArraysWithSameElements`
  - `SameSingleChainTransfer`
  - `CopyMapGeneric`
  - `GweiToEth`
  - `WeiToGwei`
- the following consts moved to `common` package
  - `HexAddressLength`
  - `SupportedNetworks`
  - `SupportedTestNetworks`
This commit is contained in:
Sale Djenic 2024-08-28 13:17:59 +02:00 committed by saledjenic
parent d10d492515
commit 00559692bc
17 changed files with 1371 additions and 1337 deletions

View File

@ -18,13 +18,13 @@ import (
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/services/wallet/bigint"
"github.com/status-im/status-go/services/wallet/router"
"github.com/status-im/status-go/services/wallet/router/fees"
"github.com/status-im/status-go/transactions"
)
type CommunityTokenFees struct {
GasUnits uint64 `json:"gasUnits"`
SuggestedFees *router.SuggestedFeesGwei `json:"suggestedFees"`
SuggestedFees *fees.SuggestedFeesGwei `json:"suggestedFees"`
}
func weiToGwei(val *big.Int) *big.Float {
@ -373,7 +373,7 @@ func (s *Service) prepareCommunityTokenFees(ctx context.Context, from common.Add
}, nil
}
func (s *Service) suggestedFeesToSendTxArgs(from common.Address, to *common.Address, gas uint64, suggestedFees *router.SuggestedFeesGwei) transactions.SendTxArgs {
func (s *Service) suggestedFeesToSendTxArgs(from common.Address, to *common.Address, gas uint64, suggestedFees *fees.SuggestedFeesGwei) transactions.SendTxArgs {
sendArgs := transactions.SendTxArgs{}
sendArgs.From = types.Address(from)
sendArgs.To = (*types.Address)(to)

View File

@ -32,7 +32,7 @@ import (
"github.com/status-im/status-go/services/utils"
"github.com/status-im/status-go/services/wallet/bigint"
wcommon "github.com/status-im/status-go/services/wallet/common"
"github.com/status-im/status-go/services/wallet/router"
"github.com/status-im/status-go/services/wallet/router/fees"
"github.com/status-im/status-go/services/wallet/walletevent"
"github.com/status-im/status-go/signal"
"github.com/status-im/status-go/transactions"
@ -49,7 +49,7 @@ type Service struct {
walletFeed *event.Feed
walletWatcher *walletevent.Watcher
transactor *transactions.Transactor
feeManager *router.FeeManager
feeManager *fees.FeeManager
}
// Returns a new Collectibles Service.
@ -63,7 +63,7 @@ func NewService(rpcClient *rpc.Client, accountsManager *account.GethManager, pen
db: communitytokensdatabase.NewCommunityTokensDatabase(appDb),
walletFeed: walletFeed,
transactor: transactor,
feeManager: &router.FeeManager{RPCClient: rpcClient},
feeManager: &fees.FeeManager{RPCClient: rpcClient},
}
}

View File

@ -31,6 +31,7 @@ import (
"github.com/status-im/status-go/services/wallet/onramp"
"github.com/status-im/status-go/services/wallet/requests"
"github.com/status-im/status-go/services/wallet/router"
"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/thirdparty"
"github.com/status-im/status-go/services/wallet/token"
@ -468,7 +469,7 @@ func (api *API) FetchTokenDetails(ctx context.Context, symbols []string) (map[st
return api.s.marketManager.FetchTokenDetails(symbols)
}
func (api *API) GetSuggestedFees(ctx context.Context, chainID uint64) (*router.SuggestedFeesGwei, error) {
func (api *API) GetSuggestedFees(ctx context.Context, chainID uint64) (*fees.SuggestedFeesGwei, error) {
log.Debug("call to GetSuggestedFees")
return api.router.GetFeesManager().SuggestedFeesGwei(ctx, chainID)
}
@ -478,7 +479,7 @@ func (api *API) GetEstimatedLatestBlockNumber(ctx context.Context, chainID uint6
return api.s.blockChainState.GetEstimatedLatestBlockNumber(ctx, chainID)
}
func (api *API) GetTransactionEstimatedTime(ctx context.Context, chainID uint64, maxFeePerGas *big.Float) (router.TransactionEstimation, error) {
func (api *API) GetTransactionEstimatedTime(ctx context.Context, chainID uint64, maxFeePerGas *big.Float) (fees.TransactionEstimation, error) {
log.Debug("call to getTransactionEstimatedTime")
return api.router.GetFeesManager().TransactionEstimatedTime(ctx, chainID, gweiToWei(maxFeePerGas)), nil
}
@ -488,13 +489,13 @@ func gweiToWei(val *big.Float) *big.Int {
return res
}
func (api *API) GetSuggestedRoutes(ctx context.Context, input *router.RouteInputParams) (*router.SuggestedRoutes, error) {
func (api *API) GetSuggestedRoutes(ctx context.Context, input *requests.RouteInputParams) (*router.SuggestedRoutes, error) {
log.Debug("call to GetSuggestedRoutes")
return api.router.SuggestedRoutes(ctx, input)
}
func (api *API) GetSuggestedRoutesAsync(ctx context.Context, input *router.RouteInputParams) {
func (api *API) GetSuggestedRoutesAsync(ctx context.Context, input *requests.RouteInputParams) {
log.Debug("call to GetSuggestedRoutesAsync")
api.router.SuggestedRoutesAsync(input)

View File

@ -11,6 +11,7 @@ type MultiTransactionIDType int64
const (
NoMultiTransactionID = MultiTransactionIDType(0)
HexAddressLength = 42
)
type ChainID uint64
@ -32,6 +33,18 @@ const (
var (
ZeroAddress = ethCommon.HexToAddress("0x0000000000000000000000000000000000000000")
SupportedNetworks = map[uint64]bool{
EthereumMainnet: true,
OptimismMainnet: true,
ArbitrumMainnet: true,
}
SupportedTestNetworks = map[uint64]bool{
EthereumSepolia: true,
OptimismSepolia: true,
ArbitrumSepolia: true,
}
)
type ContractType byte

View File

@ -2,7 +2,10 @@ package common
import (
"context"
"math/big"
"reflect"
gethParams "github.com/ethereum/go-ethereum/params"
"github.com/status-im/status-go/params"
)
@ -24,3 +27,51 @@ func NetworksToChainIDs(networks []*params.Network) []uint64 {
return chainIDs
}
func ArrayContainsElement[T comparable](el T, arr []T) bool {
for _, e := range arr {
if e == el {
return true
}
}
return false
}
func IsSingleChainOperation(fromChains []*params.Network, toChains []*params.Network) bool {
return len(fromChains) == 1 &&
len(toChains) == 1 &&
fromChains[0].ChainID == toChains[0].ChainID
}
// CopyMapGeneric creates a copy of any map, if the deepCopyValue function is provided, it will be used to copy values.
func CopyMapGeneric(original interface{}, deepCopyValueFn func(interface{}) interface{}) interface{} {
originalVal := reflect.ValueOf(original)
if originalVal.Kind() != reflect.Map {
return nil
}
newMap := reflect.MakeMap(originalVal.Type())
for iter := originalVal.MapRange(); iter.Next(); {
if deepCopyValueFn != nil {
newMap.SetMapIndex(iter.Key(), reflect.ValueOf(deepCopyValueFn(iter.Value().Interface())))
} else {
newMap.SetMapIndex(iter.Key(), iter.Value())
}
}
return newMap.Interface()
}
func GweiToEth(val *big.Float) *big.Float {
return new(big.Float).Quo(val, big.NewFloat(1000000000))
}
func WeiToGwei(val *big.Int) *big.Float {
result := new(big.Float)
result.SetInt(val)
unit := new(big.Int)
unit.SetInt64(gethParams.GWei)
return result.Quo(result, new(big.Float).SetInt(unit))
}

View File

@ -0,0 +1,200 @@
package requests
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/status-im/status-go/errors"
"github.com/status-im/status-go/services/ens"
walletCommon "github.com/status-im/status-go/services/wallet/common"
"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/sendtype"
"github.com/status-im/status-go/services/wallet/token"
)
var (
ErrENSRegisterRequiresUsernameAndPubKey = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-001"), Details: "username and public key are required for ENSRegister"}
ErrENSRegisterTestnetSTTOnly = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-002"), Details: "only STT is supported for ENSRegister on testnet"}
ErrENSRegisterMainnetSNTOnly = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-003"), Details: "only SNT is supported for ENSRegister on mainnet"}
ErrENSReleaseRequiresUsername = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-004"), Details: "username is required for ENSRelease"}
ErrENSSetPubKeyRequiresUsernameAndPubKey = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-005"), Details: "username and public key are required for ENSSetPubKey"}
ErrStickersBuyRequiresPackID = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-006"), Details: "packID is required for StickersBuy"}
ErrSwapRequiresToTokenID = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-007"), Details: "toTokenID is required for Swap"}
ErrSwapTokenIDMustBeDifferent = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-008"), Details: "tokenID and toTokenID must be different"}
ErrSwapAmountInAmountOutMustBeExclusive = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-009"), Details: "only one of amountIn or amountOut can be set"}
ErrSwapAmountInMustBePositive = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-010"), Details: "amountIn must be positive"}
ErrSwapAmountOutMustBePositive = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-011"), Details: "amountOut must be positive"}
ErrLockedAmountNotSupportedForNetwork = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-012"), Details: "locked amount is not supported for the selected network"}
ErrLockedAmountNotNegative = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-013"), Details: "locked amount must not be negative"}
ErrLockedAmountExceedsTotalSendAmount = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-014"), Details: "locked amount exceeds the total amount to send"}
ErrLockedAmountLessThanSendAmountAllNetworks = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-015"), Details: "locked amount is less than the total amount to send, but all networks are locked"}
ErrDisabledChainFoundAmongLockedNetworks = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-016"), Details: "disabled chain found among locked networks"}
ErrENSSetPubKeyInvalidUsername = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-017"), Details: "a valid username, ending in '.eth', is required for ENSSetPubKey"}
ErrLockedAmountExcludesAllSupported = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-018"), Details: "all supported chains are excluded, routing impossible"}
ErrCannotCheckLockedAmounts = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-019"), Details: "cannot check locked amounts"}
)
type RouteInputParams struct {
Uuid string `json:"uuid"`
SendType sendtype.SendType `json:"sendType" validate:"required"`
AddrFrom common.Address `json:"addrFrom" validate:"required"`
AddrTo common.Address `json:"addrTo" validate:"required"`
AmountIn *hexutil.Big `json:"amountIn" validate:"required"`
AmountOut *hexutil.Big `json:"amountOut"`
TokenID string `json:"tokenID" validate:"required"`
ToTokenID string `json:"toTokenID"`
DisabledFromChainIDs []uint64 `json:"disabledFromChainIDs"`
DisabledToChainIDs []uint64 `json:"disabledToChainIDs"`
GasFeeMode fees.GasFeeMode `json:"gasFeeMode" validate:"required"`
FromLockedAmount map[uint64]*hexutil.Big `json:"fromLockedAmount"`
TestnetMode bool
// For send types like EnsRegister, EnsRelease, EnsSetPubKey, StickersBuy
Username string `json:"username"`
PublicKey string `json:"publicKey"`
PackID *hexutil.Big `json:"packID"`
// TODO: Remove two fields below once we implement a better solution for tests
// Currently used for tests only
TestsMode bool
TestParams *RouterTestParams
}
type RouterTestParams struct {
TokenFrom *token.Token
TokenPrices map[string]float64
EstimationMap map[string]pathprocessor.Estimation // [processor-name, estimation]
BonderFeeMap map[string]*big.Int // [token-symbol, bonder-fee]
SuggestedFees *fees.SuggestedFees
BaseFee *big.Int
BalanceMap map[string]*big.Int // [token-symbol, balance]
ApprovalGasEstimation uint64
ApprovalL1Fee uint64
}
func (i *RouteInputParams) Validate() error {
if i.SendType == sendtype.ENSRegister {
if i.Username == "" || i.PublicKey == "" {
return ErrENSRegisterRequiresUsernameAndPubKey
}
if i.TestnetMode {
if i.TokenID != pathprocessor.SttSymbol {
return ErrENSRegisterTestnetSTTOnly
}
} else {
if i.TokenID != pathprocessor.SntSymbol {
return ErrENSRegisterMainnetSNTOnly
}
}
return nil
}
if i.SendType == sendtype.ENSRelease {
if i.Username == "" {
return ErrENSReleaseRequiresUsername
}
}
if i.SendType == sendtype.ENSSetPubKey {
if i.Username == "" || i.PublicKey == "" {
return ErrENSSetPubKeyRequiresUsernameAndPubKey
}
if ens.ValidateENSUsername(i.Username) != nil {
return ErrENSSetPubKeyInvalidUsername
}
}
if i.SendType == sendtype.StickersBuy {
if i.PackID == nil {
return ErrStickersBuyRequiresPackID
}
}
if i.SendType == sendtype.Swap {
if i.ToTokenID == "" {
return ErrSwapRequiresToTokenID
}
if i.TokenID == i.ToTokenID {
return ErrSwapTokenIDMustBeDifferent
}
if i.AmountIn != nil &&
i.AmountOut != nil &&
i.AmountIn.ToInt().Cmp(pathprocessor.ZeroBigIntValue) > 0 &&
i.AmountOut.ToInt().Cmp(pathprocessor.ZeroBigIntValue) > 0 {
return ErrSwapAmountInAmountOutMustBeExclusive
}
if i.AmountIn != nil && i.AmountIn.ToInt().Sign() < 0 {
return ErrSwapAmountInMustBePositive
}
if i.AmountOut != nil && i.AmountOut.ToInt().Sign() < 0 {
return ErrSwapAmountOutMustBePositive
}
}
return i.validateFromLockedAmount()
}
func (i *RouteInputParams) validateFromLockedAmount() error {
if i.FromLockedAmount == nil || len(i.FromLockedAmount) == 0 {
return nil
}
var suppNetworks map[uint64]bool
if i.TestnetMode {
suppNetworks = walletCommon.CopyMapGeneric(walletCommon.SupportedTestNetworks, nil).(map[uint64]bool)
} else {
suppNetworks = walletCommon.CopyMapGeneric(walletCommon.SupportedNetworks, nil).(map[uint64]bool)
}
if suppNetworks == nil {
return ErrCannotCheckLockedAmounts
}
totalLockedAmount := big.NewInt(0)
excludedChainCount := 0
for chainID, amount := range i.FromLockedAmount {
if walletCommon.ArrayContainsElement(chainID, i.DisabledFromChainIDs) {
return ErrDisabledChainFoundAmongLockedNetworks
}
if i.TestnetMode {
if !walletCommon.SupportedTestNetworks[chainID] {
return ErrLockedAmountNotSupportedForNetwork
}
} else {
if !walletCommon.SupportedNetworks[chainID] {
return ErrLockedAmountNotSupportedForNetwork
}
}
if amount == nil || amount.ToInt().Sign() < 0 {
return ErrLockedAmountNotNegative
}
if !(amount.ToInt().Sign() > 0) {
excludedChainCount++
}
delete(suppNetworks, chainID)
totalLockedAmount = new(big.Int).Add(totalLockedAmount, amount.ToInt())
}
if (!i.TestnetMode && excludedChainCount == len(walletCommon.SupportedNetworks)) ||
(i.TestnetMode && excludedChainCount == len(walletCommon.SupportedTestNetworks)) {
return ErrLockedAmountExcludesAllSupported
}
if totalLockedAmount.Cmp(i.AmountIn.ToInt()) > 0 {
return ErrLockedAmountExceedsTotalSendAmount
}
if totalLockedAmount.Cmp(i.AmountIn.ToInt()) < 0 && len(suppNetworks) == 0 {
return ErrLockedAmountLessThanSendAmountAllNetworks
}
return nil
}

View File

@ -1,32 +1,54 @@
package router
import (
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/services/wallet/common"
)
func arrayContainsElement[T comparable](el T, arr []T) bool {
for _, e := range arr {
if e == el {
return true
func removeBestRouteFromAllRouters(allRoutes [][]*Path, best []*Path) [][]*Path {
for i := len(allRoutes) - 1; i >= 0; i-- {
route := allRoutes[i]
routeFound := true
for _, p := range route {
found := false
for _, b := range best {
if p.ProcessorName == b.ProcessorName &&
(p.FromChain == nil && b.FromChain == nil || p.FromChain.ChainID == b.FromChain.ChainID) &&
(p.ToChain == nil && b.ToChain == nil || p.ToChain.ChainID == b.ToChain.ChainID) &&
(p.FromToken == nil && b.FromToken == nil || p.FromToken.Symbol == b.FromToken.Symbol) {
found = true
break
}
}
return false
if !found {
routeFound = false
break
}
}
if routeFound {
return append(allRoutes[:i], allRoutes[i+1:]...)
}
}
return nil
}
func arraysWithSameElements[T comparable](ar1 []T, ar2 []T, isEqual func(T, T) bool) bool {
if len(ar1) != len(ar2) {
return false
func getChainPriority(chainID uint64) int {
switch chainID {
case common.EthereumMainnet, common.EthereumSepolia:
return 1
case common.OptimismMainnet, common.OptimismSepolia:
return 2
case common.ArbitrumMainnet, common.ArbitrumSepolia:
return 3
default:
return 0
}
for _, el := range ar1 {
if !arrayContainsElement(el, ar2) {
return false
}
}
return true
}
func isSingleChainOperation(fromChains []*params.Network, toChains []*params.Network) bool {
return len(fromChains) == 1 &&
len(toChains) == 1 &&
fromChains[0].ChainID == toChains[0].ChainID
func getRoutePriority(route []*Path) int {
priority := 0
for _, path := range route {
priority += getChainPriority(path.FromChain.ChainID)
}
return priority
}

View File

@ -6,31 +6,12 @@ import (
// Abbreviation `WR` for the error code stands for Wallet Router
var (
ErrENSRegisterRequiresUsernameAndPubKey = &errors.ErrorResponse{Code: errors.ErrorCode("WR-001"), Details: "username and public key are required for ENSRegister"}
ErrENSRegisterTestnetSTTOnly = &errors.ErrorResponse{Code: errors.ErrorCode("WR-002"), Details: "only STT is supported for ENSRegister on testnet"}
ErrENSRegisterMainnetSNTOnly = &errors.ErrorResponse{Code: errors.ErrorCode("WR-003"), Details: "only SNT is supported for ENSRegister on mainnet"}
ErrENSReleaseRequiresUsername = &errors.ErrorResponse{Code: errors.ErrorCode("WR-004"), Details: "username is required for ENSRelease"}
ErrENSSetPubKeyRequiresUsernameAndPubKey = &errors.ErrorResponse{Code: errors.ErrorCode("WR-005"), Details: "username and public key are required for ENSSetPubKey"}
ErrStickersBuyRequiresPackID = &errors.ErrorResponse{Code: errors.ErrorCode("WR-006"), Details: "packID is required for StickersBuy"}
ErrSwapRequiresToTokenID = &errors.ErrorResponse{Code: errors.ErrorCode("WR-007"), Details: "toTokenID is required for Swap"}
ErrSwapTokenIDMustBeDifferent = &errors.ErrorResponse{Code: errors.ErrorCode("WR-008"), Details: "tokenID and toTokenID must be different"}
ErrSwapAmountInAmountOutMustBeExclusive = &errors.ErrorResponse{Code: errors.ErrorCode("WR-009"), Details: "only one of amountIn or amountOut can be set"}
ErrSwapAmountInMustBePositive = &errors.ErrorResponse{Code: errors.ErrorCode("WR-010"), Details: "amountIn must be positive"}
ErrSwapAmountOutMustBePositive = &errors.ErrorResponse{Code: errors.ErrorCode("WR-011"), Details: "amountOut must be positive"}
ErrLockedAmountNotSupportedForNetwork = &errors.ErrorResponse{Code: errors.ErrorCode("WR-012"), Details: "locked amount is not supported for the selected network"}
ErrLockedAmountNotNegative = &errors.ErrorResponse{Code: errors.ErrorCode("WR-013"), Details: "locked amount must not be negative"}
ErrLockedAmountExceedsTotalSendAmount = &errors.ErrorResponse{Code: errors.ErrorCode("WR-014"), Details: "locked amount exceeds the total amount to send"}
ErrLockedAmountLessThanSendAmountAllNetworks = &errors.ErrorResponse{Code: errors.ErrorCode("WR-015"), Details: "locked amount is less than the total amount to send, but all networks are locked"}
ErrNotEnoughTokenBalance = &errors.ErrorResponse{Code: errors.ErrorCode("WR-016"), Details: "not enough token balance, token: %s, chainId: %d"}
ErrNotEnoughNativeBalance = &errors.ErrorResponse{Code: errors.ErrorCode("WR-017"), Details: "not enough native balance, token: %s, chainId: %d"}
ErrNativeTokenNotFound = &errors.ErrorResponse{Code: errors.ErrorCode("WR-018"), Details: "native token not found"}
ErrDisabledChainFoundAmongLockedNetworks = &errors.ErrorResponse{Code: errors.ErrorCode("WR-019"), Details: "disabled chain found among locked networks"}
ErrENSSetPubKeyInvalidUsername = &errors.ErrorResponse{Code: errors.ErrorCode("WR-020"), Details: "a valid username, ending in '.eth', is required for ENSSetPubKey"}
ErrLockedAmountExcludesAllSupported = &errors.ErrorResponse{Code: errors.ErrorCode("WR-021"), Details: "all supported chains are excluded, routing impossible"}
ErrTokenNotFound = &errors.ErrorResponse{Code: errors.ErrorCode("WR-022"), Details: "token not found"}
ErrNoBestRouteFound = &errors.ErrorResponse{Code: errors.ErrorCode("WR-023"), Details: "no best route found"}
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"}
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"}
ErrNotEnoughTokenBalance = &errors.ErrorResponse{Code: errors.ErrorCode("WR-001"), Details: "not enough token balance, token: %s, chainId: %d"}
ErrNotEnoughNativeBalance = &errors.ErrorResponse{Code: errors.ErrorCode("WR-002"), Details: "not enough native balance, token: %s, chainId: %d"}
ErrNativeTokenNotFound = &errors.ErrorResponse{Code: errors.ErrorCode("WR-003"), Details: "native token not found"}
ErrTokenNotFound = &errors.ErrorResponse{Code: errors.ErrorCode("WR-004"), Details: "token not found"}
ErrNoBestRouteFound = &errors.ErrorResponse{Code: errors.ErrorCode("WR-005"), Details: "no best route found"}
ErrCannotCheckBalance = &errors.ErrorResponse{Code: errors.ErrorCode("WR-006"), Details: "cannot check balance"}
ErrLowAmountInForHopBridge = &errors.ErrorResponse{Code: errors.ErrorCode("WR-007"), Details: "bonder fee greater than estimated received, a higher amount is needed to cover fees"}
ErrNoPositiveBalance = &errors.ErrorResponse{Code: errors.ErrorCode("WR-008"), Details: "no positive balance"}
)

View File

@ -1,4 +1,4 @@
package router
package fees
import (
"context"
@ -54,7 +54,7 @@ type SuggestedFeesGwei struct {
EIP1559Enabled bool `json:"eip1559Enabled"`
}
func (m *MaxFeesLevels) feeFor(mode GasFeeMode) *big.Int {
func (m *MaxFeesLevels) FeeFor(mode GasFeeMode) *big.Int {
if mode == GasFeeLow {
return m.Low.ToInt()
}
@ -66,8 +66,8 @@ func (m *MaxFeesLevels) feeFor(mode GasFeeMode) *big.Int {
return m.Medium.ToInt()
}
func (s *SuggestedFees) feeFor(mode GasFeeMode) *big.Int {
return s.MaxFeesLevels.feeFor(mode)
func (s *SuggestedFees) FeeFor(mode GasFeeMode) *big.Int {
return s.MaxFeesLevels.FeeFor(mode)
}
const inclusionThreshold = 0.95
@ -90,20 +90,6 @@ type FeeManager struct {
RPCClient *rpc.Client
}
func weiToGwei(val *big.Int) *big.Float {
result := new(big.Float)
result.SetInt(val)
unit := new(big.Int)
unit.SetInt64(params.GWei)
return result.Quo(result, new(big.Float).SetInt(unit))
}
func gweiToEth(val *big.Float) *big.Float {
return new(big.Float).Quo(val, big.NewFloat(1000000000))
}
func (f *FeeManager) SuggestedFees(ctx context.Context, chainID uint64) (*SuggestedFees, error) {
backend, err := f.RPCClient.EthClient(chainID)
if err != nil {
@ -151,12 +137,12 @@ func (f *FeeManager) SuggestedFeesGwei(ctx context.Context, chainID uint64) (*Su
return nil, err
}
return &SuggestedFeesGwei{
GasPrice: weiToGwei(fees.GasPrice),
BaseFee: weiToGwei(fees.BaseFee),
MaxPriorityFeePerGas: weiToGwei(fees.MaxPriorityFeePerGas),
MaxFeePerGasLow: weiToGwei(fees.MaxFeesLevels.Low.ToInt()),
MaxFeePerGasMedium: weiToGwei(fees.MaxFeesLevels.Medium.ToInt()),
MaxFeePerGasHigh: weiToGwei(fees.MaxFeesLevels.High.ToInt()),
GasPrice: common.WeiToGwei(fees.GasPrice),
BaseFee: common.WeiToGwei(fees.BaseFee),
MaxPriorityFeePerGas: common.WeiToGwei(fees.MaxPriorityFeePerGas),
MaxFeePerGasLow: common.WeiToGwei(fees.MaxFeesLevels.Low.ToInt()),
MaxFeePerGasMedium: common.WeiToGwei(fees.MaxFeesLevels.Medium.ToInt()),
MaxFeePerGasHigh: common.WeiToGwei(fees.MaxFeesLevels.High.ToInt()),
EIP1559Enabled: fees.EIP1559Enabled,
}, nil
}

View File

@ -2,9 +2,9 @@ package router
import (
"math/big"
"reflect"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/status-im/status-go/services/wallet/common"
"github.com/status-im/status-go/services/wallet/router/pathprocessor"
"go.uber.org/zap"
@ -57,7 +57,7 @@ func filterNetworkCompliance(routes [][]*Path, fromLockedAmount map[uint64]*hexu
}
// Create fresh copies of the maps for each route check, because they are manipulated
if isValidForNetworkCompliance(route, copyMapGeneric(fromIncluded, nil).(map[uint64]bool), copyMapGeneric(fromExcluded, nil).(map[uint64]bool)) {
if isValidForNetworkCompliance(route, common.CopyMapGeneric(fromIncluded, nil).(map[uint64]bool), common.CopyMapGeneric(fromExcluded, nil).(map[uint64]bool)) {
filteredRoutes = append(filteredRoutes, route)
}
}
@ -162,22 +162,3 @@ func calculateRestAmountIn(route []*Path, excludePath *Path) *big.Int {
}
return restAmountIn
}
// copyMapGeneric creates a copy of any map, if the deepCopyValue function is provided, it will be used to copy values.
func copyMapGeneric(original interface{}, deepCopyValueFn func(interface{}) interface{}) interface{} {
originalVal := reflect.ValueOf(original)
if originalVal.Kind() != reflect.Map {
return nil
}
newMap := reflect.MakeMap(originalVal.Type())
for iter := originalVal.MapRange(); iter.Next(); {
if deepCopyValueFn != nil {
newMap.SetMapIndex(iter.Key(), reflect.ValueOf(deepCopyValueFn(iter.Value().Interface())))
} else {
newMap.SetMapIndex(iter.Key(), iter.Value())
}
}
return newMap.Interface()
}

View File

@ -3,7 +3,6 @@ package router
import (
"context"
"fmt"
"math"
"math/big"
"sort"
"strings"
@ -21,17 +20,16 @@ import (
"github.com/status-im/status-go/services/wallet/collectibles"
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/requests"
"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/sendtype"
"github.com/status-im/status-go/services/wallet/token"
walletToken "github.com/status-im/status-go/services/wallet/token"
"github.com/status-im/status-go/signal"
"github.com/status-im/status-go/transactions"
)
const (
hexAddressLength = 42
)
var (
routerTask = async.TaskType{
ID: 1,
@ -39,58 +37,6 @@ var (
}
)
var (
supportedNetworks = map[uint64]bool{
walletCommon.EthereumMainnet: true,
walletCommon.OptimismMainnet: true,
walletCommon.ArbitrumMainnet: true,
}
supportedTestNetworks = map[uint64]bool{
walletCommon.EthereumSepolia: true,
walletCommon.OptimismSepolia: true,
walletCommon.ArbitrumSepolia: true,
}
)
type RouteInputParams struct {
Uuid string `json:"uuid"`
SendType SendType `json:"sendType" validate:"required"`
AddrFrom common.Address `json:"addrFrom" validate:"required"`
AddrTo common.Address `json:"addrTo" validate:"required"`
AmountIn *hexutil.Big `json:"amountIn" validate:"required"`
AmountOut *hexutil.Big `json:"amountOut"`
TokenID string `json:"tokenID" validate:"required"`
ToTokenID string `json:"toTokenID"`
DisabledFromChainIDs []uint64 `json:"disabledFromChainIDs"`
DisabledToChainIDs []uint64 `json:"disabledToChainIDs"`
GasFeeMode GasFeeMode `json:"gasFeeMode" validate:"required"`
FromLockedAmount map[uint64]*hexutil.Big `json:"fromLockedAmount"`
testnetMode bool
// For send types like EnsRegister, EnsRelease, EnsSetPubKey, StickersBuy
Username string `json:"username"`
PublicKey string `json:"publicKey"`
PackID *hexutil.Big `json:"packID"`
// TODO: Remove two fields below once we implement a better solution for tests
// Currently used for tests only
testsMode bool
testParams *routerTestParams
}
type routerTestParams struct {
tokenFrom *walletToken.Token
tokenPrices map[string]float64
estimationMap map[string]pathprocessor.Estimation // [processor-name, estimation]
bonderFeeMap map[string]*big.Int // [token-symbol, bonder-fee]
suggestedFees *SuggestedFees
baseFee *big.Int
balanceMap map[string]*big.Int // [token-symbol, balance]
approvalGasEstimation uint64
approvalL1Fee uint64
}
type amountOption struct {
amount *big.Int
locked bool
@ -101,56 +47,11 @@ func makeBalanceKey(chainID uint64, symbol string) string {
return fmt.Sprintf("%d-%s", chainID, symbol)
}
type Path struct {
ProcessorName string
FromChain *params.Network // Source chain
ToChain *params.Network // Destination chain
FromToken *walletToken.Token // Source token
ToToken *walletToken.Token // Destination token, set if applicable
AmountIn *hexutil.Big // Amount that will be sent from the source chain
AmountInLocked bool // Is the amount locked
AmountOut *hexutil.Big // Amount that will be received on the destination chain
SuggestedLevelsForMaxFeesPerGas *MaxFeesLevels // Suggested max fees for the transaction (in ETH WEI)
MaxFeesPerGas *hexutil.Big // Max fees per gas (determined by client via GasFeeMode, in ETH WEI)
TxBaseFee *hexutil.Big // Base fee for the transaction (in ETH WEI)
TxPriorityFee *hexutil.Big // Priority fee for the transaction (in ETH WEI)
TxGasAmount uint64 // Gas used for the transaction
TxBonderFees *hexutil.Big // Bonder fees for the transaction - used for Hop bridge (in selected token)
TxTokenFees *hexutil.Big // Token fees for the transaction - used for bridges (represent the difference between the amount in and the amount out, in selected token)
TxFee *hexutil.Big // fee for the transaction (includes tx fee only, doesn't include approval fees, l1 fees, l1 approval fees, token fees or bonders fees, in ETH WEI)
TxL1Fee *hexutil.Big // L1 fee for the transaction - used for for transactions placed on L2 chains (in ETH WEI)
ApprovalRequired bool // Is approval required for the transaction
ApprovalAmountRequired *hexutil.Big // Amount required for the approval transaction
ApprovalContractAddress *common.Address // Address of the contract that needs to be approved
ApprovalBaseFee *hexutil.Big // Base fee for the approval transaction (in ETH WEI)
ApprovalPriorityFee *hexutil.Big // Priority fee for the approval transaction (in ETH WEI)
ApprovalGasAmount uint64 // Gas used for the approval transaction
ApprovalFee *hexutil.Big // Total fee for the approval transaction (includes approval tx fees only, doesn't include approval l1 fees, in ETH WEI)
ApprovalL1Fee *hexutil.Big // L1 fee for the approval transaction - used for for transactions placed on L2 chains (in ETH WEI)
TxTotalFee *hexutil.Big // Total fee for the transaction (includes tx fees, approval fees, l1 fees, l1 approval fees, in ETH WEI)
EstimatedTime TransactionEstimation
requiredTokenBalance *big.Int // (in selected token)
requiredNativeBalance *big.Int // (in ETH WEI)
subtractFees bool
}
type ProcessorError struct {
ProcessorName string
Error error
}
func (p *Path) Equal(o *Path) bool {
return p.FromChain.ChainID == o.FromChain.ChainID && p.ToChain.ChainID == o.ToChain.ChainID
}
type SuggestedRoutes struct {
Uuid string
Best []*Path
@ -168,13 +69,6 @@ type SuggestedRoutesResponse struct {
ErrorResponse *errors.ErrorResponse `json:"ErrorResponse,omitempty"`
}
type Graph []*Node
type Node struct {
Path *Path
Children Graph
}
type Router struct {
rpcClient *rpc.Client
tokenManager *token.Manager
@ -183,7 +77,7 @@ type Router struct {
collectiblesManager *collectibles.Manager
ensService *ens.Service
stickersService *stickers.Service
feesManager *FeeManager
feesManager *fees.FeeManager
pathProcessors map[string]pathprocessor.PathProcessor
scheduler *async.Scheduler
}
@ -200,7 +94,9 @@ func NewRouter(rpcClient *rpc.Client, transactor *transactions.Transactor, token
collectiblesManager: collectiblesManager,
ensService: ensService,
stickersService: stickersService,
feesManager: &FeeManager{rpcClient},
feesManager: &fees.FeeManager{
RPCClient: rpcClient,
},
pathProcessors: processors,
scheduler: async.NewScheduler(),
}
@ -214,7 +110,7 @@ func (r *Router) Stop() {
r.scheduler.Stop()
}
func (r *Router) GetFeesManager() *FeeManager {
func (r *Router) GetFeesManager() *fees.FeeManager {
return r.feesManager
}
@ -258,252 +154,7 @@ func newSuggestedRoutes(
return suggestedRoutes, allRoutes
}
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))
for _, route := range routes {
currentCost := big.NewFloat(0)
for _, path := range route {
tokenDenominator := big.NewFloat(math.Pow(10, float64(path.FromToken.Decimals)))
// calculate the cost of the path
nativeTokenPrice := new(big.Float).SetFloat64(nativeTokenPrice)
// tx fee
txFeeInEth := gweiToEth(weiToGwei(path.TxFee.ToInt()))
pathCost := new(big.Float).Mul(txFeeInEth, nativeTokenPrice)
if path.TxL1Fee.ToInt().Cmp(pathprocessor.ZeroBigIntValue) > 0 {
txL1FeeInEth := gweiToEth(weiToGwei(path.TxL1Fee.ToInt()))
pathCost.Add(pathCost, new(big.Float).Mul(txL1FeeInEth, nativeTokenPrice))
}
if path.TxBonderFees != nil && path.TxBonderFees.ToInt().Cmp(pathprocessor.ZeroBigIntValue) > 0 {
pathCost.Add(pathCost, new(big.Float).Mul(
new(big.Float).Quo(new(big.Float).SetInt(path.TxBonderFees.ToInt()), tokenDenominator),
new(big.Float).SetFloat64(tokenPrice)))
}
if path.TxTokenFees != nil && path.TxTokenFees.ToInt().Cmp(pathprocessor.ZeroBigIntValue) > 0 && path.FromToken != nil {
pathCost.Add(pathCost, new(big.Float).Mul(
new(big.Float).Quo(new(big.Float).SetInt(path.TxTokenFees.ToInt()), tokenDenominator),
new(big.Float).SetFloat64(tokenPrice)))
}
if path.ApprovalRequired {
// tx approval fee
approvalFeeInEth := gweiToEth(weiToGwei(path.ApprovalFee.ToInt()))
pathCost.Add(pathCost, new(big.Float).Mul(approvalFeeInEth, nativeTokenPrice))
if path.ApprovalL1Fee.ToInt().Cmp(pathprocessor.ZeroBigIntValue) > 0 {
approvalL1FeeInEth := gweiToEth(weiToGwei(path.ApprovalL1Fee.ToInt()))
pathCost.Add(pathCost, new(big.Float).Mul(approvalL1FeeInEth, nativeTokenPrice))
}
}
currentCost = new(big.Float).Add(currentCost, pathCost)
}
if currentCost.Cmp(bestCost) == -1 {
best = route
bestCost = currentCost
}
}
return best
}
func validateInputData(input *RouteInputParams) error {
if input.SendType == ENSRegister {
if input.Username == "" || input.PublicKey == "" {
return ErrENSRegisterRequiresUsernameAndPubKey
}
if input.testnetMode {
if input.TokenID != pathprocessor.SttSymbol {
return ErrENSRegisterTestnetSTTOnly
}
} else {
if input.TokenID != pathprocessor.SntSymbol {
return ErrENSRegisterMainnetSNTOnly
}
}
return nil
}
if input.SendType == ENSRelease {
if input.Username == "" {
return ErrENSReleaseRequiresUsername
}
}
if input.SendType == ENSSetPubKey {
if input.Username == "" || input.PublicKey == "" {
return ErrENSSetPubKeyRequiresUsernameAndPubKey
}
if ens.ValidateENSUsername(input.Username) != nil {
return ErrENSSetPubKeyInvalidUsername
}
}
if input.SendType == StickersBuy {
if input.PackID == nil {
return ErrStickersBuyRequiresPackID
}
}
if input.SendType == Swap {
if input.ToTokenID == "" {
return ErrSwapRequiresToTokenID
}
if input.TokenID == input.ToTokenID {
return ErrSwapTokenIDMustBeDifferent
}
if input.AmountIn != nil &&
input.AmountOut != nil &&
input.AmountIn.ToInt().Cmp(pathprocessor.ZeroBigIntValue) > 0 &&
input.AmountOut.ToInt().Cmp(pathprocessor.ZeroBigIntValue) > 0 {
return ErrSwapAmountInAmountOutMustBeExclusive
}
if input.AmountIn != nil && input.AmountIn.ToInt().Sign() < 0 {
return ErrSwapAmountInMustBePositive
}
if input.AmountOut != nil && input.AmountOut.ToInt().Sign() < 0 {
return ErrSwapAmountOutMustBePositive
}
}
return validateFromLockedAmount(input)
}
func validateFromLockedAmount(input *RouteInputParams) error {
if input.FromLockedAmount == nil || len(input.FromLockedAmount) == 0 {
return nil
}
var suppNetworks map[uint64]bool
if input.testnetMode {
suppNetworks = copyMapGeneric(supportedTestNetworks, nil).(map[uint64]bool)
} else {
suppNetworks = copyMapGeneric(supportedNetworks, nil).(map[uint64]bool)
}
if suppNetworks == nil {
return ErrCannotCheckLockedAmounts
}
totalLockedAmount := big.NewInt(0)
excludedChainCount := 0
for chainID, amount := range input.FromLockedAmount {
if arrayContainsElement(chainID, input.DisabledFromChainIDs) {
return ErrDisabledChainFoundAmongLockedNetworks
}
if input.testnetMode {
if !supportedTestNetworks[chainID] {
return ErrLockedAmountNotSupportedForNetwork
}
} else {
if !supportedNetworks[chainID] {
return ErrLockedAmountNotSupportedForNetwork
}
}
if amount == nil || amount.ToInt().Sign() < 0 {
return ErrLockedAmountNotNegative
}
if !(amount.ToInt().Sign() > 0) {
excludedChainCount++
}
delete(suppNetworks, chainID)
totalLockedAmount = new(big.Int).Add(totalLockedAmount, amount.ToInt())
}
if (!input.testnetMode && excludedChainCount == len(supportedNetworks)) ||
(input.testnetMode && excludedChainCount == len(supportedTestNetworks)) {
return ErrLockedAmountExcludesAllSupported
}
if totalLockedAmount.Cmp(input.AmountIn.ToInt()) > 0 {
return ErrLockedAmountExceedsTotalSendAmount
} else if totalLockedAmount.Cmp(input.AmountIn.ToInt()) < 0 && len(suppNetworks) == 0 {
return ErrLockedAmountLessThanSendAmountAllNetworks
}
return nil
}
func (r *Router) SuggestedRoutesAsync(input *RouteInputParams) {
func (r *Router) SuggestedRoutesAsync(input *requests.RouteInputParams) {
r.scheduler.Enqueue(routerTask, func(ctx context.Context) (interface{}, error) {
return r.SuggestedRoutes(ctx, input)
}, func(result interface{}, taskType async.TaskType, err error) {
@ -531,13 +182,13 @@ func (r *Router) StopSuggestedRoutesAsyncCalculation() {
r.scheduler.Stop()
}
func (r *Router) SuggestedRoutes(ctx context.Context, input *RouteInputParams) (*SuggestedRoutes, error) {
func (r *Router) SuggestedRoutes(ctx context.Context, input *requests.RouteInputParams) (*SuggestedRoutes, error) {
testnetMode, err := r.rpcClient.NetworkManager.GetTestNetworksEnabled()
if err != nil {
return nil, errors.CreateErrorResponseFromError(err)
}
input.testnetMode = testnetMode
input.TestnetMode = testnetMode
// clear all processors
for _, processor := range r.pathProcessors {
@ -546,7 +197,7 @@ func (r *Router) SuggestedRoutes(ctx context.Context, input *RouteInputParams) (
}
}
err = validateInputData(input)
err = input.Validate()
if err != nil {
return nil, errors.CreateErrorResponseFromError(err)
}
@ -608,7 +259,7 @@ func (r *Router) SuggestedRoutes(ctx context.Context, input *RouteInputParams) (
pattern := "insufficient funds for gas * price + value: address "
addressIndex := strings.Index(errors.DetailsFromError(err), pattern)
if addressIndex != -1 {
addressIndex += len(pattern) + hexAddressLength
addressIndex += len(pattern) + walletCommon.HexAddressLength
return errors.CreateErrorResponseFromError(&errors.ErrorResponse{
Code: errors.ErrorCodeFromError(err),
Details: errors.DetailsFromError(err)[:addressIndex],
@ -622,9 +273,9 @@ func (r *Router) SuggestedRoutes(ctx context.Context, input *RouteInputParams) (
// getBalanceMapForTokenOnChains returns the balance map for passed address, where the key is in format "chainID-tokenSymbol" and
// value is the balance of the token. Native token (EHT) is always added to the balance map.
func (r *Router) getBalanceMapForTokenOnChains(ctx context.Context, input *RouteInputParams, selectedFromChains []*params.Network) (balanceMap map[string]*big.Int, err error) {
if input.testsMode {
return input.testParams.balanceMap, nil
func (r *Router) getBalanceMapForTokenOnChains(ctx context.Context, input *requests.RouteInputParams, selectedFromChains []*params.Network) (balanceMap map[string]*big.Int, err error) {
if input.TestsMode {
return input.TestParams.BalanceMap, nil
}
balanceMap = make(map[string]*big.Int)
@ -653,9 +304,9 @@ func (r *Router) getBalanceMapForTokenOnChains(ctx context.Context, input *Route
// add token balance for the chain
var tokenBalance *big.Int
if input.SendType == ERC721Transfer {
if input.SendType == sendtype.ERC721Transfer {
tokenBalance = big.NewInt(1)
} else if input.SendType == ERC1155Transfer {
} else if input.SendType == sendtype.ERC1155Transfer {
tokenBalance, err = r.getERC1155Balance(ctx, chain, token, input.AddrFrom)
if err != nil {
chainError(chain.ChainID, token.Symbol, errors.CreateErrorResponseFromError(err))
@ -689,7 +340,7 @@ func (r *Router) getBalanceMapForTokenOnChains(ctx context.Context, input *Route
return
}
func (r *Router) getSelectedUnlockedChains(input *RouteInputParams, processingChain *params.Network, selectedFromChains []*params.Network) []*params.Network {
func (r *Router) getSelectedUnlockedChains(input *requests.RouteInputParams, processingChain *params.Network, selectedFromChains []*params.Network) []*params.Network {
selectedButNotLockedChains := []*params.Network{processingChain} // always add the processing chain at the beginning
for _, net := range selectedFromChains {
if net.ChainID == processingChain.ChainID {
@ -702,7 +353,7 @@ func (r *Router) getSelectedUnlockedChains(input *RouteInputParams, processingCh
return selectedButNotLockedChains
}
func (r *Router) getOptionsForAmoutToSplitAccrossChainsForProcessingChain(input *RouteInputParams, amountToSplit *big.Int, processingChain *params.Network,
func (r *Router) getOptionsForAmoutToSplitAccrossChainsForProcessingChain(input *requests.RouteInputParams, amountToSplit *big.Int, processingChain *params.Network,
selectedFromChains []*params.Network, balanceMap map[string]*big.Int) map[uint64][]amountOption {
selectedButNotLockedChains := r.getSelectedUnlockedChains(input, processingChain, selectedFromChains)
@ -739,7 +390,7 @@ func (r *Router) getOptionsForAmoutToSplitAccrossChainsForProcessingChain(input
return crossChainAmountOptions
}
func (r *Router) getCrossChainsOptionsForSendingAmount(input *RouteInputParams, selectedFromChains []*params.Network,
func (r *Router) getCrossChainsOptionsForSendingAmount(input *requests.RouteInputParams, selectedFromChains []*params.Network,
balanceMap map[string]*big.Int) map[uint64][]amountOption {
// All we do in this block we're free to do, because of the validateInputData function which checks if the locked amount
// was properly set and if there is something unexpected it will return an error and we will not reach this point
@ -786,7 +437,7 @@ func (r *Router) getCrossChainsOptionsForSendingAmount(input *RouteInputParams,
// If the amount that need to be send is bigger than the balance on the chain, then we want to check options if that
// amount can be splitted and sent across multiple chains.
if input.SendType == Transfer && len(selectedFromChains) > 1 {
if input.SendType == sendtype.Transfer && len(selectedFromChains) > 1 {
// All we do in this block we're free to do, because of the validateInputData function which checks if the locked amount
// was properly set and if there is something unexpected it will return an error and we will not reach this point
amountToSplitAccrossChains := new(big.Int).Set(amountToSend)
@ -814,7 +465,7 @@ func (r *Router) getCrossChainsOptionsForSendingAmount(input *RouteInputParams,
return finalCrossChainAmountOptions
}
func (r *Router) findOptionsForSendingAmount(input *RouteInputParams, selectedFromChains []*params.Network,
func (r *Router) findOptionsForSendingAmount(input *requests.RouteInputParams, selectedFromChains []*params.Network,
balanceMap map[string]*big.Int) (map[uint64][]amountOption, error) {
crossChainAmountOptions := r.getCrossChainsOptionsForSendingAmount(input, selectedFromChains, balanceMap)
@ -835,7 +486,7 @@ func (r *Router) findOptionsForSendingAmount(input *RouteInputParams, selectedFr
return crossChainAmountOptions, nil
}
func (r *Router) getSelectedChains(input *RouteInputParams) (selectedFromChains []*params.Network, selectedToChains []*params.Network, err error) {
func (r *Router) getSelectedChains(input *requests.RouteInputParams) (selectedFromChains []*params.Network, selectedToChains []*params.Network, err error) {
var networks []*params.Network
networks, err = r.rpcClient.NetworkManager.Get(false)
if err != nil {
@ -843,15 +494,15 @@ func (r *Router) getSelectedChains(input *RouteInputParams) (selectedFromChains
}
for _, network := range networks {
if network.IsTest != input.testnetMode {
if network.IsTest != input.TestnetMode {
continue
}
if !arrayContainsElement(network.ChainID, input.DisabledFromChainIDs) {
if !walletCommon.ArrayContainsElement(network.ChainID, input.DisabledFromChainIDs) {
selectedFromChains = append(selectedFromChains, network)
}
if !arrayContainsElement(network.ChainID, input.DisabledToChainIDs) {
if !walletCommon.ArrayContainsElement(network.ChainID, input.DisabledToChainIDs) {
selectedToChains = append(selectedToChains, network)
}
}
@ -859,10 +510,10 @@ func (r *Router) getSelectedChains(input *RouteInputParams) (selectedFromChains
return selectedFromChains, selectedToChains, nil
}
func (r *Router) resolveCandidates(ctx context.Context, input *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) {
var (
testsMode = input.testsMode && input.testParams != nil
testsMode = input.TestsMode && input.TestParams != nil
group = async.NewAtomicGroup(ctx)
mu sync.Mutex
)
@ -872,7 +523,7 @@ func (r *Router) resolveCandidates(ctx context.Context, input *RouteInputParams,
return nil, nil, errors.CreateErrorResponseFromError(err)
}
appendProcessorErrorFn := func(processorName string, sendType SendType, fromChainID uint64, toChainID uint64, amount *big.Int, err error) {
appendProcessorErrorFn := func(processorName string, sendType sendtype.SendType, fromChainID uint64, toChainID uint64, amount *big.Int, err error) {
log.Error("router.resolveCandidates error", "processor", processorName, "sendType", sendType, "fromChainId: ", fromChainID, "toChainId", toChainID, "amount", amount, "err", err)
mu.Lock()
defer mu.Unlock()
@ -891,7 +542,7 @@ func (r *Router) resolveCandidates(ctx context.Context, input *RouteInputParams,
for networkIdx := range selectedFromChains {
network := selectedFromChains[networkIdx]
if !input.SendType.isAvailableFor(network) {
if !input.SendType.IsAvailableFor(network) {
continue
}
@ -901,7 +552,7 @@ func (r *Router) resolveCandidates(ctx context.Context, input *RouteInputParams,
)
if testsMode {
token = input.testParams.tokenFrom
token = input.TestParams.TokenFrom
} else {
token = input.SendType.FindToken(r.tokenManager, r.collectiblesService, input.AddrFrom, network, input.TokenID)
}
@ -909,15 +560,15 @@ func (r *Router) resolveCandidates(ctx context.Context, input *RouteInputParams,
continue
}
if input.SendType == Swap {
if input.SendType == sendtype.Swap {
toToken = input.SendType.FindToken(r.tokenManager, r.collectiblesService, common.Address{}, network, input.ToTokenID)
}
var fees *SuggestedFees
var fetchedFees *fees.SuggestedFees
if testsMode {
fees = input.testParams.suggestedFees
fetchedFees = input.TestParams.SuggestedFees
} else {
fees, err = r.feesManager.SuggestedFees(ctx, network.ChainID)
fetchedFees, err = r.feesManager.SuggestedFees(ctx, network.ChainID)
if err != nil {
continue
}
@ -942,26 +593,26 @@ func (r *Router) resolveCandidates(ctx context.Context, input *RouteInputParams,
// 6. ...
//
// With the current routing algorithm atm we're not able to generate all possible routes.
if !input.SendType.canUseProcessor(pProcessor) {
if !input.SendType.CanUseProcessor(pProcessor) {
continue
}
// if we're doing a single chain operation, we can skip bridge processors
if isSingleChainOperation(selectedFromChains, selectedToChains) && pathprocessor.IsProcessorBridge(pProcessor.Name()) {
if walletCommon.IsSingleChainOperation(selectedFromChains, selectedToChains) && pathprocessor.IsProcessorBridge(pProcessor.Name()) {
continue
}
if !input.SendType.processZeroAmountInProcessor(amountOption.amount, input.AmountOut.ToInt(), pProcessor.Name()) {
if !input.SendType.ProcessZeroAmountInProcessor(amountOption.amount, input.AmountOut.ToInt(), pProcessor.Name()) {
continue
}
for _, dest := range selectedToChains {
if !input.SendType.isAvailableFor(network) {
if !input.SendType.IsAvailableFor(network) {
continue
}
if !input.SendType.isAvailableBetween(network, dest) {
if !input.SendType.IsAvailableBetween(network, dest) {
continue
}
@ -979,12 +630,12 @@ func (r *Router) resolveCandidates(ctx context.Context, input *RouteInputParams,
PublicKey: input.PublicKey,
PackID: input.PackID.ToInt(),
}
if input.testsMode {
processorInputParams.TestsMode = input.testsMode
processorInputParams.TestEstimationMap = input.testParams.estimationMap
processorInputParams.TestBonderFeeMap = input.testParams.bonderFeeMap
processorInputParams.TestApprovalGasEstimation = input.testParams.approvalGasEstimation
processorInputParams.TestApprovalL1Fee = input.testParams.approvalL1Fee
if input.TestsMode {
processorInputParams.TestsMode = input.TestsMode
processorInputParams.TestEstimationMap = input.TestParams.EstimationMap
processorInputParams.TestBonderFeeMap = input.TestParams.BonderFeeMap
processorInputParams.TestApprovalGasEstimation = input.TestParams.ApprovalGasEstimation
processorInputParams.TestApprovalL1Fee = input.TestParams.ApprovalL1Fee
}
can, err := pProcessor.AvailableFor(processorInputParams)
@ -1036,10 +687,10 @@ func (r *Router) resolveCandidates(ctx context.Context, input *RouteInputParams,
continue
}
maxFeesPerGas := fees.feeFor(input.GasFeeMode)
maxFeesPerGas := fetchedFees.FeeFor(input.GasFeeMode)
estimatedTime := r.feesManager.TransactionEstimatedTime(ctx, network.ChainID, maxFeesPerGas)
if approvalRequired && estimatedTime < MoreThanFiveMinutes {
if approvalRequired && estimatedTime < fees.MoreThanFiveMinutes {
estimatedTime += 1
}
@ -1090,11 +741,11 @@ func (r *Router) resolveCandidates(ctx context.Context, input *RouteInputParams,
AmountInLocked: amountOption.locked,
AmountOut: (*hexutil.Big)(amountOut),
SuggestedLevelsForMaxFeesPerGas: fees.MaxFeesLevels,
SuggestedLevelsForMaxFeesPerGas: fetchedFees.MaxFeesLevels,
MaxFeesPerGas: (*hexutil.Big)(maxFeesPerGas),
TxBaseFee: (*hexutil.Big)(fees.BaseFee),
TxPriorityFee: (*hexutil.Big)(fees.MaxPriorityFeePerGas),
TxBaseFee: (*hexutil.Big)(fetchedFees.BaseFee),
TxPriorityFee: (*hexutil.Big)(fetchedFees.MaxPriorityFeePerGas),
TxGasAmount: gasLimit,
TxBonderFees: (*hexutil.Big)(bonderFees),
TxTokenFees: (*hexutil.Big)(tokenFees),
@ -1105,8 +756,8 @@ func (r *Router) resolveCandidates(ctx context.Context, input *RouteInputParams,
ApprovalRequired: approvalRequired,
ApprovalAmountRequired: (*hexutil.Big)(approvalAmountRequired),
ApprovalContractAddress: &approvalContractAddress,
ApprovalBaseFee: (*hexutil.Big)(fees.BaseFee),
ApprovalPriorityFee: (*hexutil.Big)(fees.MaxPriorityFeePerGas),
ApprovalBaseFee: (*hexutil.Big)(fetchedFees.BaseFee),
ApprovalPriorityFee: (*hexutil.Big)(fetchedFees.MaxPriorityFeePerGas),
ApprovalGasAmount: approvalGasLimit,
ApprovalFee: (*hexutil.Big)(approvalFeeInWei),
@ -1138,7 +789,7 @@ func (r *Router) resolveCandidates(ctx context.Context, input *RouteInputParams,
}
func (r *Router) checkBalancesForTheBestRoute(ctx context.Context, bestRoute []*Path, balanceMap map[string]*big.Int) (hasPositiveBalance bool, err error) {
balanceMapCopy := copyMapGeneric(balanceMap, func(v interface{}) interface{} {
balanceMapCopy := walletCommon.CopyMapGeneric(balanceMap, func(v interface{}) interface{} {
return new(big.Int).Set(v.(*big.Int))
}).(map[string]*big.Int)
if balanceMapCopy == nil {
@ -1193,59 +844,10 @@ func (r *Router) checkBalancesForTheBestRoute(ctx context.Context, bestRoute []*
return hasPositiveBalance, nil
}
func removeBestRouteFromAllRouters(allRoutes [][]*Path, best []*Path) [][]*Path {
for i := len(allRoutes) - 1; i >= 0; i-- {
route := allRoutes[i]
routeFound := true
for _, p := range route {
found := false
for _, b := range best {
if p.ProcessorName == b.ProcessorName &&
(p.FromChain == nil && b.FromChain == nil || p.FromChain.ChainID == b.FromChain.ChainID) &&
(p.ToChain == nil && b.ToChain == nil || p.ToChain.ChainID == b.ToChain.ChainID) &&
(p.FromToken == nil && b.FromToken == nil || p.FromToken.Symbol == b.FromToken.Symbol) {
found = true
break
}
}
if !found {
routeFound = false
break
}
}
if routeFound {
return append(allRoutes[:i], allRoutes[i+1:]...)
}
}
return nil
}
func getChainPriority(chainID uint64) int {
switch chainID {
case walletCommon.EthereumMainnet, walletCommon.EthereumSepolia:
return 1
case walletCommon.OptimismMainnet, walletCommon.OptimismSepolia:
return 2
case walletCommon.ArbitrumMainnet, walletCommon.ArbitrumSepolia:
return 3
default:
return 0
}
}
func getRoutePriority(route []*Path) int {
priority := 0
for _, path := range route {
priority += getChainPriority(path.FromChain.ChainID)
}
return priority
}
func (r *Router) resolveRoutes(ctx context.Context, input *RouteInputParams, candidates []*Path, balanceMap map[string]*big.Int) (suggestedRoutes *SuggestedRoutes, err error) {
func (r *Router) resolveRoutes(ctx context.Context, input *requests.RouteInputParams, candidates []*Path, balanceMap map[string]*big.Int) (suggestedRoutes *SuggestedRoutes, err error) {
var prices map[string]float64
if input.testsMode {
prices = input.testParams.tokenPrices
if input.TestsMode {
prices = input.TestParams.TokenPrices
} else {
prices, err = input.SendType.FetchPrices(r.marketManager, input.TokenID)
if err != nil {
@ -1283,8 +885,8 @@ func (r *Router) resolveRoutes(ctx context.Context, input *RouteInputParams, can
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 {
if input.SendType == sendtype.Transfer ||
input.SendType == sendtype.Bridge {
if hasPositiveBalance {
lastBestRouteWithPositiveBalance = bestRoute
lastBestRouteErr = err

View File

@ -0,0 +1,137 @@
package router
import (
"math"
"math/big"
"github.com/status-im/status-go/services/wallet/common"
"github.com/status-im/status-go/services/wallet/router/pathprocessor"
)
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, 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))
for _, route := range routes {
currentCost := big.NewFloat(0)
for _, path := range route {
tokenDenominator := big.NewFloat(math.Pow(10, float64(path.FromToken.Decimals)))
// calculate the cost of the path
nativeTokenPrice := new(big.Float).SetFloat64(nativeTokenPrice)
// tx fee
txFeeInEth := common.GweiToEth(common.WeiToGwei(path.TxFee.ToInt()))
pathCost := new(big.Float).Mul(txFeeInEth, nativeTokenPrice)
if path.TxL1Fee.ToInt().Cmp(pathprocessor.ZeroBigIntValue) > 0 {
txL1FeeInEth := common.GweiToEth(common.WeiToGwei(path.TxL1Fee.ToInt()))
pathCost.Add(pathCost, new(big.Float).Mul(txL1FeeInEth, nativeTokenPrice))
}
if path.TxBonderFees != nil && path.TxBonderFees.ToInt().Cmp(pathprocessor.ZeroBigIntValue) > 0 {
pathCost.Add(pathCost, new(big.Float).Mul(
new(big.Float).Quo(new(big.Float).SetInt(path.TxBonderFees.ToInt()), tokenDenominator),
new(big.Float).SetFloat64(tokenPrice)))
}
if path.TxTokenFees != nil && path.TxTokenFees.ToInt().Cmp(pathprocessor.ZeroBigIntValue) > 0 && path.FromToken != nil {
pathCost.Add(pathCost, new(big.Float).Mul(
new(big.Float).Quo(new(big.Float).SetInt(path.TxTokenFees.ToInt()), tokenDenominator),
new(big.Float).SetFloat64(tokenPrice)))
}
if path.ApprovalRequired {
// tx approval fee
approvalFeeInEth := common.GweiToEth(common.WeiToGwei(path.ApprovalFee.ToInt()))
pathCost.Add(pathCost, new(big.Float).Mul(approvalFeeInEth, nativeTokenPrice))
if path.ApprovalL1Fee.ToInt().Cmp(pathprocessor.ZeroBigIntValue) > 0 {
approvalL1FeeInEth := common.GweiToEth(common.WeiToGwei(path.ApprovalL1Fee.ToInt()))
pathCost.Add(pathCost, new(big.Float).Mul(approvalL1FeeInEth, nativeTokenPrice))
}
}
currentCost = new(big.Float).Add(currentCost, pathCost)
}
if currentCost.Cmp(bestCost) == -1 {
best = route
bestCost = currentCost
}
}
return best
}

View File

@ -17,10 +17,11 @@ import (
"github.com/status-im/status-go/services/wallet/bigint"
walletCommon "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/sendtype"
"github.com/status-im/status-go/services/wallet/token"
)
func (r *Router) requireApproval(ctx context.Context, sendType SendType, approvalContractAddress *common.Address, params pathprocessor.ProcessorInputParams) (
func (r *Router) requireApproval(ctx context.Context, sendType sendtype.SendType, approvalContractAddress *common.Address, params pathprocessor.ProcessorInputParams) (
bool, *big.Int, uint64, uint64, error) {
if sendType.IsCollectiblesTransfer() || sendType.IsEnsTransfer() || sendType.IsStickersTransfer() {
return false, nil, 0, 0, nil

View File

@ -0,0 +1,56 @@
package router
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/services/wallet/router/fees"
walletToken "github.com/status-im/status-go/services/wallet/token"
)
type Path struct {
ProcessorName string
FromChain *params.Network // Source chain
ToChain *params.Network // Destination chain
FromToken *walletToken.Token // Source token
ToToken *walletToken.Token // Destination token, set if applicable
AmountIn *hexutil.Big // Amount that will be sent from the source chain
AmountInLocked bool // Is the amount locked
AmountOut *hexutil.Big // Amount that will be received on the destination chain
SuggestedLevelsForMaxFeesPerGas *fees.MaxFeesLevels // Suggested max fees for the transaction (in ETH WEI)
MaxFeesPerGas *hexutil.Big // Max fees per gas (determined by client via GasFeeMode, in ETH WEI)
TxBaseFee *hexutil.Big // Base fee for the transaction (in ETH WEI)
TxPriorityFee *hexutil.Big // Priority fee for the transaction (in ETH WEI)
TxGasAmount uint64 // Gas used for the transaction
TxBonderFees *hexutil.Big // Bonder fees for the transaction - used for Hop bridge (in selected token)
TxTokenFees *hexutil.Big // Token fees for the transaction - used for bridges (represent the difference between the amount in and the amount out, in selected token)
TxFee *hexutil.Big // fee for the transaction (includes tx fee only, doesn't include approval fees, l1 fees, l1 approval fees, token fees or bonders fees, in ETH WEI)
TxL1Fee *hexutil.Big // L1 fee for the transaction - used for for transactions placed on L2 chains (in ETH WEI)
ApprovalRequired bool // Is approval required for the transaction
ApprovalAmountRequired *hexutil.Big // Amount required for the approval transaction
ApprovalContractAddress *common.Address // Address of the contract that needs to be approved
ApprovalBaseFee *hexutil.Big // Base fee for the approval transaction (in ETH WEI)
ApprovalPriorityFee *hexutil.Big // Priority fee for the approval transaction (in ETH WEI)
ApprovalGasAmount uint64 // Gas used for the approval transaction
ApprovalFee *hexutil.Big // Total fee for the approval transaction (includes approval tx fees only, doesn't include approval l1 fees, in ETH WEI)
ApprovalL1Fee *hexutil.Big // L1 fee for the approval transaction - used for for transactions placed on L2 chains (in ETH WEI)
TxTotalFee *hexutil.Big // Total fee for the transaction (includes tx fees, approval fees, l1 fees, l1 approval fees, in ETH WEI)
EstimatedTime fees.TransactionEstimation
requiredTokenBalance *big.Int // (in selected token)
requiredNativeBalance *big.Int // (in ETH WEI)
subtractFees bool
}
func (p *Path) Equal(o *Path) bool {
return p.FromChain.ChainID == o.FromChain.ChainID && p.ToChain.ChainID == o.ToChain.ChainID
}

View File

@ -266,7 +266,7 @@ func TestAmountOptions(t *testing.T) {
selectedFromChains, _, err := router.getSelectedChains(tt.input)
assert.NoError(t, err)
amountOptions, err := router.findOptionsForSendingAmount(tt.input, selectedFromChains, tt.input.testParams.balanceMap)
amountOptions, err := router.findOptionsForSendingAmount(tt.input, selectedFromChains, tt.input.TestParams.BalanceMap)
assert.NoError(t, err)
assert.Equal(t, len(tt.expectedAmountOptions), len(amountOptions))

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
package router
package sendtype
import (
"math/big"
@ -84,7 +84,7 @@ func (s SendType) FindToken(tokenManager *token.Manager, collectibles *collectib
}
// canUseProcessor is used to check if certain SendType can be used with a given path processor
func (s SendType) canUseProcessor(p pathprocessor.PathProcessor) bool {
func (s SendType) CanUseProcessor(p pathprocessor.PathProcessor) bool {
pathProcessorName := p.Name()
switch s {
case Transfer:
@ -111,7 +111,7 @@ func (s SendType) canUseProcessor(p pathprocessor.PathProcessor) bool {
}
}
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 s == Transfer {
if processorName != pathprocessor.ProcessorTransferName {
@ -129,7 +129,7 @@ func (s SendType) processZeroAmountInProcessor(amountIn *big.Int, amountOut *big
return true
}
func (s SendType) isAvailableBetween(from, to *params.Network) bool {
func (s SendType) IsAvailableBetween(from, to *params.Network) bool {
if s.IsCollectiblesTransfer() ||
s.IsEnsTransfer() ||
s.IsStickersTransfer() ||
@ -144,7 +144,7 @@ func (s SendType) isAvailableBetween(from, to *params.Network) bool {
return true
}
func (s SendType) isAvailableFor(network *params.Network) bool {
func (s SendType) IsAvailableFor(network *params.Network) bool {
// Set of network ChainIDs allowed for any type of transaction
allAllowedNetworks := map[uint64]bool{
walletCommon.EthereumMainnet: true,