Skip to content

Commit

Permalink
use estimated gas rate for bitcoin outTx and put a cap on total fee
Browse files Browse the repository at this point in the history
  • Loading branch information
ws4charlie committed Oct 5, 2023
1 parent dec6172 commit 6f5e548
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 28 deletions.
26 changes: 17 additions & 9 deletions zetaclient/bitcoin_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,21 @@ func (ob *BitcoinChainClient) WatchGasPrice() {
}
}

// EstimateSmartFee returns the fees per kilobyte (BTC/kb)
func (ob *BitcoinChainClient) EstimateGasPrice() (*big.Int, error) {
feeResult, err := ob.rpcClient.EstimateSmartFee(1, &btcjson.EstimateModeConservative)
if err != nil {
return nil, err
}
if feeResult.Errors != nil {
return nil, fmt.Errorf("error estimating smart fee: %s", feeResult.Errors)
}
if feeResult.FeeRate == nil {
return nil, fmt.Errorf("fee rate is nil")
}
return new(big.Int).SetInt64(int64(*feeResult.FeeRate * 1e8)), nil
}

func (ob *BitcoinChainClient) PostGasPrice() error {
if ob.chain.ChainId == 18444 { //bitcoin regtest
bn, err := ob.rpcClient.GetBlockCount()
Expand All @@ -451,29 +466,22 @@ func (ob *BitcoinChainClient) PostGasPrice() error {
//ob.logger.WatchGasPrice.Debug().Msgf("PostGasPrice zeta tx: %s", zetaHash)
return nil
}
// EstimateSmartFee returns the fees per kilobyte (BTC/kb) targeting given block confirmation
feeResult, err := ob.rpcClient.EstimateSmartFee(1, &btcjson.EstimateModeConservative)
gasPrice, err := ob.EstimateGasPrice()
if err != nil {
return err
}
if feeResult.Errors != nil || feeResult.FeeRate == nil {
return fmt.Errorf("error getting gas price: %s", feeResult.Errors)
}
gasPrice := big.NewFloat(0)
gasPriceU64, _ := gasPrice.Mul(big.NewFloat(*feeResult.FeeRate), big.NewFloat(1e8)).Uint64()
bn, err := ob.rpcClient.GetBlockCount()
if err != nil {
return err
}
// #nosec G701 always positive
zetaHash, err := ob.zetaClient.PostGasPrice(ob.chain, gasPriceU64, "100", uint64(bn))
zetaHash, err := ob.zetaClient.PostGasPrice(ob.chain, gasPrice.Uint64(), "100", uint64(bn))
if err != nil {
ob.logger.WatchGasPrice.Err(err).Msg("PostGasPrice:")
return err
}
_ = zetaHash
//ob.logger.WatchGasPrice.Debug().Msgf("PostGasPrice zeta tx: %s", zetaHash)
_ = feeResult
return nil
}

Expand Down
24 changes: 16 additions & 8 deletions zetaclient/btc_signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ import (

const (
maxNoOfInputsPerTx = 20
minFee = 5_000 // 0.00005 BTC
maxFee = 100_000 // 0.001 BTC
oneKSatoshis = 1_000 // 0.00001 BTC
)

type BTCSigner struct {
Expand Down Expand Up @@ -61,7 +64,6 @@ func NewBTCSigner(cfg config.BTCConfig, tssSigner TSSSigner, logger zerolog.Logg
// SignWithdrawTx receives utxos sorted by value, amount in BTC, feeRate in BTC per Kb
func (signer *BTCSigner) SignWithdrawTx(to *btcutil.AddressWitnessPubKeyHash, amount float64, gasPrice *big.Int, btcClient *BitcoinChainClient, height uint64, nonce uint64, chain *common.Chain) (*wire.MsgTx, error) {
estimateFee := 0.0001 // 10,000 sats, should be good for testnet
minFee := 0.00005
nonceMark := NonceMarkAmount(nonce)

// refresh unspent UTXOs and continue with keysign regardless of error
Expand Down Expand Up @@ -97,10 +99,14 @@ func (signer *BTCSigner) SignWithdrawTx(to *btcutil.AddressWitnessPubKeyHash, am
fees := new(big.Int).Mul(big.NewInt(int64(tx.SerializeSize())), gasPrice)
fees.Div(fees, big.NewInt(1000)) //FIXME: feeRate KB is 1000B or 1024B?
// #nosec G701 always in range
if fees.Int64() < int64(minFee*1e8) {
fmt.Printf("fees %d is less than minFee %f; use minFee", fees, minFee*1e8)
if fees.Int64() < minFee {
fmt.Printf("fees %d is less than minFee %d; use minFee", fees, minFee)
// #nosec G701 always in range
fees = big.NewInt(int64(minFee * 1e8))
fees = big.NewInt(minFee)
} else if fees.Int64() > maxFee {
fmt.Printf("fees %d is greater than maxFee %d; use maxFee", fees, maxFee)
// #nosec G701 always in range
fees = big.NewInt(maxFee)
}

// calculate remaining btc to TSS self
Expand Down Expand Up @@ -253,11 +259,13 @@ func (signer *BTCSigner) TryProcessOutTx(send *types.CrossChainTx, outTxMan *Out
return
}

gasprice, ok := new(big.Int).SetString(params.OutboundTxGasPrice, 10)
if !ok {
logger.Error().Msgf("cannot convert gas price %s ", params.OutboundTxGasPrice)
// Estimate the fee rate
estimated, err := btcClient.EstimateGasPrice()
if err != nil {
logger.Error().Err(err).Msgf("cannot estimate fee")
return
}
gasprice := roundGasPriceUp(estimated, oneKSatoshis) // round up to nearst K satoshis

// FIXME: config chain params
addr, err := btcutil.DecodeAddress(params.Receiver, config.BitconNetParams)
Expand All @@ -279,7 +287,7 @@ func (signer *BTCSigner) TryProcessOutTx(send *types.CrossChainTx, outTxMan *Out
logger.Warn().Err(err).Msgf("SignOutboundTx error: nonce %d chain %d", outboundTxTssNonce, params.ReceiverChainId)
return
}
logger.Info().Msgf("Key-sign success: %d => %s, nonce %d", send.InboundTxParams.SenderChainId, btcClient.chain.ChainName, outboundTxTssNonce)
logger.Info().Msgf("Key-sign success: %d => %s, nonce %d, gasprice %d", send.InboundTxParams.SenderChainId, btcClient.chain.ChainName, outboundTxTssNonce, gasprice)
// FIXME: add prometheus metrics
_, err = zetaBridge.GetObserverList(btcClient.chain)
if err != nil {
Expand Down
16 changes: 5 additions & 11 deletions zetaclient/evm_signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ import (
observertypes "github.com/zeta-chain/zetacore/x/observer/types"
)

const (
Gwei = 1_000_000_000
)

type EVMSigner struct {
client *ethclient.Client
chain *common.Chain
Expand Down Expand Up @@ -329,7 +333,7 @@ func (signer *EVMSigner) TryProcessOutTx(send *types.CrossChainTx, outTxMan *Out
logger.Error().Err(err).Msgf("cannot get gas price from chain %s ", toChain)
return
}
gasprice = roundUpToNearestGwei(suggested)
gasprice = roundGasPriceUp(suggested, Gwei) // round up to nearest gwei
} else {
logger.Error().Err(err).Msgf("cannot convert gas price %s ", send.GetCurrentOutTxParam().OutboundTxGasPrice)
return
Expand Down Expand Up @@ -564,13 +568,3 @@ func (signer *EVMSigner) SignWhitelistTx(action string, _ ethcommon.Address, ass

return tx, nil
}

func roundUpToNearestGwei(gasPrice *big.Int) *big.Int {
oneGwei := big.NewInt(1_000_000_000) // 1 Gwei
mod := new(big.Int)
mod.Mod(gasPrice, oneGwei)
if mod.Cmp(big.NewInt(0)) == 0 { // gasprice is already a multiple of 1 Gwei
return gasPrice
}
return new(big.Int).Add(gasPrice, new(big.Int).Sub(oneGwei, mod))
}
12 changes: 12 additions & 0 deletions zetaclient/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package zetaclient
import (
"errors"
"math"
"math/big"
"time"

"github.com/btcsuite/btcd/txscript"
Expand Down Expand Up @@ -41,6 +42,17 @@ func round(f float64) int64 {
return int64(f + 0.5)
}

// roundGasPriceUp rounds up the gasPrice to the nearest multiple of base
func roundGasPriceUp(gasPrice *big.Int, base int64) *big.Int {
oneUnit := big.NewInt(base) // e.g. 1 Gwei
mod := new(big.Int)
mod.Mod(gasPrice, oneUnit)
if mod.Cmp(big.NewInt(0)) == 0 { // gasPrice is already a multiple of base
return gasPrice
}
return new(big.Int).Add(gasPrice, new(big.Int).Sub(oneUnit, mod))
}

func payToWitnessPubKeyHashScript(pubKeyHash []byte) ([]byte, error) {
return txscript.NewScriptBuilder().AddOp(txscript.OP_0).AddData(pubKeyHash).Script()
}
Expand Down

0 comments on commit 6f5e548

Please sign in to comment.