feat(wallet)_: refresh paraswap proposal on intervals defined per chains

This commit is contained in:
Sale Djenic 2024-10-28 18:30:17 +01:00
parent b329b158c8
commit ad56b696ed
2 changed files with 170 additions and 120 deletions

View File

@ -582,6 +582,33 @@ func (r *Router) getSelectedChains(input *requests.RouteInputParams) (selectedFr
return selectedFromChains, selectedToChains, nil 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, func (r *Router) resolveCandidates(ctx context.Context, input *requests.RouteInputParams, selectedFromChains []*params.Network,
selectedToChains []*params.Network) (candidates routes.Route, processorErrors []*ProcessorError, err error) { selectedToChains []*params.Network) (candidates routes.Route, processorErrors []*ProcessorError, err error) {
var ( var (
@ -694,114 +721,13 @@ func (r *Router) resolveCandidates(ctx context.Context, input *requests.RouteInp
continue continue
} }
processorInputParams := pathprocessor.ProcessorInputParams{ processorInputParams := mapRouteInputParamsToProcessorInputParams(input, amountOption, network, dest, token, toToken)
FromChain: network, path, err := r.resolvePath(ctx, input.SendType, input.GasFeeMode, amountOption.locked, amountOption.subtractFees, processorInputParams, pProcessor, fetchedFees)
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)
if err != nil { if err != nil {
appendProcessorErrorFn(pProcessor.Name(), input.SendType, processorInputParams.FromChain.ChainID, processorInputParams.ToChain.ChainID, processorInputParams.AmountIn, err) appendProcessorErrorFn(pProcessor.Name(), input.SendType, processorInputParams.FromChain.ChainID, processorInputParams.ToChain.ChainID, processorInputParams.AmountIn, err)
continue continue
} }
if !can { if path == nil {
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)
continue continue
} }
@ -823,6 +749,92 @@ func (r *Router) resolveCandidates(ctx context.Context, input *requests.RouteInp
return candidates, processorErrors, nil 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) { func (r *Router) checkBalancesForTheBestRoute(ctx context.Context, bestRoute routes.Route) (hasPositiveBalance bool, err error) {
// make a copy of the active balance map // make a copy of the active balance map
balanceMapCopy := make(map[string]*big.Int) balanceMapCopy := make(map[string]*big.Int)

View File

@ -10,14 +10,21 @@ import (
"github.com/status-im/status-go/logutils" "github.com/status-im/status-go/logutils"
"github.com/status-im/status-go/rpc/chain" "github.com/status-im/status-go/rpc/chain"
walletCommon "github.com/status-im/status-go/services/wallet/common" 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 ( var (
newBlockCheckIntervalMainnet = 3 * time.Second newBlockCheckIntervalMainnet = 3 * time.Second
newBlockCheckIntervalOptimism = 1 * time.Second newBlockCheckIntervalOptimism = 2 * time.Second
newBlockCheckIntervalArbitrum = 200 * time.Millisecond newBlockCheckIntervalArbitrum = 1 * time.Second
newBlockCheckIntervalAnvilMainnet = 2 * 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 feeRecalculationTimeout = 5 * time.Minute
feeRecalculationAnvilTimeout = 5 * time.Second feeRecalculationAnvilTimeout = 5 * time.Second
) )
@ -52,21 +59,31 @@ func (r *Router) subscribeForUdates(chainID uint64) error {
} }
r.startTimeoutForUpdates(flb.closeCh, timeout) r.startTimeoutForUpdates(flb.closeCh, timeout)
var ticker *time.Ticker var (
duration time.Duration
step time.Duration
limit time.Duration
)
switch chainID { switch chainID {
case walletCommon.EthereumMainnet, case walletCommon.EthereumMainnet,
walletCommon.EthereumSepolia: walletCommon.EthereumSepolia:
ticker = time.NewTicker(newBlockCheckIntervalMainnet) step = newBlockCheckIntervalMainnet
limit = paraswapProposalCheckIntervalMainnet
case walletCommon.OptimismMainnet, case walletCommon.OptimismMainnet,
walletCommon.OptimismSepolia: walletCommon.OptimismSepolia:
ticker = time.NewTicker(newBlockCheckIntervalOptimism) step = newBlockCheckIntervalOptimism
limit = paraswapProposalCheckIntervalOptimism
case walletCommon.ArbitrumMainnet, case walletCommon.ArbitrumMainnet,
walletCommon.ArbitrumSepolia: walletCommon.ArbitrumSepolia:
ticker = time.NewTicker(newBlockCheckIntervalArbitrum) step = newBlockCheckIntervalArbitrum
limit = paraswapProposalCheckIntervalArbitrum
case walletCommon.AnvilMainnet: case walletCommon.AnvilMainnet:
ticker = time.NewTicker(newBlockCheckIntervalAnvilMainnet) step = newBlockCheckIntervalAnvilMainnet
limit = paraswapProposalCheckIntervalAnvilMainnet
} }
ticker := time.NewTicker(step)
ctx, cancelCtx := context.WithCancel(context.Background()) ctx, cancelCtx := context.WithCancel(context.Background())
go func() { go func() {
@ -74,6 +91,13 @@ func (r *Router) subscribeForUdates(chainID uint64) error {
for { for {
select { select {
case <-ticker.C: case <-ticker.C:
refreshParaswapProposal := false
duration += step
if duration >= limit {
refreshParaswapProposal = true
duration = 0
}
var blockNumber uint64 var blockNumber uint64
blockNumber, err := ethClient.BlockNumber(ctx) blockNumber, err := ethClient.BlockNumber(ctx)
if err != nil { if err != nil {
@ -103,23 +127,37 @@ func (r *Router) subscribeForUdates(chainID uint64) error {
continue continue
} }
r.lastInputParamsMutex.Lock() _, inputParams := r.GetBestRouteAndAssociatedInputParams()
uuid := r.lastInputParams.Uuid
r.lastInputParamsMutex.Unlock()
r.activeRoutesMutex.Lock() r.activeRoutesMutex.Lock()
if r.activeRoutes != nil && r.activeRoutes.Best != nil && len(r.activeRoutes.Best) > 0 { if r.activeRoutes != nil && r.activeRoutes.Best != nil && len(r.activeRoutes.Best) > 0 {
for _, path := range r.activeRoutes.Best { for i, path := range r.activeRoutes.Best {
err = r.cacluateFees(ctx, path, fees, false, 0) if path.ProcessorName == pathprocessor.ProcessorSwapParaswapName && refreshParaswapProposal {
if err != nil { amountOption := amountOption{
logutils.ZapLogger().Error("Failed to calculate fees", zap.Error(err)) amount: path.AmountIn.ToInt(),
continue 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) _, err = r.checkBalancesForTheBestRoute(ctx, r.activeRoutes.Best)
sendRouterResult(uuid, r.activeRoutes, err) sendRouterResult(inputParams.Uuid, r.activeRoutes, err)
} }
r.activeRoutesMutex.Unlock() r.activeRoutesMutex.Unlock()
} }