From 508223b8f897f7f30ea4dd2bd59385bf21e1694d Mon Sep 17 00:00:00 2001 From: Webster Swift Date: Wed, 29 Oct 2025 12:36:37 +0300 Subject: [PATCH 1/2] fix: update gas estimation logic and add gas limit function for blockchain transfers --- CHANGELOG.md | 6 ++++++ internal/fsm/fsmevm/fsm_helpers.go | 13 +------------ pkg/walletsdk/evm/estimate.go | 20 +++++++++++++++++++- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b6e440..7a0b128 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,12 @@ and this project adheres to [Semantic Versioning]https://semver.org/spec/v2.0.0. ## Releases ### Unreleased + +### [0.9.8] - 2025-10-15 +- fix bandwidth estimate for tron transfer +- fix evm estimate for transfer + +### [0.9.7] - 2025-10-15 - Added support for disabled CGO_ENABLED flag in EVM, TRON cryptography [DV-3641] ### [0.9.6] - 2025-09-29 diff --git a/internal/fsm/fsmevm/fsm_helpers.go b/internal/fsm/fsmevm/fsm_helpers.go index 2840a67..f02cb1e 100644 --- a/internal/fsm/fsmevm/fsm_helpers.go +++ b/internal/fsm/fsmevm/fsm_helpers.go @@ -20,7 +20,6 @@ import ( "github.com/dv-net/dv-processing/pkg/utils" "github.com/dv-net/dv-processing/pkg/walletsdk/evm" "github.com/dv-net/dv-processing/pkg/walletsdk/evm/erc20" - "github.com/dv-net/dv-processing/pkg/walletsdk/wconstants" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -260,7 +259,7 @@ func (s *FSM) sendBaseAsset(ctx context.Context, wCreds *walletCreds, toAddress return nil, nil, err } - gasLimit := gasLimitByBlockchain(s.evm.Blockchain()) + gasLimit := evm.GasLimitByBlockchain(s.evm.Blockchain()) s.logger.Infow( s.stringForBaseAsset("sending %s"), @@ -477,13 +476,3 @@ func (s *FSM) prepareTransferTransactionTypeByStep() (*models.TransferTransactio return nil, fmt.Errorf("unknown transfer step: %s", s.wf.CurrentStep().Name) } } - -// gasLimitByBlockchain returns the gas limit by blockchain. -func gasLimitByBlockchain(blockchain wconstants.BlockchainType) uint64 { - switch blockchain { - case wconstants.BlockchainTypeArbitrum: - return 38000 - default: - return 21000 // Default gas limit for unknown blockchains - } -} diff --git a/pkg/walletsdk/evm/estimate.go b/pkg/walletsdk/evm/estimate.go index 2e33eec..92cb31d 100644 --- a/pkg/walletsdk/evm/estimate.go +++ b/pkg/walletsdk/evm/estimate.go @@ -42,7 +42,15 @@ func (s *EVM) EstimateTransfer(ctx context.Context, fromAddress, toAddress, asse return nil, fmt.Errorf("failed to estimate gas for eth: %w", err) } - gasAmount = decimal.NewFromUint64(estimatedGas) + // Use the actual gas limit that will be used in transaction + gasLimit := GasLimitByBlockchain(s.config.Blockchain) + + if estimatedGas > gasLimit { + gasAmount = decimal.NewFromUint64(estimatedGas) + } else { + gasAmount = decimal.NewFromUint64(gasLimit) + } + gasTipCap = estimate.SuggestGasTipCap totalGasPrice = estimate.MaxFeePerGas totalFeeAmount = totalGasPrice.Mul(gasAmount) @@ -161,6 +169,16 @@ func getMinGasTipCapByBlockchain(blockchain wconstants.BlockchainType) decimal.D } } +// GasLimitByBlockchain returns the gas limit by blockchain for base asset transfers. +func GasLimitByBlockchain(blockchain wconstants.BlockchainType) uint64 { + switch blockchain { + case wconstants.BlockchainTypeArbitrum: + return 38000 + default: + return 21000 // Default gas limit for unknown blockchains + } +} + func GetBaseFeeMultiplier(baseFeeWei decimal.Decimal) decimal.Decimal { items := []struct { threshold int64 From bd83b86fc4401ab1341e0f60fbc21298f1a9e078 Mon Sep 17 00:00:00 2001 From: Webster Swift Date: Wed, 29 Oct 2025 12:46:04 +0300 Subject: [PATCH 2/2] fix: refactor gas estimation logic for native and token transfers --- pkg/walletsdk/evm/estimate.go | 98 +++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 46 deletions(-) diff --git a/pkg/walletsdk/evm/estimate.go b/pkg/walletsdk/evm/estimate.go index 92cb31d..62d8b6d 100644 --- a/pkg/walletsdk/evm/estimate.go +++ b/pkg/walletsdk/evm/estimate.go @@ -22,61 +22,25 @@ type EstimateTransferResult struct { // EstimateTransfer estimates the transfer fee. Amount is in Eth func (s *EVM) EstimateTransfer(ctx context.Context, fromAddress, toAddress, assetIdentifier string, amount decimal.Decimal, decimals int64) (*EstimateTransferResult, error) { - var gasAmount decimal.Decimal - var gasTipCap decimal.Decimal - var totalFeeAmount decimal.Decimal - var totalGasPrice decimal.Decimal - estimate, err := s.EstimateFee(ctx) if err != nil { return nil, fmt.Errorf("failed to estimate fee: %w", err) } + var gasAmount decimal.Decimal if assetIdentifier == s.config.Blockchain.GetAssetIdentifier() { - estimatedGas, err := s.node.EstimateGas(ctx, ethereum.CallMsg{ - From: common.HexToAddress(fromAddress), - To: utils.Pointer(common.HexToAddress(toAddress)), - Value: NewUnit(amount, EtherUnitEther).Value(EtherUnitWei).BigInt(), - }) - if err != nil { - return nil, fmt.Errorf("failed to estimate gas for eth: %w", err) - } - - // Use the actual gas limit that will be used in transaction - gasLimit := GasLimitByBlockchain(s.config.Blockchain) - - if estimatedGas > gasLimit { - gasAmount = decimal.NewFromUint64(estimatedGas) - } else { - gasAmount = decimal.NewFromUint64(gasLimit) - } - - gasTipCap = estimate.SuggestGasTipCap - totalGasPrice = estimate.MaxFeePerGas - totalFeeAmount = totalGasPrice.Mul(gasAmount) + gasAmount, err = s.estimateNativeAssetGas(ctx, fromAddress, toAddress, amount) } else { - amount = amount.Mul(decimal.NewFromInt(1).Mul(decimal.NewFromInt(10).Pow(decimal.NewFromInt(decimals)))) - - data, err := s.abi.Pack("transfer", common.HexToAddress(toAddress), amount.BigInt()) - if err != nil { - return nil, fmt.Errorf("failed to pack transfer data: %w", err) - } - - estimatedGas, err := s.node.EstimateGas(ctx, ethereum.CallMsg{ - From: common.HexToAddress(fromAddress), - To: utils.Pointer(common.HexToAddress(assetIdentifier)), - Data: data, - }) - if err != nil { - return nil, fmt.Errorf("failed to estimate gas for contract: %w", err) - } - - gasAmount = decimal.NewFromUint64(estimatedGas) - gasTipCap = estimate.SuggestGasTipCap - totalGasPrice = estimate.MaxFeePerGas - totalFeeAmount = totalGasPrice.Mul(gasAmount) + gasAmount, err = s.estimateTokenGas(ctx, fromAddress, toAddress, assetIdentifier, amount, decimals) + } + if err != nil { + return nil, err } + gasTipCap := estimate.SuggestGasTipCap + totalGasPrice := estimate.MaxFeePerGas + totalFeeAmount := totalGasPrice.Mul(gasAmount) + return &EstimateTransferResult{ TotalFeeAmount: totalFeeAmount, TotalGasPrice: totalGasPrice, @@ -86,6 +50,48 @@ func (s *EVM) EstimateTransfer(ctx context.Context, fromAddress, toAddress, asse }, nil } +// estimateNativeAssetGas estimates gas for native blockchain asset transfers +func (s *EVM) estimateNativeAssetGas(ctx context.Context, fromAddress, toAddress string, amount decimal.Decimal) (decimal.Decimal, error) { + estimatedGas, err := s.node.EstimateGas(ctx, ethereum.CallMsg{ + From: common.HexToAddress(fromAddress), + To: utils.Pointer(common.HexToAddress(toAddress)), + Value: NewUnit(amount, EtherUnitEther).Value(EtherUnitWei).BigInt(), + }) + if err != nil { + return decimal.Zero, fmt.Errorf("failed to estimate gas for eth: %w", err) + } + + // Use the actual gas limit that will be used in transaction + gasLimit := GasLimitByBlockchain(s.config.Blockchain) + + if estimatedGas > gasLimit { + return decimal.NewFromUint64(estimatedGas), nil + } + return decimal.NewFromUint64(gasLimit), nil +} + +// estimateTokenGas estimates gas for token (ERC-20) transfers +func (s *EVM) estimateTokenGas(ctx context.Context, fromAddress, toAddress, assetIdentifier string, amount decimal.Decimal, decimals int64) (decimal.Decimal, error) { + // Convert amount to token's smallest unit + amount = amount.Mul(decimal.NewFromInt(1).Mul(decimal.NewFromInt(10).Pow(decimal.NewFromInt(decimals)))) + + data, err := s.abi.Pack("transfer", common.HexToAddress(toAddress), amount.BigInt()) + if err != nil { + return decimal.Zero, fmt.Errorf("failed to pack transfer data: %w", err) + } + + estimatedGas, err := s.node.EstimateGas(ctx, ethereum.CallMsg{ + From: common.HexToAddress(fromAddress), + To: utils.Pointer(common.HexToAddress(assetIdentifier)), + Data: data, + }) + if err != nil { + return decimal.Zero, fmt.Errorf("failed to estimate gas for contract: %w", err) + } + + return decimal.NewFromUint64(estimatedGas), nil +} + // EstimateFeeResult represents the result of the fee estimation // // All values are in Wei