Skip to content
Merged
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 1 addition & 12 deletions internal/fsm/fsmevm/fsm_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"),
Expand Down Expand Up @@ -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
}
}
100 changes: 62 additions & 38 deletions pkg/walletsdk/evm/estimate.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,53 +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)
}

gasAmount = decimal.NewFromUint64(estimatedGas)
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,
Expand All @@ -78,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
Expand Down Expand Up @@ -161,6 +175,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
Expand Down