Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
220 changes: 116 additions & 104 deletions services/wallet/router/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,33 @@
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 (
Expand Down Expand Up @@ -694,114 +721,13 @@
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)
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)
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
}

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
}

Expand All @@ -823,6 +749,92 @@
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

Check warning on line 764 in services/wallet/router/router.go

View check run for this annotation

Codecov / codecov/patch

services/wallet/router/router.go#L764

Added line #L764 was not covered by tests
}

gasLimit, err := pProcessor.EstimateGas(processorInputParams)
if err != nil {
return nil, err
}

approvalContractAddress, err := pProcessor.GetContractAddress(processorInputParams)
if err != nil {
return nil, err

Check warning on line 774 in services/wallet/router/router.go

View check run for this annotation

Codecov / codecov/patch

services/wallet/router/router.go#L774

Added line #L774 was not covered by tests
}
approvalRequired, approvalAmountRequired, err := r.requireApproval(ctx, sendType, &approvalContractAddress, processorInputParams)
if err != nil {
return nil, err

Check warning on line 778 in services/wallet/router/router.go

View check run for this annotation

Codecov / codecov/patch

services/wallet/router/router.go#L778

Added line #L778 was not covered by tests
}

var approvalGasLimit uint64
if approvalRequired {
if processorInputParams.TestsMode {
approvalGasLimit = processorInputParams.TestApprovalGasEstimation
} else {
approvalGasLimit, err = r.estimateGasForApproval(processorInputParams, &approvalContractAddress)
if err != nil {
return nil, err

Check warning on line 788 in services/wallet/router/router.go

View check run for this annotation

Codecov / codecov/patch

services/wallet/router/router.go#L786-L788

Added lines #L786 - L788 were not covered by tests
}
}
}

amountOut, err := pProcessor.CalculateAmountOut(processorInputParams)
if err != nil {
return nil, err

Check warning on line 795 in services/wallet/router/router.go

View check run for this annotation

Codecov / codecov/patch

services/wallet/router/router.go#L795

Added line #L795 was not covered by tests
}

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

Check warning on line 832 in services/wallet/router/router.go

View check run for this annotation

Codecov / codecov/patch

services/wallet/router/router.go#L832

Added line #L832 was not covered by tests
}

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)
Expand Down
70 changes: 54 additions & 16 deletions services/wallet/router/router_updates.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,21 @@
"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
)
Expand Down Expand Up @@ -52,28 +59,45 @@
}
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() {
defer gocommon.LogOnPanic()
for {
select {
case <-ticker.C:
refreshParaswapProposal := false
duration += step
if duration >= limit {
refreshParaswapProposal = true
duration = 0

Check warning on line 98 in services/wallet/router/router_updates.go

View check run for this annotation

Codecov / codecov/patch

services/wallet/router/router_updates.go#L94-L98

Added lines #L94 - L98 were not covered by tests
}

var blockNumber uint64
blockNumber, err := ethClient.BlockNumber(ctx)
if err != nil {
Expand Down Expand Up @@ -103,23 +127,37 @@
continue
}

r.lastInputParamsMutex.Lock()
uuid := r.lastInputParams.Uuid
r.lastInputParamsMutex.Unlock()
_, inputParams := r.GetBestRouteAndAssociatedInputParams()

Check warning on line 130 in services/wallet/router/router_updates.go

View check run for this annotation

Codecov / codecov/patch

services/wallet/router/router_updates.go#L130

Added line #L130 was not covered by tests

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,

Check warning on line 139 in services/wallet/router/router_updates.go

View check run for this annotation

Codecov / codecov/patch

services/wallet/router/router_updates.go#L134-L139

Added lines #L134 - L139 were not covered by tests
}
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

Check warning on line 146 in services/wallet/router/router_updates.go

View check run for this annotation

Codecov / codecov/patch

services/wallet/router/router_updates.go#L141-L146

Added lines #L141 - L146 were not covered by tests
}
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

Check warning on line 153 in services/wallet/router/router_updates.go

View check run for this annotation

Codecov / codecov/patch

services/wallet/router/router_updates.go#L148-L153

Added lines #L148 - L153 were not covered by tests
}
}
}

_, err = r.checkBalancesForTheBestRoute(ctx, r.activeRoutes.Best)

sendRouterResult(uuid, r.activeRoutes, err)
sendRouterResult(inputParams.Uuid, r.activeRoutes, err)

Check warning on line 160 in services/wallet/router/router_updates.go

View check run for this annotation

Codecov / codecov/patch

services/wallet/router/router_updates.go#L160

Added line #L160 was not covered by tests
}
r.activeRoutesMutex.Unlock()
}
Expand Down