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
4 changes: 3 additions & 1 deletion .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ builds:
- linux
goarch:
- amd64

env:
- CGO_ENABLED=0
- CC=gcc
ldflags:
- -s -w -X main.version={{.Tag}} -X main.commitHash={{.ShortCommit}}

Expand Down
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,32 @@ and this project adheres to [Semantic Versioning]https://semver.org/spec/v2.0.0.

### Unreleased

### [0.9.8] - 2025-11-17

- fix: disable CGO support in goreleaser configuration
- fix: bug in banwidth delegation accounting
- fix: fixed evm incorrectly assigning gas fee tip
- fix: adjust total gas price calculation to remove gas tip cap addition
- fix: correct total gas price calculation to exclude gas tip cap
- fix: remove gas tip cap from total gas price calculation
- fix: fixed bug with secp256k1 curve in cgo disabled env
- fix: update gas estimation logic and add gas limit function for block
- fix: bandwidth estimate for tron transfer
- fix: evm estimate for transfer
- init e2e tests

### [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

- Added two-factor secret encryption [DV-2526]
- Add support events status for internal transactions [DV-3436]
- fix code rpc wrapping [DV-3461]

### [0.9.5] - 2025-09-15

- Add memory buffer for processing logs [DV-3361]
- Add dockerfile [DV-3387]

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ require (
github.com/btcsuite/btcd/btcec/v2 v2.3.5-0.20250213152832-bb52d7d78d9c
github.com/btcsuite/btcd/btcutil v1.1.7-0.20250213152832-bb52d7d78d9c
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.1-0.20250213152832-bb52d7d78d9c
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
github.com/ethereum/go-ethereum v1.15.8
github.com/fbsobreira/gotron-sdk v0.0.0-20250427130616-96b87f5d2100
github.com/go-playground/validator/v10 v10.26.0
Expand Down
23 changes: 6 additions & 17 deletions internal/fsm/fsmevm/fsm_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package fsmevm

import (
"context"
"crypto/ecdsa"
"fmt"
"math/big"
"strings"
Expand All @@ -11,7 +12,6 @@ import (
"github.com/dv-net/dv-processing/internal/store/repos/repo_transfer_transactions"
trxv2 "github.com/dv-net/dv-proto/gen/go/eproxy/transactions/v2"

"github.com/btcsuite/btcd/btcec/v2"
"github.com/dv-net/dv-processing/internal/constants"
"github.com/dv-net/dv-processing/internal/services/webhooks"
"github.com/dv-net/dv-processing/internal/store/repos"
Expand All @@ -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 @@ -202,8 +201,8 @@ func (s *FSM) getAssetDecimals(ctx context.Context, assetIdentifier string) (int

type walletCreds struct {
Address string
PrivateKey *btcec.PrivateKey
PublicKey *btcec.PublicKey
PrivateKey *ecdsa.PrivateKey
PublicKey *ecdsa.PublicKey
}

// getWalletCreds
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 @@ -288,7 +287,7 @@ func (s *FSM) sendBaseAsset(ctx context.Context, wCreds *walletCreds, toAddress
Data: nil,
})

signedTx, err := types.SignTx(tx, types.LatestSignerForChainID(chainID), wCreds.PrivateKey.ToECDSA())
signedTx, err := types.SignTx(tx, types.LatestSignerForChainID(chainID), wCreds.PrivateKey)
if err != nil {
return nil, nil, fmt.Errorf("sign transaction: %w", err)
}
Expand Down Expand Up @@ -346,7 +345,7 @@ func (s *FSM) sendERC20(ctx context.Context, wCreds *walletCreds, contractAddres
}

// create auth
auth, err := bind.NewKeyedTransactorWithChainID(wCreds.PrivateKey.ToECDSA(), chainID)
auth, err := bind.NewKeyedTransactorWithChainID(wCreds.PrivateKey, chainID)
if err != nil {
return nil, nil, err
}
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
}
}
16 changes: 8 additions & 8 deletions internal/fsm/fsmtron/fsm_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ package fsmtron

import (
"context"
"crypto/ecdsa"
"fmt"
"strings"
"time"

"connectrpc.com/connect"
"github.com/btcsuite/btcd/btcec/v2"
trxv2 "github.com/dv-net/dv-proto/gen/go/eproxy/transactions/v2"
"github.com/fbsobreira/gotron-sdk/pkg/common"
"github.com/fbsobreira/gotron-sdk/pkg/proto/api"
Expand Down Expand Up @@ -155,8 +155,8 @@ func (s *FSM) getAssetDecimals(ctx context.Context, assetIdentifier string) (int

type walletCreds struct {
Address string
PrivateKey *btcec.PrivateKey
PublicKey *btcec.PublicKey
PrivateKey *ecdsa.PrivateKey
PublicKey *ecdsa.PublicKey
}

// getWalletCreds
Expand Down Expand Up @@ -201,7 +201,7 @@ func (s *FSM) systemActivation(ctx context.Context, wCreds *walletCreds, toAddre
return nil, fmt.Errorf("create system activation tx error: %s", string(tx.Result.Message))
}

if err := s.tron.SignTransaction(tx.GetTransaction(), wCreds.PrivateKey.ToECDSA()); err != nil {
if err := s.tron.SignTransaction(tx.GetTransaction(), wCreds.PrivateKey); err != nil {
return nil, fmt.Errorf("sign transaction: %w", err)
}

Expand Down Expand Up @@ -252,7 +252,7 @@ func (s *FSM) sendTRX(ctx context.Context, wCreds *walletCreds, toAddress string
return nil, fmt.Errorf("create send trx tx error: %s", string(tx.Result.Message))
}

if err := s.tron.SignTransaction(tx.GetTransaction(), wCreds.PrivateKey.ToECDSA()); err != nil {
if err := s.tron.SignTransaction(tx.GetTransaction(), wCreds.PrivateKey); err != nil {
return nil, fmt.Errorf("sign transaction: %w", err)
}

Expand Down Expand Up @@ -319,7 +319,7 @@ func (s *FSM) sendTrc20(ctx context.Context, wCreds *walletCreds, contractAddres
return nil, fmt.Errorf("create send trc20 tx error: %s", string(tx.Result.Message))
}

if err := s.tron.SignTransaction(tx.GetTransaction(), wCreds.PrivateKey.ToECDSA()); err != nil {
if err := s.tron.SignTransaction(tx.GetTransaction(), wCreds.PrivateKey); err != nil {
return nil, fmt.Errorf("sign transaction: %w", err)
}

Expand Down Expand Up @@ -407,7 +407,7 @@ func (s *FSM) delegateResource(
return nil, nil, fmt.Errorf("create delegate tx error: %s", string(tx.Result.Message))
}

if err := s.tron.SignTransaction(tx.GetTransaction(), wCreds.PrivateKey.ToECDSA()); err != nil {
if err := s.tron.SignTransaction(tx.GetTransaction(), wCreds.PrivateKey); err != nil {
return nil, nil, fmt.Errorf("sign transaction: %w", err)
}

Expand Down Expand Up @@ -464,7 +464,7 @@ func (s *FSM) reclaimResource(ctx context.Context, wCreds *walletCreds, toAddres
return nil, nil, fmt.Errorf("create reclaim tx error: %s", string(tx.Result.Message))
}

if err := s.tron.SignTransaction(tx.GetTransaction(), wCreds.PrivateKey.ToECDSA()); err != nil {
if err := s.tron.SignTransaction(tx.GetTransaction(), wCreds.PrivateKey); err != nil {
return nil, nil, fmt.Errorf("sign transaction: %w", err)
}

Expand Down
2 changes: 1 addition & 1 deletion internal/fsm/fsmtron/stage_before.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ func (s *FSM) activateWalletResources(ctx context.Context, _ *workflow.Workflow,
}

// sign transaction
if err = s.tron.SignTransaction(tx.Transaction, wcreds.PrivateKey.ToECDSA()); err != nil {
if err = s.tron.SignTransaction(tx.Transaction, wcreds.PrivateKey); err != nil {
return fmt.Errorf("sign activation transaction: %w", err)
}

Expand Down
106 changes: 67 additions & 39 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 @@ -133,7 +147,11 @@ func (s *EVM) EstimateFee(ctx context.Context) (*EstimateFeeResult, error) {
fee.SuggestGasTipCap = minGasTipCap
}

fee.MaxFeePerGas = GetBaseFeeMultiplier(fee.SuggestGasPrice).Add(fee.SuggestGasTipCap)
fee.MaxFeePerGas = GetBaseFeeMultiplier(fee.SuggestGasPrice)

if fee.MaxFeePerGas.LessThan(fee.SuggestGasPrice.Add(fee.SuggestGasTipCap)) {
fee.MaxFeePerGas = fee.SuggestGasPrice.Add(fee.SuggestGasTipCap)
}

return fee, nil
}
Expand All @@ -157,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
Loading