feat(wallet)_: refresh paraswap proposal on intervals defined per chains
This commit is contained in:
parent
b329b158c8
commit
ad56b696ed
|
@ -582,6 +582,33 @@ func (r *Router) getSelectedChains(input *requests.RouteInputParams) (selectedFr
|
|||
return selectedFromChains, selectedToChains, nil
|
||||
}
|
||||
|
||||
func mapRouteInputParamsToProcessorInputParams(input *requests.RouteInputParams, amountOption amountOption, fromChain *params.Network,
|
||||
toChain *params.Network, token *walletToken.Token, toToken *walletToken.Token) pathprocessor.ProcessorInputParams {
|
||||
processorInputParams := pathprocessor.ProcessorInputParams{
|
||||
FromChain: fromChain,
|
||||
ToChain: toChain,
|
||||
FromToken: token,
|
||||
ToToken: toToken,
|
||||
ToAddr: input.AddrTo,
|
||||
FromAddr: input.AddrFrom,
|
||||
AmountIn: amountOption.amount,
|
||||
AmountOut: input.AmountOut.ToInt(),
|
||||
|
||||
Username: input.Username,
|
||||
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
|
||||
}
|
||||
|
||||
return processorInputParams
|
||||
}
|
||||
|
||||
func (r *Router) resolveCandidates(ctx context.Context, input *requests.RouteInputParams, selectedFromChains []*params.Network,
|
||||
selectedToChains []*params.Network) (candidates routes.Route, processorErrors []*ProcessorError, err error) {
|
||||
var (
|
||||
|
@ -694,114 +721,13 @@ func (r *Router) resolveCandidates(ctx context.Context, input *requests.RouteInp
|
|||
continue
|
||||
}
|
||||
|
||||
processorInputParams := pathprocessor.ProcessorInputParams{
|
||||
FromChain: network,
|
||||
ToChain: dest,
|
||||
FromToken: token,
|
||||
ToToken: toToken,
|
||||
ToAddr: input.AddrTo,
|
||||
FromAddr: input.AddrFrom,
|
||||
AmountIn: amountOption.amount,
|
||||
AmountOut: input.AmountOut.ToInt(),
|
||||
|
||||
Username: input.Username,
|
||||
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
|
||||
}
|
||||
|
||||
can, err := pProcessor.AvailableFor(processorInputParams)
|
||||
processorInputParams := mapRouteInputParamsToProcessorInputParams(input, amountOption, network, dest, token, toToken)
|
||||
path, err := r.resolvePath(ctx, input.SendType, input.GasFeeMode, amountOption.locked, amountOption.subtractFees, processorInputParams, pProcessor, fetchedFees)
|
||||
if err != nil {
|
||||
appendProcessorErrorFn(pProcessor.Name(), input.SendType, processorInputParams.FromChain.ChainID, processorInputParams.ToChain.ChainID, processorInputParams.AmountIn, err)
|
||||
continue
|
||||
}
|
||||
if !can {
|
||||
continue
|
||||
}
|
||||
|
||||
bonderFees, tokenFees, err := pProcessor.CalculateFees(processorInputParams)
|
||||
if err != nil {
|
||||
appendProcessorErrorFn(pProcessor.Name(), input.SendType, processorInputParams.FromChain.ChainID, processorInputParams.ToChain.ChainID, processorInputParams.AmountIn, err)
|
||||
continue
|
||||
}
|
||||
|
||||
gasLimit, err := pProcessor.EstimateGas(processorInputParams)
|
||||
if err != nil {
|
||||
appendProcessorErrorFn(pProcessor.Name(), input.SendType, processorInputParams.FromChain.ChainID, processorInputParams.ToChain.ChainID, processorInputParams.AmountIn, err)
|
||||
continue
|
||||
}
|
||||
|
||||
approvalContractAddress, err := pProcessor.GetContractAddress(processorInputParams)
|
||||
if err != nil {
|
||||
appendProcessorErrorFn(pProcessor.Name(), input.SendType, processorInputParams.FromChain.ChainID, processorInputParams.ToChain.ChainID, processorInputParams.AmountIn, err)
|
||||
continue
|
||||
}
|
||||
approvalRequired, approvalAmountRequired, err := r.requireApproval(ctx, input.SendType, &approvalContractAddress, processorInputParams)
|
||||
if err != nil {
|
||||
appendProcessorErrorFn(pProcessor.Name(), input.SendType, processorInputParams.FromChain.ChainID, processorInputParams.ToChain.ChainID, processorInputParams.AmountIn, err)
|
||||
continue
|
||||
}
|
||||
|
||||
var approvalGasLimit uint64
|
||||
if approvalRequired {
|
||||
if processorInputParams.TestsMode {
|
||||
approvalGasLimit = processorInputParams.TestApprovalGasEstimation
|
||||
} else {
|
||||
approvalGasLimit, err = r.estimateGasForApproval(processorInputParams, &approvalContractAddress)
|
||||
if err != nil {
|
||||
appendProcessorErrorFn(pProcessor.Name(), input.SendType, processorInputParams.FromChain.ChainID, processorInputParams.ToChain.ChainID, processorInputParams.AmountIn, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
amountOut, err := pProcessor.CalculateAmountOut(processorInputParams)
|
||||
if err != nil {
|
||||
appendProcessorErrorFn(pProcessor.Name(), input.SendType, processorInputParams.FromChain.ChainID, processorInputParams.ToChain.ChainID, processorInputParams.AmountIn, err)
|
||||
continue
|
||||
}
|
||||
|
||||
maxFeesPerGas := fetchedFees.FeeFor(input.GasFeeMode)
|
||||
|
||||
estimatedTime := r.feesManager.TransactionEstimatedTime(ctx, network.ChainID, maxFeesPerGas)
|
||||
if approvalRequired && estimatedTime < fees.MoreThanFiveMinutes {
|
||||
estimatedTime += 1
|
||||
}
|
||||
|
||||
path := &routes.Path{
|
||||
ProcessorName: pProcessor.Name(),
|
||||
FromChain: network,
|
||||
ToChain: dest,
|
||||
FromToken: token,
|
||||
ToToken: toToken,
|
||||
AmountIn: (*hexutil.Big)(amountOption.amount),
|
||||
AmountInLocked: amountOption.locked,
|
||||
AmountOut: (*hexutil.Big)(amountOut),
|
||||
|
||||
// set params that we don't want to be recalculated with every new block creation
|
||||
TxGasAmount: gasLimit,
|
||||
TxBonderFees: (*hexutil.Big)(bonderFees),
|
||||
TxTokenFees: (*hexutil.Big)(tokenFees),
|
||||
|
||||
ApprovalRequired: approvalRequired,
|
||||
ApprovalAmountRequired: (*hexutil.Big)(approvalAmountRequired),
|
||||
ApprovalContractAddress: &approvalContractAddress,
|
||||
ApprovalGasAmount: approvalGasLimit,
|
||||
|
||||
EstimatedTime: estimatedTime,
|
||||
|
||||
SubtractFees: amountOption.subtractFees,
|
||||
}
|
||||
|
||||
err = r.cacluateFees(ctx, path, fetchedFees, processorInputParams.TestsMode, processorInputParams.TestApprovalL1Fee)
|
||||
if err != nil {
|
||||
appendProcessorErrorFn(pProcessor.Name(), input.SendType, processorInputParams.FromChain.ChainID, processorInputParams.ToChain.ChainID, processorInputParams.AmountIn, err)
|
||||
if path == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -823,6 +749,92 @@ func (r *Router) resolveCandidates(ctx context.Context, input *requests.RouteInp
|
|||
return candidates, processorErrors, nil
|
||||
}
|
||||
|
||||
func (r *Router) resolvePath(ctx context.Context, sendType sendtype.SendType, gasFeeMode fees.GasFeeMode, amountInLocked bool, subtractFees bool,
|
||||
processorInputParams pathprocessor.ProcessorInputParams, pProcessor pathprocessor.PathProcessor, fetchedFees *fees.SuggestedFees) (*routes.Path, error) {
|
||||
can, err := pProcessor.AvailableFor(processorInputParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !can {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
bonderFees, tokenFees, err := pProcessor.CalculateFees(processorInputParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gasLimit, err := pProcessor.EstimateGas(processorInputParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
approvalContractAddress, err := pProcessor.GetContractAddress(processorInputParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
approvalRequired, approvalAmountRequired, err := r.requireApproval(ctx, sendType, &approvalContractAddress, processorInputParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var approvalGasLimit uint64
|
||||
if approvalRequired {
|
||||
if processorInputParams.TestsMode {
|
||||
approvalGasLimit = processorInputParams.TestApprovalGasEstimation
|
||||
} else {
|
||||
approvalGasLimit, err = r.estimateGasForApproval(processorInputParams, &approvalContractAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
amountOut, err := pProcessor.CalculateAmountOut(processorInputParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
maxFeesPerGas := fetchedFees.FeeFor(gasFeeMode)
|
||||
|
||||
estimatedTime := r.feesManager.TransactionEstimatedTime(ctx, processorInputParams.FromChain.ChainID, maxFeesPerGas)
|
||||
if approvalRequired && estimatedTime < fees.MoreThanFiveMinutes {
|
||||
estimatedTime += 1
|
||||
}
|
||||
|
||||
path := &routes.Path{
|
||||
ProcessorName: pProcessor.Name(),
|
||||
FromChain: processorInputParams.FromChain,
|
||||
ToChain: processorInputParams.ToChain,
|
||||
FromToken: processorInputParams.FromToken,
|
||||
ToToken: processorInputParams.ToToken,
|
||||
AmountIn: (*hexutil.Big)(processorInputParams.AmountIn),
|
||||
AmountInLocked: amountInLocked,
|
||||
AmountOut: (*hexutil.Big)(amountOut),
|
||||
|
||||
// set params that we don't want to be recalculated with every new block creation
|
||||
TxGasAmount: gasLimit,
|
||||
TxBonderFees: (*hexutil.Big)(bonderFees),
|
||||
TxTokenFees: (*hexutil.Big)(tokenFees),
|
||||
|
||||
ApprovalRequired: approvalRequired,
|
||||
ApprovalAmountRequired: (*hexutil.Big)(approvalAmountRequired),
|
||||
ApprovalContractAddress: &approvalContractAddress,
|
||||
ApprovalGasAmount: approvalGasLimit,
|
||||
|
||||
EstimatedTime: estimatedTime,
|
||||
|
||||
SubtractFees: subtractFees,
|
||||
}
|
||||
|
||||
err = r.cacluateFees(ctx, path, fetchedFees, processorInputParams.TestsMode, processorInputParams.TestApprovalL1Fee)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func (r *Router) checkBalancesForTheBestRoute(ctx context.Context, bestRoute routes.Route) (hasPositiveBalance bool, err error) {
|
||||
// make a copy of the active balance map
|
||||
balanceMapCopy := make(map[string]*big.Int)
|
||||
|
|
|
@ -10,14 +10,21 @@ import (
|
|||
"github.com/status-im/status-go/logutils"
|
||||
"github.com/status-im/status-go/rpc/chain"
|
||||
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"
|
||||
)
|
||||
|
||||
var (
|
||||
newBlockCheckIntervalMainnet = 3 * time.Second
|
||||
newBlockCheckIntervalOptimism = 1 * time.Second
|
||||
newBlockCheckIntervalArbitrum = 200 * time.Millisecond
|
||||
newBlockCheckIntervalOptimism = 2 * time.Second
|
||||
newBlockCheckIntervalArbitrum = 1 * time.Second
|
||||
newBlockCheckIntervalAnvilMainnet = 2 * time.Second
|
||||
|
||||
paraswapProposalCheckIntervalMainnet = 12 * time.Second
|
||||
paraswapProposalCheckIntervalOptimism = 3 * time.Second
|
||||
paraswapProposalCheckIntervalArbitrum = 3 * time.Second
|
||||
paraswapProposalCheckIntervalAnvilMainnet = 2 * time.Second
|
||||
|
||||
feeRecalculationTimeout = 5 * time.Minute
|
||||
feeRecalculationAnvilTimeout = 5 * time.Second
|
||||
)
|
||||
|
@ -52,21 +59,31 @@ func (r *Router) subscribeForUdates(chainID uint64) error {
|
|||
}
|
||||
r.startTimeoutForUpdates(flb.closeCh, timeout)
|
||||
|
||||
var ticker *time.Ticker
|
||||
var (
|
||||
duration time.Duration
|
||||
step time.Duration
|
||||
limit time.Duration
|
||||
)
|
||||
switch chainID {
|
||||
case walletCommon.EthereumMainnet,
|
||||
walletCommon.EthereumSepolia:
|
||||
ticker = time.NewTicker(newBlockCheckIntervalMainnet)
|
||||
step = newBlockCheckIntervalMainnet
|
||||
limit = paraswapProposalCheckIntervalMainnet
|
||||
case walletCommon.OptimismMainnet,
|
||||
walletCommon.OptimismSepolia:
|
||||
ticker = time.NewTicker(newBlockCheckIntervalOptimism)
|
||||
step = newBlockCheckIntervalOptimism
|
||||
limit = paraswapProposalCheckIntervalOptimism
|
||||
case walletCommon.ArbitrumMainnet,
|
||||
walletCommon.ArbitrumSepolia:
|
||||
ticker = time.NewTicker(newBlockCheckIntervalArbitrum)
|
||||
step = newBlockCheckIntervalArbitrum
|
||||
limit = paraswapProposalCheckIntervalArbitrum
|
||||
case walletCommon.AnvilMainnet:
|
||||
ticker = time.NewTicker(newBlockCheckIntervalAnvilMainnet)
|
||||
step = newBlockCheckIntervalAnvilMainnet
|
||||
limit = paraswapProposalCheckIntervalAnvilMainnet
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(step)
|
||||
|
||||
ctx, cancelCtx := context.WithCancel(context.Background())
|
||||
|
||||
go func() {
|
||||
|
@ -74,6 +91,13 @@ func (r *Router) subscribeForUdates(chainID uint64) error {
|
|||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
refreshParaswapProposal := false
|
||||
duration += step
|
||||
if duration >= limit {
|
||||
refreshParaswapProposal = true
|
||||
duration = 0
|
||||
}
|
||||
|
||||
var blockNumber uint64
|
||||
blockNumber, err := ethClient.BlockNumber(ctx)
|
||||
if err != nil {
|
||||
|
@ -103,23 +127,37 @@ func (r *Router) subscribeForUdates(chainID uint64) error {
|
|||
continue
|
||||
}
|
||||
|
||||
r.lastInputParamsMutex.Lock()
|
||||
uuid := r.lastInputParams.Uuid
|
||||
r.lastInputParamsMutex.Unlock()
|
||||
_, inputParams := r.GetBestRouteAndAssociatedInputParams()
|
||||
|
||||
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 {
|
||||
logutils.ZapLogger().Error("Failed to calculate fees", zap.Error(err))
|
||||
continue
|
||||
for i, path := range r.activeRoutes.Best {
|
||||
if path.ProcessorName == pathprocessor.ProcessorSwapParaswapName && refreshParaswapProposal {
|
||||
amountOption := amountOption{
|
||||
amount: path.AmountIn.ToInt(),
|
||||
locked: path.AmountInLocked,
|
||||
subtractFees: path.SubtractFees,
|
||||
}
|
||||
processorInputParams := mapRouteInputParamsToProcessorInputParams(&inputParams, amountOption, path.FromChain, path.ToChain, path.FromToken, path.ToToken)
|
||||
swapProcessor := r.pathProcessors[path.ProcessorName]
|
||||
newPath, err := r.resolvePath(ctx, sendtype.Swap, inputParams.GasFeeMode, amountOption.locked, amountOption.subtractFees, processorInputParams, swapProcessor, fees)
|
||||
if err != nil {
|
||||
logutils.ZapLogger().Error("Failed to calculate fees", zap.Error(err))
|
||||
continue
|
||||
}
|
||||
r.activeRoutes.Best[i] = newPath
|
||||
} else {
|
||||
err = r.cacluateFees(ctx, path, fees, false, 0)
|
||||
if err != nil {
|
||||
logutils.ZapLogger().Error("Failed to calculate fees", zap.Error(err))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_, err = r.checkBalancesForTheBestRoute(ctx, r.activeRoutes.Best)
|
||||
|
||||
sendRouterResult(uuid, r.activeRoutes, err)
|
||||
sendRouterResult(inputParams.Uuid, r.activeRoutes, err)
|
||||
}
|
||||
r.activeRoutesMutex.Unlock()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue