chore_: old router logic removed
This commit is contained in:
parent
bcaf8ebbe6
commit
e58787536a
|
@ -488,31 +488,6 @@ func gweiToWei(val *big.Float) *big.Int {
|
|||
return res
|
||||
}
|
||||
|
||||
func (api *API) GetSuggestedRoutes(
|
||||
ctx context.Context,
|
||||
sendType router.SendType,
|
||||
addrFrom common.Address,
|
||||
addrTo common.Address,
|
||||
amountIn *hexutil.Big,
|
||||
tokenID string,
|
||||
toTokenID string,
|
||||
disabledFromChainIDs,
|
||||
disabledToChainIDs,
|
||||
preferedChainIDs []uint64,
|
||||
gasFeeMode router.GasFeeMode,
|
||||
fromLockedAmount map[uint64]*hexutil.Big,
|
||||
) (*router.SuggestedRoutes, error) {
|
||||
log.Debug("call to GetSuggestedRoutes")
|
||||
|
||||
testnetMode, err := api.s.rpcClient.NetworkManager.GetTestNetworksEnabled()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return api.router.SuggestedRoutes(ctx, sendType, addrFrom, addrTo, amountIn.ToInt(), tokenID, toTokenID, disabledFromChainIDs,
|
||||
disabledToChainIDs, preferedChainIDs, gasFeeMode, fromLockedAmount, testnetMode)
|
||||
}
|
||||
|
||||
func (api *API) GetSuggestedRoutesV2(ctx context.Context, input *router.RouteInputParams) (*router.SuggestedRoutesV2, error) {
|
||||
log.Debug("call to GetSuggestedRoutesV2")
|
||||
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
package router
|
||||
|
||||
import (
|
||||
"github.com/status-im/status-go/params"
|
||||
)
|
||||
|
||||
func arrayContainsElement[T comparable](el T, arr []T) bool {
|
||||
for _, e := range arr {
|
||||
if e == el {
|
||||
return true
|
||||
}
|
||||
}
|
||||
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 isSingleChainOperation(fromChains []*params.Network, toChains []*params.Network) bool {
|
||||
return len(fromChains) == 1 &&
|
||||
len(toChains) == 1 &&
|
||||
fromChains[0].ChainID == toChains[0].ChainID
|
||||
}
|
|
@ -70,18 +70,6 @@ func (s *SuggestedFees) feeFor(mode GasFeeMode) *big.Int {
|
|||
return s.MaxFeesLevels.feeFor(mode)
|
||||
}
|
||||
|
||||
func (s *SuggestedFeesGwei) feeFor(mode GasFeeMode) *big.Float {
|
||||
if mode == GasFeeLow {
|
||||
return s.MaxFeePerGasLow
|
||||
}
|
||||
|
||||
if mode == GasFeeHigh {
|
||||
return s.MaxFeePerGasHigh
|
||||
}
|
||||
|
||||
return s.MaxFeePerGasMedium
|
||||
}
|
||||
|
||||
const inclusionThreshold = 0.95
|
||||
|
||||
type TransactionEstimation int
|
||||
|
@ -116,11 +104,6 @@ func gweiToEth(val *big.Float) *big.Float {
|
|||
return new(big.Float).Quo(val, big.NewFloat(1000000000))
|
||||
}
|
||||
|
||||
func gweiToWei(val *big.Float) *big.Int {
|
||||
res, _ := new(big.Float).Mul(val, big.NewFloat(1000000000)).Int(nil)
|
||||
return res
|
||||
}
|
||||
|
||||
func (f *FeeManager) SuggestedFees(ctx context.Context, chainID uint64) (*SuggestedFees, error) {
|
||||
backend, err := f.RPCClient.EthClient(chainID)
|
||||
if err != nil {
|
||||
|
|
|
@ -1,737 +0,0 @@
|
|||
package router
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"math"
|
||||
"math/big"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/status-im/status-go/contracts"
|
||||
gaspriceoracle "github.com/status-im/status-go/contracts/gas-price-oracle"
|
||||
"github.com/status-im/status-go/contracts/ierc20"
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/rpc"
|
||||
"github.com/status-im/status-go/services/ens"
|
||||
"github.com/status-im/status-go/services/stickers"
|
||||
"github.com/status-im/status-go/services/wallet/async"
|
||||
"github.com/status-im/status-go/services/wallet/bigint"
|
||||
"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/router/pathprocessor"
|
||||
"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/transactions"
|
||||
)
|
||||
|
||||
// //////////////////////////////////////////////////////////////////////////////
|
||||
// TODO: once new router is in place, remove this `router.go` file,
|
||||
// rename and make `router_v2.go` file the main and only file
|
||||
// //////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// TODO: remove the following two consts once we fully move to routerV2
|
||||
const EstimateUsername = "RandomUsername"
|
||||
const EstimatePubKey = "0x04bb2024ce5d72e45d4a4f8589ae657ef9745855006996115a23a1af88d536cf02c0524a585fce7bfa79d6a9669af735eda6205d6c7e5b3cdc2b8ff7b2fa1f0b56"
|
||||
|
||||
type Path struct {
|
||||
BridgeName string
|
||||
From *params.Network
|
||||
To *params.Network
|
||||
MaxAmountIn *hexutil.Big
|
||||
AmountIn *hexutil.Big
|
||||
AmountInLocked bool
|
||||
AmountOut *hexutil.Big
|
||||
GasAmount uint64
|
||||
GasFees *SuggestedFeesGwei
|
||||
BonderFees *hexutil.Big
|
||||
TokenFees *big.Float
|
||||
Cost *big.Float
|
||||
EstimatedTime TransactionEstimation
|
||||
ApprovalRequired bool
|
||||
ApprovalGasFees *big.Float
|
||||
ApprovalAmountRequired *hexutil.Big
|
||||
ApprovalContractAddress *common.Address
|
||||
}
|
||||
|
||||
func (p *Path) Equal(o *Path) bool {
|
||||
return p.From.ChainID == o.From.ChainID && p.To.ChainID == o.To.ChainID
|
||||
}
|
||||
|
||||
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.From.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.MaxAmountIn.ToInt())
|
||||
if newAmountIn.Sign() > 0 {
|
||||
newSourceChainIDs := make([]uint64, len(sourceChainIDs))
|
||||
copy(newSourceChainIDs, sourceChainIDs)
|
||||
newSourceChainIDs = append(newSourceChainIDs, route.From.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 filterRoutes(routes [][]*Path, amountIn *big.Int, fromLockedAmount map[uint64]*hexutil.Big) [][]*Path {
|
||||
if len(fromLockedAmount) == 0 {
|
||||
return routes
|
||||
}
|
||||
|
||||
filteredRoutesLevel1 := make([][]*Path, 0)
|
||||
for _, route := range routes {
|
||||
routeOk := true
|
||||
fromIncluded := make(map[uint64]bool)
|
||||
fromExcluded := make(map[uint64]bool)
|
||||
for chainID, amount := range fromLockedAmount {
|
||||
if amount.ToInt().Cmp(pathprocessor.ZeroBigIntValue) == 0 {
|
||||
fromExcluded[chainID] = false
|
||||
} else {
|
||||
fromIncluded[chainID] = false
|
||||
}
|
||||
|
||||
}
|
||||
for _, path := range route {
|
||||
if _, ok := fromExcluded[path.From.ChainID]; ok {
|
||||
routeOk = false
|
||||
break
|
||||
}
|
||||
if _, ok := fromIncluded[path.From.ChainID]; ok {
|
||||
fromIncluded[path.From.ChainID] = true
|
||||
}
|
||||
}
|
||||
for _, value := range fromIncluded {
|
||||
if !value {
|
||||
routeOk = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if routeOk {
|
||||
filteredRoutesLevel1 = append(filteredRoutesLevel1, route)
|
||||
}
|
||||
}
|
||||
|
||||
filteredRoutesLevel2 := make([][]*Path, 0)
|
||||
for _, route := range filteredRoutesLevel1 {
|
||||
routeOk := true
|
||||
for _, path := range route {
|
||||
if amount, ok := fromLockedAmount[path.From.ChainID]; ok {
|
||||
requiredAmountIn := new(big.Int).Sub(amountIn, amount.ToInt())
|
||||
restAmountIn := big.NewInt(0)
|
||||
|
||||
for _, otherPath := range route {
|
||||
if path.Equal(otherPath) {
|
||||
continue
|
||||
}
|
||||
restAmountIn = new(big.Int).Add(otherPath.MaxAmountIn.ToInt(), restAmountIn)
|
||||
}
|
||||
if restAmountIn.Cmp(requiredAmountIn) >= 0 {
|
||||
path.AmountIn = amount
|
||||
path.AmountInLocked = true
|
||||
} else {
|
||||
routeOk = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if routeOk {
|
||||
filteredRoutesLevel2 = append(filteredRoutesLevel2, route)
|
||||
}
|
||||
}
|
||||
|
||||
return filteredRoutesLevel2
|
||||
}
|
||||
|
||||
func findBest(routes [][]*Path) []*Path {
|
||||
var best []*Path
|
||||
bestCost := big.NewFloat(math.Inf(1))
|
||||
for _, route := range routes {
|
||||
currentCost := big.NewFloat(0)
|
||||
for _, path := range route {
|
||||
currentCost = new(big.Float).Add(currentCost, path.Cost)
|
||||
}
|
||||
|
||||
if currentCost.Cmp(bestCost) == -1 {
|
||||
best = route
|
||||
bestCost = currentCost
|
||||
}
|
||||
}
|
||||
|
||||
return best
|
||||
}
|
||||
|
||||
type SuggestedRoutes struct {
|
||||
Best []*Path
|
||||
Candidates []*Path
|
||||
TokenPrice float64
|
||||
NativeChainTokenPrice float64
|
||||
}
|
||||
|
||||
func newSuggestedRoutes(
|
||||
amountIn *big.Int,
|
||||
candidates []*Path,
|
||||
fromLockedAmount map[uint64]*hexutil.Big,
|
||||
) *SuggestedRoutes {
|
||||
if len(candidates) == 0 {
|
||||
return &SuggestedRoutes{
|
||||
Candidates: candidates,
|
||||
Best: candidates,
|
||||
}
|
||||
}
|
||||
|
||||
node := &Node{
|
||||
Path: nil,
|
||||
Children: buildGraph(amountIn, candidates, 0, []uint64{}),
|
||||
}
|
||||
routes := node.buildAllRoutes()
|
||||
routes = filterRoutes(routes, amountIn, fromLockedAmount)
|
||||
best := findBest(routes)
|
||||
|
||||
if len(best) > 0 {
|
||||
sort.Slice(best, func(i, j int) bool {
|
||||
return best[i].AmountInLocked
|
||||
})
|
||||
rest := new(big.Int).Set(amountIn)
|
||||
for _, path := range best {
|
||||
diff := new(big.Int).Sub(rest, path.MaxAmountIn.ToInt())
|
||||
if diff.Cmp(pathprocessor.ZeroBigIntValue) >= 0 {
|
||||
path.AmountIn = (*hexutil.Big)(path.MaxAmountIn.ToInt())
|
||||
} else {
|
||||
path.AmountIn = (*hexutil.Big)(new(big.Int).Set(rest))
|
||||
}
|
||||
rest.Sub(rest, path.AmountIn.ToInt())
|
||||
}
|
||||
}
|
||||
|
||||
return &SuggestedRoutes{
|
||||
Candidates: candidates,
|
||||
Best: best,
|
||||
}
|
||||
}
|
||||
|
||||
func NewRouter(rpcClient *rpc.Client, transactor *transactions.Transactor, tokenManager *token.Manager, marketManager *market.Manager,
|
||||
collectibles *collectibles.Service, collectiblesManager *collectibles.Manager, ensService *ens.Service, stickersService *stickers.Service) *Router {
|
||||
processors := make(map[string]pathprocessor.PathProcessor)
|
||||
|
||||
return &Router{
|
||||
rpcClient: rpcClient,
|
||||
tokenManager: tokenManager,
|
||||
marketManager: marketManager,
|
||||
collectiblesService: collectibles,
|
||||
collectiblesManager: collectiblesManager,
|
||||
ensService: ensService,
|
||||
stickersService: stickersService,
|
||||
feesManager: &FeeManager{rpcClient},
|
||||
pathProcessors: processors,
|
||||
scheduler: async.NewScheduler(),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Router) AddPathProcessor(processor pathprocessor.PathProcessor) {
|
||||
r.pathProcessors[processor.Name()] = processor
|
||||
}
|
||||
|
||||
func (r *Router) Stop() {
|
||||
r.scheduler.Stop()
|
||||
}
|
||||
|
||||
func (r *Router) GetFeesManager() *FeeManager {
|
||||
return r.feesManager
|
||||
}
|
||||
|
||||
func (r *Router) GetPathProcessors() map[string]pathprocessor.PathProcessor {
|
||||
return r.pathProcessors
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
type Router struct {
|
||||
rpcClient *rpc.Client
|
||||
tokenManager *token.Manager
|
||||
marketManager *market.Manager
|
||||
collectiblesService *collectibles.Service
|
||||
collectiblesManager *collectibles.Manager
|
||||
ensService *ens.Service
|
||||
stickersService *stickers.Service
|
||||
feesManager *FeeManager
|
||||
pathProcessors map[string]pathprocessor.PathProcessor
|
||||
scheduler *async.Scheduler
|
||||
}
|
||||
|
||||
func (r *Router) requireApproval(ctx context.Context, 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
|
||||
}
|
||||
|
||||
if params.FromToken.IsNative() {
|
||||
return false, nil, 0, 0, nil
|
||||
}
|
||||
|
||||
contractMaker, err := contracts.NewContractMaker(r.rpcClient)
|
||||
if err != nil {
|
||||
return false, nil, 0, 0, err
|
||||
}
|
||||
|
||||
contract, err := contractMaker.NewERC20(params.FromChain.ChainID, params.FromToken.Address)
|
||||
if err != nil {
|
||||
return false, nil, 0, 0, err
|
||||
}
|
||||
|
||||
if approvalContractAddress == nil || *approvalContractAddress == pathprocessor.ZeroAddress {
|
||||
return false, nil, 0, 0, nil
|
||||
}
|
||||
|
||||
if params.TestsMode {
|
||||
return true, params.AmountIn, params.TestApprovalGasEstimation, params.TestApprovalL1Fee, nil
|
||||
}
|
||||
|
||||
allowance, err := contract.Allowance(&bind.CallOpts{
|
||||
Context: ctx,
|
||||
}, params.FromAddr, *approvalContractAddress)
|
||||
|
||||
if err != nil {
|
||||
return false, nil, 0, 0, err
|
||||
}
|
||||
|
||||
if allowance.Cmp(params.AmountIn) >= 0 {
|
||||
return false, nil, 0, 0, nil
|
||||
}
|
||||
|
||||
ethClient, err := r.rpcClient.EthClient(params.FromChain.ChainID)
|
||||
if err != nil {
|
||||
return false, nil, 0, 0, err
|
||||
}
|
||||
|
||||
erc20ABI, err := abi.JSON(strings.NewReader(ierc20.IERC20ABI))
|
||||
if err != nil {
|
||||
return false, nil, 0, 0, err
|
||||
}
|
||||
|
||||
data, err := erc20ABI.Pack("approve", approvalContractAddress, params.AmountIn)
|
||||
if err != nil {
|
||||
return false, nil, 0, 0, err
|
||||
}
|
||||
|
||||
estimate, err := ethClient.EstimateGas(context.Background(), ethereum.CallMsg{
|
||||
From: params.FromAddr,
|
||||
To: ¶ms.FromToken.Address,
|
||||
Value: pathprocessor.ZeroBigIntValue,
|
||||
Data: data,
|
||||
})
|
||||
if err != nil {
|
||||
return false, nil, 0, 0, err
|
||||
}
|
||||
|
||||
// fetching l1 fee
|
||||
var l1Fee uint64
|
||||
oracleContractAddress, err := gaspriceoracle.ContractAddress(params.FromChain.ChainID)
|
||||
if err == nil {
|
||||
oracleContract, err := gaspriceoracle.NewGaspriceoracleCaller(oracleContractAddress, ethClient)
|
||||
if err != nil {
|
||||
return false, nil, 0, 0, err
|
||||
}
|
||||
|
||||
callOpt := &bind.CallOpts{}
|
||||
|
||||
l1FeeResult, _ := oracleContract.GetL1Fee(callOpt, data)
|
||||
l1Fee = l1FeeResult.Uint64()
|
||||
}
|
||||
|
||||
return true, params.AmountIn, estimate, l1Fee, nil
|
||||
}
|
||||
|
||||
func (r *Router) getBalance(ctx context.Context, chainID uint64, token *token.Token, account common.Address) (*big.Int, error) {
|
||||
client, err := r.rpcClient.EthClient(chainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.tokenManager.GetBalance(ctx, client, account, token.Address)
|
||||
}
|
||||
|
||||
func (r *Router) getERC1155Balance(ctx context.Context, network *params.Network, token *token.Token, account common.Address) (*big.Int, error) {
|
||||
tokenID, success := new(big.Int).SetString(token.Symbol, 10)
|
||||
if !success {
|
||||
return nil, errors.New("failed to convert token symbol to big.Int")
|
||||
}
|
||||
|
||||
balances, err := r.collectiblesManager.FetchERC1155Balances(
|
||||
ctx,
|
||||
account,
|
||||
walletCommon.ChainID(network.ChainID),
|
||||
token.Address,
|
||||
[]*bigint.BigInt{&bigint.BigInt{Int: tokenID}},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(balances) != 1 || balances[0] == nil {
|
||||
return nil, errors.New("invalid ERC1155 balance fetch response")
|
||||
}
|
||||
|
||||
return balances[0].Int, nil
|
||||
}
|
||||
|
||||
func (r *Router) SuggestedRoutes(
|
||||
ctx context.Context,
|
||||
sendType SendType,
|
||||
addrFrom common.Address,
|
||||
addrTo common.Address,
|
||||
amountIn *big.Int,
|
||||
tokenID string,
|
||||
toTokenID string,
|
||||
disabledFromChainIDs,
|
||||
disabledToChainIDs,
|
||||
preferedChainIDs []uint64,
|
||||
gasFeeMode GasFeeMode,
|
||||
fromLockedAmount map[uint64]*hexutil.Big,
|
||||
testnetMode bool,
|
||||
) (*SuggestedRoutes, error) {
|
||||
|
||||
networks, err := r.rpcClient.NetworkManager.Get(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
prices, err := sendType.FetchPrices(r.marketManager, tokenID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var (
|
||||
group = async.NewAtomicGroup(ctx)
|
||||
mu sync.Mutex
|
||||
candidates = make([]*Path, 0)
|
||||
)
|
||||
|
||||
for networkIdx := range networks {
|
||||
network := networks[networkIdx]
|
||||
if network.IsTest != testnetMode {
|
||||
continue
|
||||
}
|
||||
|
||||
if arrayContainsElement(network.ChainID, disabledFromChainIDs) {
|
||||
continue
|
||||
}
|
||||
|
||||
if !sendType.isAvailableFor(network) {
|
||||
continue
|
||||
}
|
||||
|
||||
token := sendType.FindToken(r.tokenManager, r.collectiblesService, addrFrom, network, tokenID)
|
||||
if token == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var toToken *walletToken.Token
|
||||
if sendType == Swap {
|
||||
toToken = sendType.FindToken(r.tokenManager, r.collectiblesService, common.Address{}, network, toTokenID)
|
||||
}
|
||||
|
||||
nativeToken := r.tokenManager.FindToken(network, network.NativeCurrencySymbol)
|
||||
if nativeToken == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
group.Add(func(c context.Context) error {
|
||||
gasFees, err := r.feesManager.SuggestedFeesGwei(ctx, network.ChainID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Default value is 1 as in case of erc721 as we built the token we are sure the account owns it
|
||||
balance := big.NewInt(1)
|
||||
if sendType == ERC1155Transfer {
|
||||
balance, err = r.getERC1155Balance(ctx, network, token, addrFrom)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if sendType != ERC721Transfer {
|
||||
balance, err = r.getBalance(ctx, network.ChainID, token, addrFrom)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
maxAmountIn := (*hexutil.Big)(balance)
|
||||
if amount, ok := fromLockedAmount[network.ChainID]; ok {
|
||||
if amount.ToInt().Cmp(balance) == 1 {
|
||||
return errors.New("locked amount cannot be bigger than balance")
|
||||
}
|
||||
maxAmountIn = amount
|
||||
}
|
||||
|
||||
nativeBalance, err := r.getBalance(ctx, network.ChainID, nativeToken, addrFrom)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
maxFees := gasFees.feeFor(gasFeeMode)
|
||||
|
||||
estimatedTime := r.feesManager.TransactionEstimatedTime(ctx, network.ChainID, gweiToWei(maxFees))
|
||||
for _, pProcessor := range r.pathProcessors {
|
||||
// Skip processors that are added because of the Router V2, to not break the current functionality
|
||||
if pProcessor.Name() == pathprocessor.ProcessorENSRegisterName ||
|
||||
pProcessor.Name() == pathprocessor.ProcessorENSReleaseName ||
|
||||
pProcessor.Name() == pathprocessor.ProcessorENSPublicKeyName ||
|
||||
pProcessor.Name() == pathprocessor.ProcessorStickersBuyName {
|
||||
continue
|
||||
}
|
||||
|
||||
if !sendType.canUseProcessor(pProcessor) {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, dest := range networks {
|
||||
if dest.IsTest != testnetMode {
|
||||
continue
|
||||
}
|
||||
|
||||
if !sendType.isAvailableFor(network) {
|
||||
continue
|
||||
}
|
||||
|
||||
if !sendType.isAvailableBetween(network, dest) {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(preferedChainIDs) > 0 && !arrayContainsElement(dest.ChainID, preferedChainIDs) {
|
||||
continue
|
||||
}
|
||||
if arrayContainsElement(dest.ChainID, disabledToChainIDs) {
|
||||
continue
|
||||
}
|
||||
|
||||
processorInputParams := pathprocessor.ProcessorInputParams{
|
||||
FromChain: network,
|
||||
ToChain: dest,
|
||||
FromToken: token,
|
||||
ToToken: toToken,
|
||||
ToAddr: addrTo,
|
||||
FromAddr: addrFrom,
|
||||
AmountIn: amountIn,
|
||||
}
|
||||
|
||||
can, err := pProcessor.AvailableFor(processorInputParams)
|
||||
if err != nil || !can {
|
||||
continue
|
||||
}
|
||||
if maxAmountIn.ToInt().Cmp(pathprocessor.ZeroBigIntValue) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
bonderFees, tokenFees, err := pProcessor.CalculateFees(processorInputParams)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if bonderFees.Cmp(pathprocessor.ZeroBigIntValue) != 0 {
|
||||
if maxAmountIn.ToInt().Cmp(amountIn) >= 0 {
|
||||
if bonderFees.Cmp(amountIn) >= 0 {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
if bonderFees.Cmp(maxAmountIn.ToInt()) >= 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
gasLimit := uint64(0)
|
||||
if sendType.isTransfer(false) {
|
||||
gasLimit, err = pProcessor.EstimateGas(processorInputParams)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
gasLimit = sendType.EstimateGas(r.ensService, r.stickersService, network, addrFrom, tokenID)
|
||||
}
|
||||
|
||||
approvalContractAddress, err := pProcessor.GetContractAddress(processorInputParams)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
approvalRequired, approvalAmountRequired, approvalGasLimit, l1ApprovalFee, err := r.requireApproval(ctx, sendType, &approvalContractAddress, processorInputParams)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var l1GasFeeWei uint64
|
||||
if sendType.needL1Fee() {
|
||||
txInputData, err := pProcessor.PackTxInputData(processorInputParams)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
l1GasFeeWei, _ = r.feesManager.GetL1Fee(ctx, network.ChainID, txInputData)
|
||||
l1GasFeeWei += l1ApprovalFee
|
||||
}
|
||||
|
||||
gasFees.L1GasFee = weiToGwei(big.NewInt(int64(l1GasFeeWei)))
|
||||
|
||||
requiredNativeBalance := new(big.Int).Mul(gweiToWei(maxFees), big.NewInt(int64(gasLimit)))
|
||||
requiredNativeBalance.Add(requiredNativeBalance, new(big.Int).Mul(gweiToWei(maxFees), big.NewInt(int64(approvalGasLimit))))
|
||||
requiredNativeBalance.Add(requiredNativeBalance, big.NewInt(int64(l1GasFeeWei))) // add l1Fee to requiredNativeBalance, in case of L1 chain l1Fee is 0
|
||||
|
||||
if nativeBalance.Cmp(requiredNativeBalance) <= 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Removed the required fees from maxAMount in case of native token tx
|
||||
if token.IsNative() {
|
||||
maxAmountIn = (*hexutil.Big)(new(big.Int).Sub(maxAmountIn.ToInt(), requiredNativeBalance))
|
||||
}
|
||||
|
||||
ethPrice := big.NewFloat(prices["ETH"])
|
||||
|
||||
approvalGasFees := new(big.Float).Mul(gweiToEth(maxFees), big.NewFloat((float64(approvalGasLimit))))
|
||||
|
||||
approvalGasCost := new(big.Float)
|
||||
approvalGasCost.Mul(approvalGasFees, ethPrice)
|
||||
|
||||
l1GasCost := new(big.Float)
|
||||
l1GasCost.Mul(gasFees.L1GasFee, ethPrice)
|
||||
|
||||
gasCost := new(big.Float)
|
||||
gasCost.Mul(new(big.Float).Mul(gweiToEth(maxFees), big.NewFloat(float64(gasLimit))), ethPrice)
|
||||
|
||||
tokenFeesAsFloat := new(big.Float).Quo(
|
||||
new(big.Float).SetInt(tokenFees),
|
||||
big.NewFloat(math.Pow(10, float64(token.Decimals))),
|
||||
)
|
||||
tokenCost := new(big.Float)
|
||||
tokenCost.Mul(tokenFeesAsFloat, big.NewFloat(prices[tokenID]))
|
||||
|
||||
cost := new(big.Float)
|
||||
cost.Add(tokenCost, gasCost)
|
||||
cost.Add(cost, approvalGasCost)
|
||||
cost.Add(cost, l1GasCost)
|
||||
mu.Lock()
|
||||
candidates = append(candidates, &Path{
|
||||
BridgeName: pProcessor.Name(),
|
||||
From: network,
|
||||
To: dest,
|
||||
MaxAmountIn: maxAmountIn,
|
||||
AmountIn: (*hexutil.Big)(pathprocessor.ZeroBigIntValue),
|
||||
AmountOut: (*hexutil.Big)(pathprocessor.ZeroBigIntValue),
|
||||
GasAmount: gasLimit,
|
||||
GasFees: gasFees,
|
||||
BonderFees: (*hexutil.Big)(bonderFees),
|
||||
TokenFees: tokenFeesAsFloat,
|
||||
Cost: cost,
|
||||
EstimatedTime: estimatedTime,
|
||||
ApprovalRequired: approvalRequired,
|
||||
ApprovalGasFees: approvalGasFees,
|
||||
ApprovalAmountRequired: (*hexutil.Big)(approvalAmountRequired),
|
||||
ApprovalContractAddress: &approvalContractAddress,
|
||||
})
|
||||
mu.Unlock()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
group.Wait()
|
||||
|
||||
suggestedRoutes := newSuggestedRoutes(amountIn, candidates, fromLockedAmount)
|
||||
suggestedRoutes.TokenPrice = prices[tokenID]
|
||||
suggestedRoutes.NativeChainTokenPrice = prices["ETH"]
|
||||
for _, path := range suggestedRoutes.Best {
|
||||
processorInputParams := pathprocessor.ProcessorInputParams{
|
||||
FromChain: path.From,
|
||||
ToChain: path.To,
|
||||
AmountIn: path.AmountIn.ToInt(),
|
||||
FromToken: &token.Token{
|
||||
Symbol: tokenID,
|
||||
},
|
||||
ToToken: &token.Token{
|
||||
Symbol: toTokenID,
|
||||
},
|
||||
}
|
||||
|
||||
amountOut, err := r.pathProcessors[path.BridgeName].CalculateAmountOut(processorInputParams)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
path.AmountOut = (*hexutil.Big)(amountOut)
|
||||
}
|
||||
|
||||
return suggestedRoutes, nil
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
package router
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"math/big"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/status-im/status-go/contracts"
|
||||
gaspriceoracle "github.com/status-im/status-go/contracts/gas-price-oracle"
|
||||
"github.com/status-im/status-go/contracts/ierc20"
|
||||
"github.com/status-im/status-go/params"
|
||||
"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/token"
|
||||
)
|
||||
|
||||
func (r *Router) requireApproval(ctx context.Context, 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
|
||||
}
|
||||
|
||||
if params.FromToken.IsNative() {
|
||||
return false, nil, 0, 0, nil
|
||||
}
|
||||
|
||||
contractMaker, err := contracts.NewContractMaker(r.rpcClient)
|
||||
if err != nil {
|
||||
return false, nil, 0, 0, err
|
||||
}
|
||||
|
||||
contract, err := contractMaker.NewERC20(params.FromChain.ChainID, params.FromToken.Address)
|
||||
if err != nil {
|
||||
return false, nil, 0, 0, err
|
||||
}
|
||||
|
||||
if approvalContractAddress == nil || *approvalContractAddress == pathprocessor.ZeroAddress {
|
||||
return false, nil, 0, 0, nil
|
||||
}
|
||||
|
||||
if params.TestsMode {
|
||||
return true, params.AmountIn, params.TestApprovalGasEstimation, params.TestApprovalL1Fee, nil
|
||||
}
|
||||
|
||||
allowance, err := contract.Allowance(&bind.CallOpts{
|
||||
Context: ctx,
|
||||
}, params.FromAddr, *approvalContractAddress)
|
||||
|
||||
if err != nil {
|
||||
return false, nil, 0, 0, err
|
||||
}
|
||||
|
||||
if allowance.Cmp(params.AmountIn) >= 0 {
|
||||
return false, nil, 0, 0, nil
|
||||
}
|
||||
|
||||
ethClient, err := r.rpcClient.EthClient(params.FromChain.ChainID)
|
||||
if err != nil {
|
||||
return false, nil, 0, 0, err
|
||||
}
|
||||
|
||||
erc20ABI, err := abi.JSON(strings.NewReader(ierc20.IERC20ABI))
|
||||
if err != nil {
|
||||
return false, nil, 0, 0, err
|
||||
}
|
||||
|
||||
data, err := erc20ABI.Pack("approve", approvalContractAddress, params.AmountIn)
|
||||
if err != nil {
|
||||
return false, nil, 0, 0, err
|
||||
}
|
||||
|
||||
estimate, err := ethClient.EstimateGas(context.Background(), ethereum.CallMsg{
|
||||
From: params.FromAddr,
|
||||
To: ¶ms.FromToken.Address,
|
||||
Value: pathprocessor.ZeroBigIntValue,
|
||||
Data: data,
|
||||
})
|
||||
if err != nil {
|
||||
return false, nil, 0, 0, err
|
||||
}
|
||||
|
||||
// fetching l1 fee
|
||||
var l1Fee uint64
|
||||
oracleContractAddress, err := gaspriceoracle.ContractAddress(params.FromChain.ChainID)
|
||||
if err == nil {
|
||||
oracleContract, err := gaspriceoracle.NewGaspriceoracleCaller(oracleContractAddress, ethClient)
|
||||
if err != nil {
|
||||
return false, nil, 0, 0, err
|
||||
}
|
||||
|
||||
callOpt := &bind.CallOpts{}
|
||||
|
||||
l1FeeResult, _ := oracleContract.GetL1Fee(callOpt, data)
|
||||
l1Fee = l1FeeResult.Uint64()
|
||||
}
|
||||
|
||||
return true, params.AmountIn, estimate, l1Fee, nil
|
||||
}
|
||||
|
||||
func (r *Router) getERC1155Balance(ctx context.Context, network *params.Network, token *token.Token, account common.Address) (*big.Int, error) {
|
||||
tokenID, success := new(big.Int).SetString(token.Symbol, 10)
|
||||
if !success {
|
||||
return nil, errors.New("failed to convert token symbol to big.Int")
|
||||
}
|
||||
|
||||
balances, err := r.collectiblesManager.FetchERC1155Balances(
|
||||
ctx,
|
||||
account,
|
||||
walletCommon.ChainID(network.ChainID),
|
||||
token.Address,
|
||||
[]*bigint.BigInt{&bigint.BigInt{Int: tokenID}},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(balances) != 1 || balances[0] == nil {
|
||||
return nil, errors.New("invalid ERC1155 balance fetch response")
|
||||
}
|
||||
|
||||
return balances[0].Int, nil
|
||||
}
|
||||
|
||||
func (r *Router) getBalance(ctx context.Context, chainID uint64, token *token.Token, account common.Address) (*big.Int, error) {
|
||||
client, err := r.rpcClient.EthClient(chainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.tokenManager.GetBalance(ctx, client, account, token.Address)
|
||||
}
|
|
@ -1,24 +1,16 @@
|
|||
package router
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/services/ens"
|
||||
"github.com/status-im/status-go/services/stickers"
|
||||
"github.com/status-im/status-go/services/wallet/bigint"
|
||||
"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/router/pathprocessor"
|
||||
"github.com/status-im/status-go/services/wallet/token"
|
||||
"github.com/status-im/status-go/transactions"
|
||||
)
|
||||
|
||||
type SendType int
|
||||
|
@ -91,18 +83,6 @@ func (s SendType) FindToken(tokenManager *token.Manager, collectibles *collectib
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: remove this function once we fully move to routerV2
|
||||
func (s SendType) isTransfer(routerV2Logic bool) bool {
|
||||
return s == Transfer ||
|
||||
s == Bridge && routerV2Logic ||
|
||||
s == Swap ||
|
||||
s.IsCollectiblesTransfer()
|
||||
}
|
||||
|
||||
func (s SendType) needL1Fee() bool {
|
||||
return !s.IsEnsTransfer() && !s.IsStickersTransfer()
|
||||
}
|
||||
|
||||
// canUseProcessor is used to check if certain SendType can be used with a given path processor
|
||||
func (s SendType) canUseProcessor(p pathprocessor.PathProcessor) bool {
|
||||
pathProcessorName := p.Name()
|
||||
|
@ -195,44 +175,3 @@ func (s SendType) isAvailableFor(network *params.Network) bool {
|
|||
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO: remove this function once we fully move to routerV2
|
||||
func (s SendType) EstimateGas(ensService *ens.Service, stickersService *stickers.Service, network *params.Network, from common.Address, tokenID string) uint64 {
|
||||
tx := transactions.SendTxArgs{
|
||||
From: (types.Address)(from),
|
||||
Value: (*hexutil.Big)(pathprocessor.ZeroBigIntValue),
|
||||
}
|
||||
switch s {
|
||||
case ENSRegister:
|
||||
estimate, err := ensService.API().RegisterEstimate(context.Background(), network.ChainID, tx, EstimateUsername, EstimatePubKey)
|
||||
if err != nil {
|
||||
return 400000
|
||||
}
|
||||
return estimate
|
||||
|
||||
case ENSRelease:
|
||||
estimate, err := ensService.API().ReleaseEstimate(context.Background(), network.ChainID, tx, EstimateUsername)
|
||||
if err != nil {
|
||||
return 200000
|
||||
}
|
||||
return estimate
|
||||
|
||||
case ENSSetPubKey:
|
||||
estimate, err := ensService.API().SetPubKeyEstimate(context.Background(), network.ChainID, tx, fmt.Sprint(EstimateUsername, ".stateofus.eth"), EstimatePubKey)
|
||||
if err != nil {
|
||||
return 400000
|
||||
}
|
||||
return estimate
|
||||
|
||||
case StickersBuy:
|
||||
packID := &bigint.BigInt{Int: big.NewInt(2)}
|
||||
estimate, err := stickersService.API().BuyEstimate(context.Background(), network.ChainID, (types.Address)(from), packID)
|
||||
if err != nil {
|
||||
return 400000
|
||||
}
|
||||
return estimate
|
||||
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,12 +14,18 @@ import (
|
|||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/status-im/status-go/errors"
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/rpc"
|
||||
"github.com/status-im/status-go/services/ens"
|
||||
"github.com/status-im/status-go/services/stickers"
|
||||
"github.com/status-im/status-go/services/wallet/async"
|
||||
"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/router/pathprocessor"
|
||||
"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 (
|
||||
|
@ -169,6 +175,53 @@ type NodeV2 struct {
|
|||
Children GraphV2
|
||||
}
|
||||
|
||||
type Router struct {
|
||||
rpcClient *rpc.Client
|
||||
tokenManager *token.Manager
|
||||
marketManager *market.Manager
|
||||
collectiblesService *collectibles.Service
|
||||
collectiblesManager *collectibles.Manager
|
||||
ensService *ens.Service
|
||||
stickersService *stickers.Service
|
||||
feesManager *FeeManager
|
||||
pathProcessors map[string]pathprocessor.PathProcessor
|
||||
scheduler *async.Scheduler
|
||||
}
|
||||
|
||||
func NewRouter(rpcClient *rpc.Client, transactor *transactions.Transactor, tokenManager *token.Manager, marketManager *market.Manager,
|
||||
collectibles *collectibles.Service, collectiblesManager *collectibles.Manager, ensService *ens.Service, stickersService *stickers.Service) *Router {
|
||||
processors := make(map[string]pathprocessor.PathProcessor)
|
||||
|
||||
return &Router{
|
||||
rpcClient: rpcClient,
|
||||
tokenManager: tokenManager,
|
||||
marketManager: marketManager,
|
||||
collectiblesService: collectibles,
|
||||
collectiblesManager: collectiblesManager,
|
||||
ensService: ensService,
|
||||
stickersService: stickersService,
|
||||
feesManager: &FeeManager{rpcClient},
|
||||
pathProcessors: processors,
|
||||
scheduler: async.NewScheduler(),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Router) AddPathProcessor(processor pathprocessor.PathProcessor) {
|
||||
r.pathProcessors[processor.Name()] = processor
|
||||
}
|
||||
|
||||
func (r *Router) Stop() {
|
||||
r.scheduler.Stop()
|
||||
}
|
||||
|
||||
func (r *Router) GetFeesManager() *FeeManager {
|
||||
return r.feesManager
|
||||
}
|
||||
|
||||
func (r *Router) GetPathProcessors() map[string]pathprocessor.PathProcessor {
|
||||
return r.pathProcessors
|
||||
}
|
||||
|
||||
func newSuggestedRoutesV2(
|
||||
uuid string,
|
||||
amountIn *big.Int,
|
||||
|
|
Loading…
Reference in New Issue