From f9b00adf4ddd7c086c87e67e8816246ada6d59af Mon Sep 17 00:00:00 2001 From: Sale Djenic Date: Mon, 17 Feb 2025 11:37:12 +0100 Subject: [PATCH] feat(wallet)_: calculating tx estimated time --- .../connector/commands/send_transaction.go | 2 +- services/wallet/api.go | 5 + services/wallet/common/utils.go | 9 + services/wallet/router/fees/estimated_time.go | 180 ++++++++++++++++-- services/wallet/router/fees/fees.go | 51 ++--- services/wallet/router/router.go | 41 ++-- services/wallet/router/router_helper.go | 21 +- services/wallet/router/routes/router_path.go | 4 +- .../wallet/router/routes/router_path_test.go | 4 +- 9 files changed, 244 insertions(+), 73 deletions(-) diff --git a/services/connector/commands/send_transaction.go b/services/connector/commands/send_transaction.go index fe5be2daa87..fe28ef65609 100644 --- a/services/connector/commands/send_transaction.go +++ b/services/connector/commands/send_transaction.go @@ -97,7 +97,7 @@ func (c *SendTransactionCommand) Execute(ctx context.Context, request RPCRequest if !fetchedFees.EIP1559Enabled { params.GasPrice = (*hexutil.Big)(fetchedFees.GasPrice) } else { - maxFees, priorityFee, err := fetchedFees.FeeFor(fees.GasFeeMedium) + maxFees, priorityFee, _, err := fetchedFees.FeeFor(fees.GasFeeMedium) if err != nil { return "", err } diff --git a/services/wallet/api.go b/services/wallet/api.go index da603e5bf7b..1fa7f367d2f 100644 --- a/services/wallet/api.go +++ b/services/wallet/api.go @@ -465,6 +465,11 @@ func (api *API) GetTransactionEstimatedTime(ctx context.Context, chainID uint64, return api.s.router.GetFeesManager().TransactionEstimatedTime(ctx, chainID, gweiToWei(maxFeePerGas)), nil } +func (api *API) GetTransactionEstimatedTimeV2(ctx context.Context, chainID uint64, maxFeePerGas *hexutil.Big, maxPriorityFeePerGas *hexutil.Big) (uint, error) { + logutils.ZapLogger().Debug("call to getTransactionEstimatedTimeV2") + return api.s.router.GetFeesManager().TransactionEstimatedTimeV2(ctx, chainID, maxFeePerGas.ToInt(), maxPriorityFeePerGas.ToInt()), nil +} + func gweiToWei(val *big.Float) *big.Int { res, _ := new(big.Float).Mul(val, big.NewFloat(1000000000)).Int(nil) return res diff --git a/services/wallet/common/utils.go b/services/wallet/common/utils.go index 793fb0a8661..ae6fa2415f5 100644 --- a/services/wallet/common/utils.go +++ b/services/wallet/common/utils.go @@ -4,6 +4,7 @@ import ( "context" "math/big" "reflect" + "time" gethParams "github.com/ethereum/go-ethereum/params" "github.com/status-im/status-go/params" @@ -75,3 +76,11 @@ func WeiToGwei(val *big.Int) *big.Float { return result.Quo(result, new(big.Float).SetInt(unit)) } + +func GetBlockCreationTimeForChain(chainID uint64) time.Duration { + blockDuration, found := AverageBlockDurationForChain[ChainID(chainID)] + if !found { + blockDuration = AverageBlockDurationForChain[ChainID(UnknownChainID)] + } + return blockDuration +} diff --git a/services/wallet/router/fees/estimated_time.go b/services/wallet/router/fees/estimated_time.go index 9498ae2fba0..5da1ea90108 100644 --- a/services/wallet/router/fees/estimated_time.go +++ b/services/wallet/router/fees/estimated_time.go @@ -5,7 +5,8 @@ import ( "math" "math/big" "sort" - "strings" + + "github.com/status-im/status-go/services/wallet/common" ) const inclusionThreshold = 0.95 @@ -20,6 +21,18 @@ const ( MoreThanFiveMinutes ) +const ( + EstimatedTimeUnknown = 0 + EstimatedTime5Sec = 5 + EstimatedTime10Sec = 10 + EstimatedTime15Sec = 15 + EstimatedTime20Sec = 20 + EstimatedTime30Sec = 30 + EstimatedTime45Sec = 45 + EstimatedTime1Min = 60 + EstimatedTimeMoreThan1Min = 61 // the client should display "more than 1 minute" if the expected inclusion time is more than 61 seconds +) + func (f *FeeManager) TransactionEstimatedTime(ctx context.Context, chainID uint64, maxFeePerGas *big.Int) TransactionEstimation { feeHistory, err := f.getFeeHistory(ctx, chainID, 100, "latest", nil) if err != nil { @@ -30,8 +43,8 @@ func (f *FeeManager) TransactionEstimatedTime(ctx context.Context, chainID uint6 } func (f *FeeManager) estimatedTime(feeHistory *FeeHistory, maxFeePerGas *big.Int) TransactionEstimation { - fees, err := f.getFeeHistorySorted(feeHistory) - if err != nil || len(fees) == 0 { + fees := f.convertToBigIntAndSort(feeHistory.BaseFeePerGas) + if len(fees) == 0 { return Unknown } @@ -102,14 +115,159 @@ func (f *FeeManager) estimatedTime(feeHistory *FeeHistory, maxFeePerGas *big.Int return MoreThanFiveMinutes } -func (f *FeeManager) getFeeHistorySorted(feeHistory *FeeHistory) ([]*big.Int, error) { - fees := []*big.Int{} - for _, fee := range feeHistory.BaseFeePerGas { - i := new(big.Int) - i.SetString(strings.Replace(fee, "0x", "", 1), 16) - fees = append(fees, i) +func (f *FeeManager) convertToBigIntAndSort(hexArray []string) []*big.Int { + values := []*big.Int{} + for _, sValue := range hexArray { + iValue := new(big.Int) + _, ok := iValue.SetString(sValue, 0) + if !ok { + continue + } + values = append(values, iValue) + } + + sort.Slice(values, func(i, j int) bool { return values[i].Cmp(values[j]) < 0 }) + return values +} + +// TransactionEstimatedTimeV2 returns the estimated time in seconds for a transaction to be included in a block +func (f *FeeManager) TransactionEstimatedTimeV2(ctx context.Context, chainID uint64, maxFeePerGas *big.Int, priorityFee *big.Int) uint { + blockCount := uint64(10) // use the last 10 blocks for L1 chains + if chainID != common.EthereumMainnet && chainID != common.EthereumSepolia { + blockCount = 50 // use the last 50 blocks for L2 chains + } + feeHistory, err := f.getFeeHistory(ctx, chainID, blockCount, "latest", []int{RewardPercentiles2}) + if err != nil { + return 0 + } + + return f.estimatedTimeV2(feeHistory, maxFeePerGas, priorityFee, chainID) +} + +func calculateTimeForInclusion(chainID uint64, expectedInclusionInBlock int) uint { + blockCreationTime := common.GetBlockCreationTimeForChain(chainID) + blockCreationTimeInSeconds := uint(blockCreationTime.Seconds()) + + expectedInclusionTime := uint(expectedInclusionInBlock) * blockCreationTimeInSeconds + if expectedInclusionTime < EstimatedTime5Sec { + return EstimatedTime5Sec + } else if expectedInclusionTime < EstimatedTime10Sec { + return EstimatedTime10Sec + } else if expectedInclusionTime < EstimatedTime15Sec { + return EstimatedTime15Sec + } else if expectedInclusionTime < EstimatedTime20Sec { + return EstimatedTime20Sec + } else if expectedInclusionTime < EstimatedTime30Sec { + return EstimatedTime30Sec + } else if expectedInclusionTime < EstimatedTime45Sec { + return EstimatedTime45Sec + } else if expectedInclusionTime < EstimatedTime1Min { + return EstimatedTime1Min + } + + return EstimatedTimeMoreThan1Min +} + +func (f *FeeManager) estimatedTimeV2(feeHistory *FeeHistory, txMaxFeePerGas *big.Int, txPriorityFee *big.Int, chainID uint64) uint { + sortedBaseFees := f.convertToBigIntAndSort(feeHistory.BaseFeePerGas) + if len(sortedBaseFees) == 0 { + return EstimatedTimeUnknown + } + + var mediumPriorityFees []string // based on 50th percentile in the last 100 blocks + for _, fee := range feeHistory.Reward { + mediumPriorityFees = append(mediumPriorityFees, fee[0]) + } + mediumPriorityFeesSorted := f.convertToBigIntAndSort(mediumPriorityFees) + if len(mediumPriorityFeesSorted) == 0 { + return EstimatedTimeUnknown + } + + txBaseFee := new(big.Int).Sub(txMaxFeePerGas, txPriorityFee) + + networkCongestion := calculateNetworkCongestion(feeHistory) + + // Priority fee for the first two blocks has to be higher than 60th percentile of the mediumPriorityFeesSorted + priorityFeePercentileIndex := int(float64(len(mediumPriorityFeesSorted)) * 0.6) + priorityFeeForFirstTwoBlock := mediumPriorityFeesSorted[priorityFeePercentileIndex] + // Priority fee for the second two blocks has to be higher than 50th percentile of the mediumPriorityFeesSorted + priorityFeePercentileIndex = int(float64(len(mediumPriorityFeesSorted)) * 0.5) + priorityFeeForSecondTwoBlocks := mediumPriorityFeesSorted[priorityFeePercentileIndex] + // Priority fee for the third two blocks has to be higher than 40th percentile of the mediumPriorityFeesSorted + priorityFeePercentileIndex = int(float64(len(mediumPriorityFeesSorted)) * 0.4) + priorityFeeForThirdTwoBlocks := mediumPriorityFeesSorted[priorityFeePercentileIndex] + + // To include the transaction in the next block its base fee has to be in a higher than 80 percentile + // of the base fees in the last 100 blocks (corrected by the network congestion) + baseFeePercentileIndex := int(float64(len(sortedBaseFees)) * 0.8 * networkCongestion) + // index correction + if baseFeePercentileIndex >= len(sortedBaseFees) { + baseFeePercentileIndex = len(sortedBaseFees) - 1 + } + // check if the transaction is included in the next block + if txPriorityFee.Cmp(priorityFeeForFirstTwoBlock) >= 0 && txBaseFee.Cmp(sortedBaseFees[baseFeePercentileIndex]) >= 0 { + return calculateTimeForInclusion(chainID, 1) + } + + // To include the transaction in the next 2 blocks its base fee has to be in a higher than 70 percentile + // of the base fees in the last 100 blocks (corrected by the network congestion) + baseFeePercentileIndex = int(float64(len(sortedBaseFees)) * 0.7 * networkCongestion) + // index correction + if baseFeePercentileIndex >= len(sortedBaseFees) { + baseFeePercentileIndex = len(sortedBaseFees) - 1 + } + // check if the transaction is included in the next 2 blocks + if txPriorityFee.Cmp(priorityFeeForFirstTwoBlock) >= 0 && txBaseFee.Cmp(sortedBaseFees[baseFeePercentileIndex]) >= 0 { + return calculateTimeForInclusion(chainID, 2) + } + + // To include the transaction in the next 3 blocks its base fee has to be in a higher than 60 percentile + // of the base fees in the last 100 blocks (corrected by the network congestion) + baseFeePercentileIndex = int(float64(len(sortedBaseFees)) * 0.6 * networkCongestion) + // index correction + if baseFeePercentileIndex >= len(sortedBaseFees) { + baseFeePercentileIndex = len(sortedBaseFees) - 1 + } + // check if the transaction is included in the next 3 blocks + if txPriorityFee.Cmp(priorityFeeForSecondTwoBlocks) >= 0 && txBaseFee.Cmp(sortedBaseFees[baseFeePercentileIndex]) >= 0 { + return calculateTimeForInclusion(chainID, 3) + } + + // To include the transaction in the next 4 blocks its base fee has to be in a higher than 50 percentile + // of the base fees in the last 100 blocks (corrected by the network congestion) + baseFeePercentileIndex = int(float64(len(sortedBaseFees)) * 0.5 * networkCongestion) + // index correction + if baseFeePercentileIndex >= len(sortedBaseFees) { + baseFeePercentileIndex = len(sortedBaseFees) - 1 + } + // check if the transaction is included in the next 4 blocks + if txPriorityFee.Cmp(priorityFeeForSecondTwoBlocks) >= 0 && txBaseFee.Cmp(sortedBaseFees[baseFeePercentileIndex]) >= 0 { + return calculateTimeForInclusion(chainID, 4) + } + + // To include the transaction in the next 5 blocks its base fee has to be in a higher than 40 percentile + // of the base fees in the last 100 blocks (corrected by the network congestion) + baseFeePercentileIndex = int(float64(len(sortedBaseFees)) * 0.4 * networkCongestion) + // index correction + if baseFeePercentileIndex >= len(sortedBaseFees) { + baseFeePercentileIndex = len(sortedBaseFees) - 1 + } + // check if the transaction is included in the next 5 blocks + if txPriorityFee.Cmp(priorityFeeForThirdTwoBlocks) >= 0 && txBaseFee.Cmp(sortedBaseFees[baseFeePercentileIndex]) >= 0 { + return calculateTimeForInclusion(chainID, 5) + } + + // To include the transaction in the next 6 blocks its base fee has to be in a higher than 30 percentile + // of the base fees in the last 100 blocks (corrected by the network congestion) + baseFeePercentileIndex = int(float64(len(sortedBaseFees)) * 0.3 * networkCongestion) + // index correction + if baseFeePercentileIndex >= len(sortedBaseFees) { + baseFeePercentileIndex = len(sortedBaseFees) - 1 + } + // check if the transaction is included in the next 6 blocks + if txPriorityFee.Cmp(priorityFeeForThirdTwoBlocks) >= 0 && txBaseFee.Cmp(sortedBaseFees[baseFeePercentileIndex]) >= 0 { + return calculateTimeForInclusion(chainID, 6) } - sort.Slice(fees, func(i, j int) bool { return fees[i].Cmp(fees[j]) < 0 }) - return fees, nil + return EstimatedTimeMoreThan1Min } diff --git a/services/wallet/router/fees/fees.go b/services/wallet/router/fees/fees.go index ab1d26515c9..d30b96adb80 100644 --- a/services/wallet/router/fees/fees.go +++ b/services/wallet/router/fees/fees.go @@ -32,28 +32,31 @@ var ( ) type MaxFeesLevels struct { - Low *hexutil.Big `json:"low"` - LowPriority *hexutil.Big `json:"lowPriority"` - Medium *hexutil.Big `json:"medium"` - MediumPriority *hexutil.Big `json:"mediumPriority"` - High *hexutil.Big `json:"high"` - HighPriority *hexutil.Big `json:"highPriority"` + Low *hexutil.Big `json:"low"` // Low max fee per gas in WEI + LowPriority *hexutil.Big `json:"lowPriority"` // Low priority fee in WEI + LowEstimatedTime uint `json:"lowEstimatedTime"` // Estimated time for low fees in seconds + Medium *hexutil.Big `json:"medium"` // Medium max fee per gas in WEI + MediumPriority *hexutil.Big `json:"mediumPriority"` // Medium priority fee in WEI + MediumEstimatedTime uint `json:"mediumEstimatedTime"` // Estimated time for medium fees in seconds + High *hexutil.Big `json:"high"` // High max fee per gas in WEI + HighPriority *hexutil.Big `json:"highPriority"` // High priority fee in WEI + HighEstimatedTime uint `json:"highEstimatedTime"` // Estimated time for high fees in seconds } type MaxPriorityFeesSuggestedBounds struct { - Lower *big.Int - Upper *big.Int + Lower *big.Int // Lower bound for priority fee per gas in WEI + Upper *big.Int // Upper bound for priority fee per gas in WEI } type SuggestedFees struct { - GasPrice *big.Int - BaseFee *big.Int - CurrentBaseFee *big.Int // Current network base fee (in ETH WEI) - MaxFeesLevels *MaxFeesLevels - MaxPriorityFeePerGas *big.Int // TODO: remove once clients stop using this field - MaxPriorityFeeSuggestedBounds *MaxPriorityFeesSuggestedBounds - L1GasFee *big.Float - EIP1559Enabled bool + GasPrice *big.Int // TODO: remove once clients stop using this field, used for EIP-1559 incompatible chains, not in use anymore + BaseFee *big.Int // TODO: remove once clients stop using this field, current network base fee (in ETH WEI), kept for backward compatibility + CurrentBaseFee *big.Int // Current network base fee (in ETH WEI) + MaxFeesLevels *MaxFeesLevels // Max fees levels for low, medium and high fee modes + MaxPriorityFeePerGas *big.Int // TODO: remove once clients stop using this field, kept for backward compatibility + MaxPriorityFeeSuggestedBounds *MaxPriorityFeesSuggestedBounds // Lower and upper bounds for priority fee per gas in WEI + L1GasFee *big.Float // TODO: remove once clients stop using this field, not in use anymore + EIP1559Enabled bool // TODO: remove it since all chains we have support EIP-1559 } // ////////////////////////////////////////////////////////////////////////////// @@ -71,23 +74,23 @@ type SuggestedFeesGwei struct { EIP1559Enabled bool `json:"eip1559Enabled"` } -func (m *MaxFeesLevels) FeeFor(mode GasFeeMode) (*big.Int, *big.Int, error) { +func (m *MaxFeesLevels) FeeFor(mode GasFeeMode) (*big.Int, *big.Int, uint, error) { if mode == GasFeeCustom { - return nil, nil, ErrCustomFeeModeNotAvailableInSuggestedFees + return nil, nil, 0, ErrCustomFeeModeNotAvailableInSuggestedFees } if mode == GasFeeLow { - return m.Low.ToInt(), m.LowPriority.ToInt(), nil + return m.Low.ToInt(), m.LowPriority.ToInt(), m.LowEstimatedTime, nil } if mode == GasFeeHigh { - return m.High.ToInt(), m.HighPriority.ToInt(), nil + return m.High.ToInt(), m.HighPriority.ToInt(), m.MediumEstimatedTime, nil } - return m.Medium.ToInt(), m.MediumPriority.ToInt(), nil + return m.Medium.ToInt(), m.MediumPriority.ToInt(), m.HighEstimatedTime, nil } -func (s *SuggestedFees) FeeFor(mode GasFeeMode) (*big.Int, *big.Int, error) { +func (s *SuggestedFees) FeeFor(mode GasFeeMode) (*big.Int, *big.Int, uint, error) { return s.MaxFeesLevels.FeeFor(mode) } @@ -150,6 +153,10 @@ func (f *FeeManager) SuggestedFees(ctx context.Context, chainID uint64) (*Sugges } } + suggestedFees.MaxFeesLevels.LowEstimatedTime = f.TransactionEstimatedTimeV2(ctx, chainID, suggestedFees.MaxFeesLevels.Low.ToInt(), suggestedFees.MaxFeesLevels.LowPriority.ToInt()) + suggestedFees.MaxFeesLevels.MediumEstimatedTime = f.TransactionEstimatedTimeV2(ctx, chainID, suggestedFees.MaxFeesLevels.Medium.ToInt(), suggestedFees.MaxFeesLevels.MediumPriority.ToInt()) + suggestedFees.MaxFeesLevels.HighEstimatedTime = f.TransactionEstimatedTimeV2(ctx, chainID, suggestedFees.MaxFeesLevels.High.ToInt(), suggestedFees.MaxFeesLevels.HighPriority.ToInt()) + return suggestedFees, nil } diff --git a/services/wallet/router/router.go b/services/wallet/router/router.go index 6973be94523..815ebeefce6 100644 --- a/services/wallet/router/router.go +++ b/services/wallet/router/router.go @@ -142,7 +142,7 @@ func (r *Router) SetTestBalanceMap(balanceMap map[string]*big.Int) { } } -func (r *Router) setCustomTxDetails(pathTxIdentity *requests.PathTxIdentity, pathTxCustomParams *requests.PathTxCustomParams) error { +func (r *Router) setCustomTxDetails(ctx context.Context, pathTxIdentity *requests.PathTxIdentity, pathTxCustomParams *requests.PathTxCustomParams) error { if pathTxIdentity == nil { return ErrTxIdentityNotProvided } @@ -164,31 +164,17 @@ func (r *Router) setCustomTxDetails(pathTxIdentity *requests.PathTxIdentity, pat return ErrCannotCustomizeIfNoRoute } + fetchedFees, err := r.feesManager.SuggestedFees(ctx, pathTxIdentity.ChainID) + if err != nil { + return err + } + for _, path := range r.activeRoutes.Best { if path.PathIdentity() != pathTxIdentity.PathIdentity() { continue } - if pathTxIdentity.IsApprovalTx { - path.ApprovalGasFeeMode = pathTxCustomParams.GasFeeMode - if pathTxCustomParams.GasFeeMode == fees.GasFeeCustom { - path.ApprovalTxNonce = (*hexutil.Uint64)(&pathTxCustomParams.Nonce) - path.ApprovalGasAmount = pathTxCustomParams.GasAmount - path.ApprovalMaxFeesPerGas = pathTxCustomParams.MaxFeesPerGas - path.ApprovalBaseFee = (*hexutil.Big)(new(big.Int).Sub(pathTxCustomParams.MaxFeesPerGas.ToInt(), pathTxCustomParams.PriorityFee.ToInt())) - path.ApprovalPriorityFee = pathTxCustomParams.PriorityFee - } - } else { - path.TxGasFeeMode = pathTxCustomParams.GasFeeMode - if pathTxCustomParams.GasFeeMode == fees.GasFeeCustom { - path.TxNonce = (*hexutil.Uint64)(&pathTxCustomParams.Nonce) - path.TxGasAmount = pathTxCustomParams.GasAmount - path.TxMaxFeesPerGas = pathTxCustomParams.MaxFeesPerGas - path.TxBaseFee = (*hexutil.Big)(new(big.Int).Sub(pathTxCustomParams.MaxFeesPerGas.ToInt(), pathTxCustomParams.PriorityFee.ToInt())) - path.TxPriorityFee = pathTxCustomParams.PriorityFee - } - } - + // update the custom params r.lastInputParamsMutex.Lock() if r.lastInputParams.PathTxCustomParams == nil { r.lastInputParams.PathTxCustomParams = make(map[string]*requests.PathTxCustomParams) @@ -196,6 +182,15 @@ func (r *Router) setCustomTxDetails(pathTxIdentity *requests.PathTxIdentity, pat r.lastInputParams.PathTxCustomParams[pathTxIdentity.TxIdentityKey()] = pathTxCustomParams r.lastInputParamsMutex.Unlock() + // update the path details + usedNonces := make(map[uint64]uint64) + err = r.evaluateAndUpdatePathDetails(ctx, path, fetchedFees, usedNonces, false, 0) + if err != nil { + return err + } + // inform the client about the changes + sendRouterResult(pathTxIdentity.RouterInputParamsUuid, r.activeRoutes, nil) + return nil } @@ -207,14 +202,14 @@ func (r *Router) SetFeeMode(ctx context.Context, pathTxIdentity *requests.PathTx return ErrCustomFeeModeCannotBeSetThisWay } - return r.setCustomTxDetails(pathTxIdentity, &requests.PathTxCustomParams{GasFeeMode: feeMode}) + return r.setCustomTxDetails(ctx, pathTxIdentity, &requests.PathTxCustomParams{GasFeeMode: feeMode}) } func (r *Router) SetCustomTxDetails(ctx context.Context, pathTxIdentity *requests.PathTxIdentity, pathTxCustomParams *requests.PathTxCustomParams) error { if pathTxCustomParams != nil && pathTxCustomParams.GasFeeMode != fees.GasFeeCustom { return ErrOnlyCustomFeeModeCanBeSetThisWay } - return r.setCustomTxDetails(pathTxIdentity, pathTxCustomParams) + return r.setCustomTxDetails(ctx, pathTxIdentity, pathTxCustomParams) } func newSuggestedRoutes( diff --git a/services/wallet/router/router_helper.go b/services/wallet/router/router_helper.go index b9a89d88ad3..f10ce489aca 100644 --- a/services/wallet/router/router_helper.go +++ b/services/wallet/router/router_helper.go @@ -195,7 +195,7 @@ func (r *Router) applyCustomFields(ctx context.Context, path *routes.Path, fetch if r.lastInputParams.PathTxCustomParams == nil || len(r.lastInputParams.PathTxCustomParams) == 0 { // if no custom params are provided, use the initial fee mode - maxFeesPerGas, priorityFee, err := fetchedFees.FeeFor(r.lastInputParams.GasFeeMode) + maxFeesPerGas, priorityFee, estimatedTime, err := fetchedFees.FeeFor(r.lastInputParams.GasFeeMode) if err != nil { return err } @@ -204,31 +204,35 @@ func (r *Router) applyCustomFields(ctx context.Context, path *routes.Path, fetch path.ApprovalMaxFeesPerGas = (*hexutil.Big)(maxFeesPerGas) path.ApprovalBaseFee = (*hexutil.Big)(fetchedFees.BaseFee) path.ApprovalPriorityFee = (*hexutil.Big)(priorityFee) + path.ApprovalEstimatedTime = estimatedTime } path.TxGasFeeMode = r.lastInputParams.GasFeeMode path.TxMaxFeesPerGas = (*hexutil.Big)(maxFeesPerGas) path.TxBaseFee = (*hexutil.Big)(fetchedFees.BaseFee) path.TxPriorityFee = (*hexutil.Big)(priorityFee) + path.TxEstimatedTime = estimatedTime } else { if path.ApprovalRequired { approvalTxIdentityKey := path.TxIdentityKey(true) if approvalTxCustomParams, ok := r.lastInputParams.PathTxCustomParams[approvalTxIdentityKey]; ok { path.ApprovalGasFeeMode = approvalTxCustomParams.GasFeeMode if approvalTxCustomParams.GasFeeMode != fees.GasFeeCustom { - maxFeesPerGas, priorityFee, err := fetchedFees.FeeFor(approvalTxCustomParams.GasFeeMode) + maxFeesPerGas, priorityFee, estimatedTime, err := fetchedFees.FeeFor(approvalTxCustomParams.GasFeeMode) if err != nil { return err } path.ApprovalMaxFeesPerGas = (*hexutil.Big)(maxFeesPerGas) path.ApprovalBaseFee = (*hexutil.Big)(fetchedFees.BaseFee) path.ApprovalPriorityFee = (*hexutil.Big)(priorityFee) + path.ApprovalEstimatedTime = estimatedTime } else { path.ApprovalTxNonce = (*hexutil.Uint64)(&approvalTxCustomParams.Nonce) path.ApprovalGasAmount = approvalTxCustomParams.GasAmount path.ApprovalMaxFeesPerGas = approvalTxCustomParams.MaxFeesPerGas path.ApprovalBaseFee = (*hexutil.Big)(new(big.Int).Sub(approvalTxCustomParams.MaxFeesPerGas.ToInt(), approvalTxCustomParams.PriorityFee.ToInt())) path.ApprovalPriorityFee = approvalTxCustomParams.PriorityFee + path.ApprovalEstimatedTime = r.feesManager.TransactionEstimatedTimeV2(ctx, path.FromChain.ChainID, path.ApprovalMaxFeesPerGas.ToInt(), path.ApprovalPriorityFee.ToInt()) } } } @@ -237,19 +241,21 @@ func (r *Router) applyCustomFields(ctx context.Context, path *routes.Path, fetch if txCustomParams, ok := r.lastInputParams.PathTxCustomParams[txIdentityKey]; ok { path.TxGasFeeMode = txCustomParams.GasFeeMode if txCustomParams.GasFeeMode != fees.GasFeeCustom { - maxFeesPerGas, priorityFee, err := fetchedFees.FeeFor(txCustomParams.GasFeeMode) + maxFeesPerGas, priorityFee, estimatedTime, err := fetchedFees.FeeFor(txCustomParams.GasFeeMode) if err != nil { return err } path.TxMaxFeesPerGas = (*hexutil.Big)(maxFeesPerGas) path.TxBaseFee = (*hexutil.Big)(fetchedFees.BaseFee) path.TxPriorityFee = (*hexutil.Big)(priorityFee) + path.TxEstimatedTime = estimatedTime } else { path.TxNonce = (*hexutil.Uint64)(&txCustomParams.Nonce) path.TxGasAmount = txCustomParams.GasAmount path.TxMaxFeesPerGas = txCustomParams.MaxFeesPerGas path.TxBaseFee = (*hexutil.Big)(new(big.Int).Sub(txCustomParams.MaxFeesPerGas.ToInt(), txCustomParams.PriorityFee.ToInt())) path.TxPriorityFee = txCustomParams.PriorityFee + path.TxEstimatedTime = r.feesManager.TransactionEstimatedTimeV2(ctx, path.FromChain.ChainID, path.TxMaxFeesPerGas.ToInt(), path.TxPriorityFee.ToInt()) } } } @@ -336,15 +342,6 @@ func (r *Router) evaluateAndUpdatePathDetails(ctx context.Context, path *routes. path.RequiredTokenBalance = requiredTokenBalance path.RequiredNativeBalance = requiredNativeBalance - path.TxEstimatedTime = r.feesManager.TransactionEstimatedTime(ctx, path.FromChain.ChainID, path.TxMaxFeesPerGas.ToInt()) - if path.ApprovalRequired { - if path.TxMaxFeesPerGas.ToInt().Cmp(path.ApprovalMaxFeesPerGas.ToInt()) == 0 { - path.ApprovalEstimatedTime = path.TxEstimatedTime - } else { - path.ApprovalEstimatedTime = r.feesManager.TransactionEstimatedTime(ctx, path.FromChain.ChainID, path.ApprovalMaxFeesPerGas.ToInt()) - } - } - return } diff --git a/services/wallet/router/routes/router_path.go b/services/wallet/router/routes/router_path.go index 622a985663a..7850ec47b1e 100644 --- a/services/wallet/router/routes/router_path.go +++ b/services/wallet/router/routes/router_path.go @@ -42,7 +42,7 @@ type Path struct { TxGasAmount uint64 // Gas used for the transaction TxBonderFees *hexutil.Big // Bonder fees for the transaction - used for Hop bridge (in selected token) TxTokenFees *hexutil.Big // Token fees for the transaction - used for bridges (represent the difference between the amount in and the amount out, in selected token) - TxEstimatedTime fees.TransactionEstimation + TxEstimatedTime uint // Estimated time for the transaction in seconds TxFee *hexutil.Big // fee for the transaction (includes tx fee only, doesn't include approval fees, l1 fees, l1 approval fees, token fees or bonders fees, in ETH WEI) TxL1Fee *hexutil.Big // L1 fee for the transaction - used for for transactions placed on L2 chains (in ETH WEI) @@ -57,7 +57,7 @@ type Path struct { ApprovalBaseFee *hexutil.Big // Base fee for the approval transaction (in ETH WEI) ApprovalPriorityFee *hexutil.Big // Priority fee for the approval transaction (in ETH WEI) ApprovalGasAmount uint64 // Gas used for the approval transaction - ApprovalEstimatedTime fees.TransactionEstimation + ApprovalEstimatedTime uint // Estimated time for the approval transaction in seconds ApprovalFee *hexutil.Big // Total fee for the approval transaction (includes approval tx fees only, doesn't include approval l1 fees, in ETH WEI) ApprovalL1Fee *hexutil.Big // L1 fee for the approval transaction - used for for transactions placed on L2 chains (in ETH WEI) diff --git a/services/wallet/router/routes/router_path_test.go b/services/wallet/router/routes/router_path_test.go index 866177b0dd4..256851713f6 100644 --- a/services/wallet/router/routes/router_path_test.go +++ b/services/wallet/router/routes/router_path_test.go @@ -36,7 +36,7 @@ func TestCopyPath(t *testing.T) { TxGasAmount: 100, TxBonderFees: (*hexutil.Big)(big.NewInt(100)), TxTokenFees: (*hexutil.Big)(big.NewInt(100)), - TxEstimatedTime: fees.TransactionEstimation(100), + TxEstimatedTime: 100, TxFee: (*hexutil.Big)(big.NewInt(100)), TxL1Fee: (*hexutil.Big)(big.NewInt(100)), ApprovalRequired: true, @@ -46,7 +46,7 @@ func TestCopyPath(t *testing.T) { ApprovalBaseFee: (*hexutil.Big)(big.NewInt(100)), ApprovalPriorityFee: (*hexutil.Big)(big.NewInt(100)), ApprovalGasAmount: 100, - ApprovalEstimatedTime: fees.TransactionEstimation(100), + ApprovalEstimatedTime: 100, ApprovalFee: (*hexutil.Big)(big.NewInt(100)), ApprovalL1Fee: (*hexutil.Big)(big.NewInt(100)), TxTotalFee: (*hexutil.Big)(big.NewInt(100)),