2024-08-29 13:40:30 +00:00
|
|
|
package router
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/log"
|
2024-09-26 22:37:32 +00:00
|
|
|
gocommon "github.com/status-im/status-go/common"
|
2024-08-29 13:40:30 +00:00
|
|
|
"github.com/status-im/status-go/rpc/chain"
|
|
|
|
walletCommon "github.com/status-im/status-go/services/wallet/common"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2024-09-25 12:27:04 +00:00
|
|
|
newBlockCheckIntervalMainnet = 3 * time.Second
|
|
|
|
newBlockCheckIntervalOptimism = 1 * time.Second
|
|
|
|
newBlockCheckIntervalArbitrum = 200 * time.Millisecond
|
|
|
|
newBlockCheckIntervalAnvilMainnet = 2 * time.Second
|
2024-08-29 13:40:30 +00:00
|
|
|
|
2024-09-25 12:27:04 +00:00
|
|
|
feeRecalculationTimeout = 5 * time.Minute
|
|
|
|
feeRecalculationAnvilTimeout = 5 * time.Second
|
2024-08-29 13:40:30 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type fetchingLastBlock struct {
|
|
|
|
client chain.ClientInterface
|
|
|
|
lastBlock uint64
|
|
|
|
closeCh chan struct{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Router) subscribeForUdates(chainID uint64) error {
|
|
|
|
if _, ok := r.clientsForUpdatesPerChains.Load(chainID); ok {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
ethClient, err := r.rpcClient.EthClient(chainID)
|
|
|
|
if err != nil {
|
|
|
|
log.Error("Failed to get eth client", "error", err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
flb := fetchingLastBlock{
|
|
|
|
client: ethClient,
|
|
|
|
lastBlock: 0,
|
|
|
|
closeCh: make(chan struct{}),
|
|
|
|
}
|
|
|
|
r.clientsForUpdatesPerChains.Store(chainID, flb)
|
|
|
|
|
2024-09-25 12:27:04 +00:00
|
|
|
timeout := feeRecalculationTimeout
|
|
|
|
if chainID == walletCommon.AnvilMainnet {
|
|
|
|
timeout = feeRecalculationAnvilTimeout
|
|
|
|
}
|
|
|
|
r.startTimeoutForUpdates(flb.closeCh, timeout)
|
2024-08-29 13:40:30 +00:00
|
|
|
|
|
|
|
var ticker *time.Ticker
|
|
|
|
switch chainID {
|
|
|
|
case walletCommon.EthereumMainnet,
|
|
|
|
walletCommon.EthereumSepolia:
|
|
|
|
ticker = time.NewTicker(newBlockCheckIntervalMainnet)
|
|
|
|
case walletCommon.OptimismMainnet,
|
|
|
|
walletCommon.OptimismSepolia:
|
|
|
|
ticker = time.NewTicker(newBlockCheckIntervalOptimism)
|
|
|
|
case walletCommon.ArbitrumMainnet,
|
|
|
|
walletCommon.ArbitrumSepolia:
|
|
|
|
ticker = time.NewTicker(newBlockCheckIntervalArbitrum)
|
2024-09-25 12:27:04 +00:00
|
|
|
case walletCommon.AnvilMainnet:
|
|
|
|
ticker = time.NewTicker(newBlockCheckIntervalAnvilMainnet)
|
2024-08-29 13:40:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ctx, cancelCtx := context.WithCancel(context.Background())
|
|
|
|
|
|
|
|
go func() {
|
2024-09-26 22:37:32 +00:00
|
|
|
defer gocommon.LogOnPanic()
|
2024-08-29 13:40:30 +00:00
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-ticker.C:
|
|
|
|
var blockNumber uint64
|
|
|
|
blockNumber, err := ethClient.BlockNumber(ctx)
|
|
|
|
if err != nil {
|
|
|
|
log.Error("Failed to get block number", "error", err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
val, ok := r.clientsForUpdatesPerChains.Load(chainID)
|
|
|
|
if !ok {
|
|
|
|
log.Error("Failed to get fetchingLastBlock", "chain", chainID)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
flbLoaded, ok := val.(fetchingLastBlock)
|
|
|
|
if !ok {
|
|
|
|
log.Error("Failed to get fetchingLastBlock", "chain", chainID)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if blockNumber > flbLoaded.lastBlock {
|
|
|
|
flbLoaded.lastBlock = blockNumber
|
|
|
|
r.clientsForUpdatesPerChains.Store(chainID, flbLoaded)
|
|
|
|
|
|
|
|
fees, err := r.feesManager.SuggestedFees(ctx, chainID)
|
|
|
|
if err != nil {
|
|
|
|
log.Error("Failed to get suggested fees", "error", err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
r.lastInputParamsMutex.Lock()
|
|
|
|
uuid := r.lastInputParams.Uuid
|
|
|
|
r.lastInputParamsMutex.Unlock()
|
|
|
|
|
|
|
|
r.activeRoutesMutex.Lock()
|
|
|
|
if r.activeRoutes != nil && r.activeRoutes.Best != nil && len(r.activeRoutes.Best) > 0 {
|
|
|
|
for _, path := range r.activeRoutes.Best {
|
|
|
|
err = r.cacluateFees(ctx, path, fees, false, 0)
|
|
|
|
if err != nil {
|
|
|
|
log.Error("Failed to calculate fees", "error", err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-30 09:03:52 +00:00
|
|
|
_, err = r.checkBalancesForTheBestRoute(ctx, r.activeRoutes.Best)
|
|
|
|
|
|
|
|
sendRouterResult(uuid, r.activeRoutes, err)
|
2024-08-29 13:40:30 +00:00
|
|
|
}
|
|
|
|
r.activeRoutesMutex.Unlock()
|
|
|
|
}
|
|
|
|
case <-flb.closeCh:
|
|
|
|
ticker.Stop()
|
|
|
|
cancelCtx()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-09-25 12:27:04 +00:00
|
|
|
func (r *Router) startTimeoutForUpdates(closeCh chan struct{}, timeout time.Duration) {
|
|
|
|
dedlineTicker := time.NewTicker(timeout)
|
2024-08-29 13:40:30 +00:00
|
|
|
go func() {
|
2024-09-26 22:37:32 +00:00
|
|
|
defer gocommon.LogOnPanic()
|
2024-08-29 13:40:30 +00:00
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-dedlineTicker.C:
|
|
|
|
r.unsubscribeFeesUpdateAccrossAllChains()
|
|
|
|
return
|
|
|
|
case <-closeCh:
|
|
|
|
dedlineTicker.Stop()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Router) unsubscribeFeesUpdateAccrossAllChains() {
|
|
|
|
r.clientsForUpdatesPerChains.Range(func(key, value interface{}) bool {
|
|
|
|
flb, ok := value.(fetchingLastBlock)
|
|
|
|
if !ok {
|
|
|
|
log.Error("Failed to get fetchingLastBlock", "chain", key)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
close(flb.closeCh)
|
|
|
|
r.clientsForUpdatesPerChains.Delete(key)
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
}
|