feat: allow to lock amount in router
This commit is contained in:
parent
b4bdfd3df6
commit
6abbe98cd2
|
@ -347,9 +347,20 @@ func (api *API) GetTransactionEstimatedTime(ctx context.Context, chainID uint64,
|
||||||
return api.s.feesManager.transactionEstimatedTime(ctx, chainID, maxFeePerGas), nil
|
return api.s.feesManager.transactionEstimatedTime(ctx, chainID, maxFeePerGas), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) GetSuggestedRoutes(ctx context.Context, sendType SendType, account common.Address, amountIn *hexutil.Big, tokenSymbol string, disabledFromChainIDs, disabledToChaindIDs, preferedChainIDs []uint64, gasFeeMode GasFeeMode) (*SuggestedRoutes, error) {
|
func (api *API) GetSuggestedRoutes(
|
||||||
|
ctx context.Context,
|
||||||
|
sendType SendType,
|
||||||
|
account common.Address,
|
||||||
|
amountIn *hexutil.Big,
|
||||||
|
tokenSymbol string,
|
||||||
|
disabledFromChainIDs,
|
||||||
|
disabledToChaindIDs,
|
||||||
|
preferedChainIDs []uint64,
|
||||||
|
gasFeeMode GasFeeMode,
|
||||||
|
fromLockedAmount map[uint64]*hexutil.Big,
|
||||||
|
) (*SuggestedRoutes, error) {
|
||||||
log.Debug("call to GetSuggestedRoutes")
|
log.Debug("call to GetSuggestedRoutes")
|
||||||
return api.router.suggestedRoutes(ctx, sendType, account, amountIn.ToInt(), tokenSymbol, disabledFromChainIDs, disabledToChaindIDs, preferedChainIDs, gasFeeMode)
|
return api.router.suggestedRoutes(ctx, sendType, account, amountIn.ToInt(), tokenSymbol, disabledFromChainIDs, disabledToChaindIDs, preferedChainIDs, gasFeeMode, fromLockedAmount)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) GetDerivedAddressesForPath(ctx context.Context, password string, derivedFrom string, path string, pageSize int, pageNumber int) ([]*DerivedAddress, error) {
|
func (api *API) GetDerivedAddressesForPath(ctx context.Context, password string, derivedFrom string, path string, pageSize int, pageNumber int) ([]*DerivedAddress, error) {
|
||||||
|
|
|
@ -2,9 +2,11 @@ package wallet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
@ -106,6 +108,7 @@ type Path struct {
|
||||||
To *params.Network
|
To *params.Network
|
||||||
MaxAmountIn *hexutil.Big
|
MaxAmountIn *hexutil.Big
|
||||||
AmountIn *hexutil.Big
|
AmountIn *hexutil.Big
|
||||||
|
AmountInLocked bool
|
||||||
AmountOut *hexutil.Big
|
AmountOut *hexutil.Big
|
||||||
GasAmount uint64
|
GasAmount uint64
|
||||||
GasFees *SuggestedFees
|
GasFees *SuggestedFees
|
||||||
|
@ -115,6 +118,10 @@ type Path struct {
|
||||||
EstimatedTime TransactionEstimation
|
EstimatedTime TransactionEstimation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Path) Equal(o *Path) bool {
|
||||||
|
return p.From.ChainID == o.From.ChainID && p.To.ChainID == o.To.ChainID
|
||||||
|
}
|
||||||
|
|
||||||
type Graph = []*Node
|
type Graph = []*Node
|
||||||
|
|
||||||
type Node struct {
|
type Node struct {
|
||||||
|
@ -143,7 +150,7 @@ func buildGraph(AmountIn *big.Int, routes []*Path, level int, sourceChainIDs []u
|
||||||
|
|
||||||
newRoutes := make([]*Path, 0)
|
newRoutes := make([]*Path, 0)
|
||||||
for _, r := range routes {
|
for _, r := range routes {
|
||||||
if r.From.ChainID == route.From.ChainID && r.To.ChainID == route.To.ChainID {
|
if route.Equal(r) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
newRoutes = append(newRoutes, r)
|
newRoutes = append(newRoutes, r)
|
||||||
|
@ -167,30 +174,112 @@ func buildGraph(AmountIn *big.Int, routes []*Path, level int, sourceChainIDs []u
|
||||||
return graph
|
return graph
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n Node) findBest(level int) ([]*Path, *big.Float) {
|
func (n Node) buildAllRoutes() [][]*Path {
|
||||||
if len(n.Children) == 0 {
|
res := make([][]*Path, 0)
|
||||||
if n.Path == nil {
|
|
||||||
return []*Path{}, big.NewFloat(0)
|
|
||||||
}
|
|
||||||
return []*Path{n.Path}, n.Path.Cost
|
|
||||||
}
|
|
||||||
|
|
||||||
var best []*Path
|
if len(n.Children) == 0 && n.Path != nil {
|
||||||
bestTotalCost := big.NewFloat(math.Inf(1))
|
res = append(res, []*Path{n.Path})
|
||||||
|
}
|
||||||
|
|
||||||
for _, node := range n.Children {
|
for _, node := range n.Children {
|
||||||
routes, totalCost := node.findBest(level + 1)
|
for _, route := range node.buildAllRoutes() {
|
||||||
if totalCost.Cmp(bestTotalCost) < 0 {
|
extendedRoute := route
|
||||||
best = routes
|
if n.Path != nil {
|
||||||
bestTotalCost = totalCost
|
extendedRoute = append([]*Path{n.Path}, route...)
|
||||||
|
}
|
||||||
|
res = append(res, extendedRoute)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if n.Path == nil {
|
return res
|
||||||
return best, bestTotalCost
|
}
|
||||||
|
|
||||||
|
func filterRoutes(routes [][]*Path, amountIn *big.Int, fromLockedAmount map[uint64]*hexutil.Big) [][]*Path {
|
||||||
|
if len(fromLockedAmount) == 0 {
|
||||||
|
return routes
|
||||||
}
|
}
|
||||||
|
|
||||||
return append([]*Path{n.Path}, best...), new(big.Float).Add(bestTotalCost, n.Path.Cost)
|
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(zero) == 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 {
|
type SuggestedRoutes struct {
|
||||||
|
@ -200,7 +289,11 @@ type SuggestedRoutes struct {
|
||||||
NativeChainTokenPrice float64
|
NativeChainTokenPrice float64
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSuggestedRoutes(amountIn *big.Int, candidates []*Path) *SuggestedRoutes {
|
func newSuggestedRoutes(
|
||||||
|
amountIn *big.Int,
|
||||||
|
candidates []*Path,
|
||||||
|
fromLockedAmount map[uint64]*hexutil.Big,
|
||||||
|
) *SuggestedRoutes {
|
||||||
if len(candidates) == 0 {
|
if len(candidates) == 0 {
|
||||||
return &SuggestedRoutes{
|
return &SuggestedRoutes{
|
||||||
Candidates: candidates,
|
Candidates: candidates,
|
||||||
|
@ -212,14 +305,19 @@ func newSuggestedRoutes(amountIn *big.Int, candidates []*Path) *SuggestedRoutes
|
||||||
Path: nil,
|
Path: nil,
|
||||||
Children: buildGraph(amountIn, candidates, 0, []uint64{}),
|
Children: buildGraph(amountIn, candidates, 0, []uint64{}),
|
||||||
}
|
}
|
||||||
best, _ := node.findBest(0)
|
routes := node.buildAllRoutes()
|
||||||
|
routes = filterRoutes(routes, amountIn, fromLockedAmount)
|
||||||
|
best := findBest(routes)
|
||||||
|
|
||||||
if len(best) > 0 {
|
if len(best) > 0 {
|
||||||
|
sort.Slice(best, func(i, j int) bool {
|
||||||
|
return best[i].AmountInLocked
|
||||||
|
})
|
||||||
rest := new(big.Int).Set(amountIn)
|
rest := new(big.Int).Set(amountIn)
|
||||||
for _, path := range best {
|
for _, path := range best {
|
||||||
diff := new(big.Int).Sub(rest, path.MaxAmountIn.ToInt())
|
diff := new(big.Int).Sub(rest, path.MaxAmountIn.ToInt())
|
||||||
if diff.Cmp(zero) >= 0 {
|
if diff.Cmp(zero) >= 0 {
|
||||||
path.AmountIn = path.MaxAmountIn
|
path.AmountIn = (*hexutil.Big)(path.MaxAmountIn.ToInt())
|
||||||
} else {
|
} else {
|
||||||
path.AmountIn = (*hexutil.Big)(new(big.Int).Set(rest))
|
path.AmountIn = (*hexutil.Big)(new(big.Int).Set(rest))
|
||||||
}
|
}
|
||||||
|
@ -279,7 +377,18 @@ func (r *Router) estimateTimes(ctx context.Context, network *params.Network, gas
|
||||||
return r.s.feesManager.transactionEstimatedTime(ctx, network.ChainID, gasFees.MaxFeePerGasHigh)
|
return r.s.feesManager.transactionEstimatedTime(ctx, network.ChainID, gasFees.MaxFeePerGasHigh)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) suggestedRoutes(ctx context.Context, sendType SendType, account common.Address, amountIn *big.Int, tokenSymbol string, disabledFromChainIDs, disabledToChaindIDs, preferedChainIDs []uint64, gasFeeMode GasFeeMode) (*SuggestedRoutes, error) {
|
func (r *Router) suggestedRoutes(
|
||||||
|
ctx context.Context,
|
||||||
|
sendType SendType,
|
||||||
|
account common.Address,
|
||||||
|
amountIn *big.Int,
|
||||||
|
tokenSymbol string,
|
||||||
|
disabledFromChainIDs,
|
||||||
|
disabledToChaindIDs,
|
||||||
|
preferedChainIDs []uint64,
|
||||||
|
gasFeeMode GasFeeMode,
|
||||||
|
fromLockedAmount map[uint64]*hexutil.Big,
|
||||||
|
) (*SuggestedRoutes, error) {
|
||||||
areTestNetworksEnabled, err := r.s.accountsDB.GetTestNetworksEnabled()
|
areTestNetworksEnabled, err := r.s.accountsDB.GetTestNetworksEnabled()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -335,6 +444,14 @@ func (r *Router) suggestedRoutes(ctx context.Context, sendType SendType, account
|
||||||
return err
|
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, nativeToken, account)
|
nativeBalance, err := r.getBalance(ctx, network, nativeToken, account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -365,7 +482,7 @@ func (r *Router) suggestedRoutes(ctx context.Context, sendType SendType, account
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
can, err := bridge.Can(network, dest, token, balance)
|
can, err := bridge.Can(network, dest, token, maxAmountIn.ToInt())
|
||||||
if err != nil || !can {
|
if err != nil || !can {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -409,7 +526,7 @@ func (r *Router) suggestedRoutes(ctx context.Context, sendType SendType, account
|
||||||
BridgeName: bridge.Name(),
|
BridgeName: bridge.Name(),
|
||||||
From: network,
|
From: network,
|
||||||
To: dest,
|
To: dest,
|
||||||
MaxAmountIn: (*hexutil.Big)(balance),
|
MaxAmountIn: maxAmountIn,
|
||||||
AmountIn: (*hexutil.Big)(zero),
|
AmountIn: (*hexutil.Big)(zero),
|
||||||
AmountOut: (*hexutil.Big)(zero),
|
AmountOut: (*hexutil.Big)(zero),
|
||||||
GasAmount: gasLimit,
|
GasAmount: gasLimit,
|
||||||
|
@ -428,7 +545,7 @@ func (r *Router) suggestedRoutes(ctx context.Context, sendType SendType, account
|
||||||
|
|
||||||
group.Wait()
|
group.Wait()
|
||||||
|
|
||||||
suggestedRoutes := newSuggestedRoutes(amountIn, candidates)
|
suggestedRoutes := newSuggestedRoutes(amountIn, candidates, fromLockedAmount)
|
||||||
suggestedRoutes.TokenPrice = prices[tokenSymbol]
|
suggestedRoutes.TokenPrice = prices[tokenSymbol]
|
||||||
suggestedRoutes.NativeChainTokenPrice = prices["ETH"]
|
suggestedRoutes.NativeChainTokenPrice = prices["ETH"]
|
||||||
for _, path := range suggestedRoutes.Best {
|
for _, path := range suggestedRoutes.Best {
|
||||||
|
|
Loading…
Reference in New Issue