Skip to content

Commit

Permalink
use 2x fee cap (wallet configuration setting) for Swap transaction ty…
Browse files Browse the repository at this point in the history
…pe since it's under sever time limit for trade to execute successfully
  • Loading branch information
norwnd committed Jan 6, 2025
1 parent e35c7c3 commit 00f0d2c
Show file tree
Hide file tree
Showing 14 changed files with 117 additions and 47 deletions.
24 changes: 17 additions & 7 deletions client/asset/btc/btc.go
Original file line number Diff line number Diff line change
Expand Up @@ -990,7 +990,17 @@ var _ asset.FeeRater = (*ExchangeWalletNoAuth)(nil)

// FeeRate satisfies asset.FeeRater.
func (btc *baseWallet) FeeRate() (rate uint64, tooLow bool) {
rate, tooLow, err := btc.feeRate(1)
rate, tooLow, err := btc.feeRate(1, btc.feeRateLimit())
if err != nil {
btc.log.Tracef("Failed to get fee rate: %v", err)
return 0, false
}
return rate, tooLow
}

// FeeRateSwap is same as FeeRate but for swaps.
func (btc *baseWallet) FeeRateSwap() (rate uint64, tooLow bool) {
rate, tooLow, err := btc.feeRate(1, 2*btc.feeRateLimit())
if err != nil {
btc.log.Tracef("Failed to get fee rate: %v", err)
return 0, false
Expand Down Expand Up @@ -1604,7 +1614,7 @@ func (btc *baseWallet) connect(ctx context.Context) (*sync.WaitGroup, error) {
return nil, fmt.Errorf("invalid best block hash from %s node: %v", btc.symbol, err)
}
// Check for method unknown error for feeRate method.
_, _, err = btc.feeRate(1)
_, _, err = btc.feeRate(1, btc.feeRateLimit())
if isMethodNotFoundErr(err) {
return nil, fmt.Errorf("fee estimation method not found. Are you configured for the correct RPC?")
}
Expand Down Expand Up @@ -1855,7 +1865,7 @@ func (btc *baseWallet) legacyBalance() (*asset.Balance, error) {

// feeRate returns the current optimal fee rate in sat / byte using the
// estimatesmartfee RPC or an external API if configured and enabled.
func (btc *baseWallet) feeRate(confTarget uint64) (feeRate uint64, tooLow bool, err error) {
func (btc *baseWallet) feeRate(confTarget uint64, feeRateCap uint64) (feeRate uint64, tooLow bool, err error) {
allowExternalFeeRate := btc.apiFeeFallback()
// Because of the problems Bitcoin's unstable estimatesmartfee has caused,
// we won't use it.
Expand Down Expand Up @@ -1884,12 +1894,12 @@ func (btc *baseWallet) feeRate(confTarget uint64) (feeRate uint64, tooLow bool,
}

btc.log.Tracef("Retrieved fee rate from external API: %v", feeRate)
if feeRate > btc.feeRateLimit() {
if feeRate > feeRateCap {
btc.log.Tracef("capping fee rate %v retrieved from external API at user-configured limit of %v", feeRate, btc.feeRateLimit())
if float64(feeRate) > 1.5*float64(btc.feeRateLimit()) {
if float64(feeRate) > 1.5*float64(feeRateCap) {
tooLow = true // capped rate will be too low
}
feeRate = btc.feeRateLimit()
feeRate = feeRateCap
}

return feeRate, tooLow, nil
Expand Down Expand Up @@ -1955,7 +1965,7 @@ func (a amount) String() string {
// number of confirmations, but falls back to the suggestion or fallbackFeeRate
// via feeRateWithFallback.
func (btc *baseWallet) targetFeeRateWithFallback(confTarget, feeSuggestion uint64) uint64 {
feeRate, _, err := btc.feeRate(confTarget)
feeRate, _, err := btc.feeRate(confTarget, btc.feeRateLimit())
if err == nil && feeRate > 0 {
btc.log.Tracef("Obtained estimate for %d-conf fee rate, %d", confTarget, feeRate)
return feeRate
Expand Down
6 changes: 3 additions & 3 deletions client/asset/dash/dash.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ var (
Key: "feeratelimit",
DisplayName: "Highest acceptable fee rate",
Description: "This is the highest network fee rate you are willing to " +
"pay on swap transactions. If feeratelimit is lower than a market's " +
"maxfeerate, you will not be able to trade on that market with this " +
"wallet. Units: DASH/kB",
"pay for transactions, fee rate for Swap transactions will be 2x of that" +
"(because they need to be mined faster than any other transaction type for " +
"trades to execute). Units: DASH/kB",
DefaultValue: dexdash.DefaultFeeRateLimit * 1000 / 1e8,
},
{
Expand Down
11 changes: 8 additions & 3 deletions client/asset/dcr/dcr.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,9 @@ var (
Key: "feeratelimit",
DisplayName: "Highest acceptable fee rate",
Description: "This is the highest network fee rate you are willing to " +
"pay on swap transactions. If feeratelimit is lower than a market's " +
"maxfeerate, you will not be able to trade on that market with this " +
"wallet. Units: DCR/kB",
"pay for transactions, fee rate for Swap transactions will be 2x of that" +
"(because they need to be mined faster than any other transaction type for " +
"trades to execute). Units: DCR/kB",
DefaultValue: defaultFeeRateLimit * 1000 / 1e8,
},
{
Expand Down Expand Up @@ -1276,6 +1276,11 @@ func (dcr *ExchangeWallet) FeeRate() (rate uint64, tooLow bool) {
return rate, false // DCR fees are never too low in practice
}

// FeeRateSwap is same as FeeRate but for swaps.
func (dcr *ExchangeWallet) FeeRateSwap() (rate uint64, tooLow bool) {
return dcr.FeeRate()
}

// feeRate returns the current optimal fee rate in atoms / byte.
func (dcr *ExchangeWallet) feeRate(confTarget uint64) (uint64, error) {
if dcr.ctx == nil {
Expand Down
6 changes: 3 additions & 3 deletions client/asset/dgb/dgb.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ var (
Key: "feeratelimit",
DisplayName: "Highest acceptable fee rate",
Description: "This is the highest network fee rate you are willing to " +
"pay on swap transactions. If feeratelimit is lower than a market's " +
"maxfeerate, you will not be able to trade on that market with this " +
"wallet. Units: BTC/kB",
"pay for transactions, fee rate for Swap transactions will be 2x of that" +
"(because they need to be mined faster than any other transaction type for " +
"trades to execute). Units: BTC/kB",
DefaultValue: dexdgb.DefaultFeeRateLimit * 1000 / 1e8, // higher than BTC default
},
{
Expand Down
6 changes: 3 additions & 3 deletions client/asset/doge/doge.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ var (
Key: "feeratelimit",
DisplayName: "Highest acceptable fee rate",
Description: "This is the highest network fee rate you are willing to " +
"pay on swap transactions. If feeratelimit is lower than a market's " +
"maxfeerate, you will not be able to trade on that market with this " +
"wallet. Units: BTC/kB",
"pay for transactions, fee rate for Swap transactions will be 2x of that" +
"(because they need to be mined faster than any other transaction type for " +
"trades to execute). Units: BTC/kB",
DefaultValue: dexdoge.DefaultFeeRateLimit * 1000 / 1e8,
},
{
Expand Down
57 changes: 44 additions & 13 deletions client/asset/eth/eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,9 @@ var (
Key: "gasfeelimit",
DisplayName: "Gas Fee Limit",
Description: "This is the highest network fee rate you are willing to " +
"pay on swap transactions. If gasfeelimit is lower than a market's " +
"maxfeerate, you will not be able to trade on that market with this " +
"wallet. Units: gwei / gas",
"pay for transactions, fee rate for Swap transactions will be 2x of that" +
"(because they need to be mined faster than any other transaction type for " +
"trades to execute). Units: gwei / gas",
DefaultValue: defaultGasFeeLimit,
},
}
Expand Down Expand Up @@ -3634,38 +3634,50 @@ func (w *baseWallet) currentFeeRate(ctx context.Context) (_ *big.Int, err error)
return new(big.Int).Add(b, t), nil
}

// recommendedMaxFeeRate finds recommended max fee rate (in wei units) using the
// somewhat standard baseRate * 2 + tip formula, capping it at user-configured value.
// recommendedMaxFeeRate returns recommended max fee rate (in wei units) for various
// transactions this wallet supports (pretty much all transaction types except for
// swap transactions).
func (eth *baseWallet) recommendedMaxFeeRate(ctx context.Context) (maxFeeRate, tipRate *big.Int, tooLow bool, err error) {
userConfiguredTotalCap := new(big.Int).Mul(big.NewInt(int64(eth.gasFeeLimitV)), big.NewInt(gweiConversionFactor))
return eth.recommendedMaxFeeRateWithCap(ctx, userConfiguredTotalCap)
}

// recommendedMaxFeeRateSwap is same as recommendedMaxFeeRate but for swap transactions.
func (eth *baseWallet) recommendedMaxFeeRateSwap(ctx context.Context) (maxFeeRate, tipRate *big.Int, tooLow bool, err error) {
userConfiguredTotalCap := new(big.Int).Mul(big.NewInt(int64(eth.gasFeeLimitV)), big.NewInt(gweiConversionFactor))
userConfiguredTotalCap = new(big.Int).Mul(userConfiguredTotalCap, big.NewInt(2))
return eth.recommendedMaxFeeRateWithCap(ctx, userConfiguredTotalCap)
}

// recommendedMaxFeeRateWithCap finds recommended max fee rate (in wei units) using the
// somewhat standard baseRate * 2 + tip formula, capping it at user-configured value.
func (eth *baseWallet) recommendedMaxFeeRateWithCap(ctx context.Context, userConfiguredTotalCap *big.Int) (maxFeeRate, tipRate *big.Int, tooLow bool, err error) {
base, tip, err := eth.currentNetworkFees(ctx)
if err != nil {
return nil, nil, false, fmt.Errorf("Error getting net fee state: %v", err)
}

userConfiguredMaxTotal := new(big.Int).Mul(big.NewInt(int64(eth.gasFeeLimitV)), big.NewInt(gweiConversionFactor))
recommendedTotal := new(big.Int).Add(tip, new(big.Int).Mul(base, big.NewInt(2)))
// make sure we don't blindly follow 3rd-party supplied estimates by capping it
// at user-configured value
if recommendedTotal.Cmp(userConfiguredMaxTotal) > 0 {
if recommendedTotal.Cmp(userConfiguredTotalCap) > 0 {
recommendedTotalGwei, err := dexeth.WeiToGweiSafe(recommendedTotal)
if err != nil {
return nil, nil, false, fmt.Errorf("couldn't convert Wei to Gwei: %v", err)
}
userConfiguredMaxTotalGwei, err := dexeth.WeiToGweiSafe(userConfiguredMaxTotal)
userConfiguredTotalCapGwei, err := dexeth.WeiToGweiSafe(userConfiguredTotalCap)
if err != nil {
return nil, nil, false, fmt.Errorf("couldn't convert Wei to Gwei: %v", err)
}
tooLow = false
if float64(recommendedTotalGwei) > 1.5*float64(userConfiguredMaxTotalGwei) {
if float64(recommendedTotalGwei) > 1.5*float64(userConfiguredTotalCapGwei) {
tooLow = true
}
return userConfiguredMaxTotal, tip, tooLow, nil
return userConfiguredTotalCap, tip, tooLow, nil
}
return recommendedTotal, tip, false, nil
}

// recommendedMaxFeeRateGwei gets the recommended max fee rate and converts it
// to gwei.
func (w *baseWallet) recommendedMaxFeeRateGwei(ctx context.Context) (maxRate uint64, tooLow bool, err error) {
feeRate, _, tooLow, err := w.recommendedMaxFeeRate(ctx)
if err != nil {
Expand All @@ -3675,7 +3687,16 @@ func (w *baseWallet) recommendedMaxFeeRateGwei(ctx context.Context) (maxRate uin
return maxRate, tooLow, err
}

// FeeRate satisfies asset.FeeRater.
func (w *baseWallet) recommendedMaxFeeRateSwapGwei(ctx context.Context) (maxRate uint64, tooLow bool, err error) {
feeRate, _, tooLow, err := w.recommendedMaxFeeRateSwap(ctx)
if err != nil {
return 0, false, err
}
maxRate, err = dexeth.WeiToGweiSafe(feeRate)
return maxRate, tooLow, err
}

// FeeRate satisfies asset.FeeRater and returns fee rate (gas price) in Gwei.
func (eth *baseWallet) FeeRate() (rate uint64, tooLow bool) {
r, tooLow, err := eth.recommendedMaxFeeRateGwei(eth.ctx)
if err != nil {
Expand All @@ -3685,6 +3706,16 @@ func (eth *baseWallet) FeeRate() (rate uint64, tooLow bool) {
return r, tooLow
}

// FeeRateSwap is same as FeeRate but for swaps.
func (eth *baseWallet) FeeRateSwap() (rate uint64, tooLow bool) {
r, tooLow, err := eth.recommendedMaxFeeRateSwapGwei(eth.ctx)
if err != nil {
eth.log.Errorf("Error getting max fee recommendation: %v", err)
return 0, false
}
return r, tooLow
}

func (eth *ETHWallet) checkPeers() {
numPeers := eth.node.peerCount()

Expand Down
6 changes: 3 additions & 3 deletions client/asset/firo/firo.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ var (
Key: "feeratelimit",
DisplayName: "Highest acceptable fee rate",
Description: "This is the highest network fee rate you are willing to " +
"pay on swap transactions. If feeratelimit is lower than a market's " +
"maxfeerate, you will not be able to trade on that market with this " +
"wallet. Units: FIRO/kB",
"pay for transactions, fee rate for Swap transactions will be 2x of that" +
"(because they need to be mined faster than any other transaction type for " +
"trades to execute). Units: FIRO/kB",
DefaultValue: dexfiro.DefaultFeeRateLimit * 1000 / 1e8,
},
{
Expand Down
5 changes: 5 additions & 0 deletions client/asset/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,11 @@ type FeeRater interface {
// as well as tooLow signalling if this rate would be too low with respect to
// current networking conditions.
FeeRate() (rate uint64, tooLow bool)
// FeeRateSwap is same as FeeRate but returns fee rate specifically for swap transactions,
// the main difference being that swap transactions have a tight time-constraints so it's
// very desirable to have higher fee rate for swaps than all other types of transactions
// wallet supports.
FeeRateSwap() (rate uint64, tooLow bool)
}

// FundsMixingStats describes the current state of a wallet's funds mixer.
Expand Down
6 changes: 3 additions & 3 deletions client/asset/polygon/polygon.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ var (
Key: "gasfeelimit",
DisplayName: "Gas Fee Limit",
Description: "This is the highest network fee rate you are willing to " +
"pay on swap transactions. If gasfeelimit is lower than a market's " +
"maxfeerate, you will not be able to trade on that market with this " +
"wallet. Units: gwei / gas",
"pay for transactions, fee rate for Swap transactions will be 2x of that" +
"(because they need to be mined faster than any other transaction type for " +
"trades to execute). Units: gwei / gas",
DefaultValue: defaultGasFeeLimit,
},
}
Expand Down
6 changes: 3 additions & 3 deletions client/asset/zcl/zcl.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ var (
Key: "feeratelimit",
DisplayName: "Highest acceptable fee rate",
Description: "This is the highest network fee rate you are willing to " +
"pay on swap transactions. If feeratelimit is lower than a market's " +
"maxfeerate, you will not be able to trade on that market with this " +
"wallet. Units: BTC/kB",
"pay for transactions, fee rate for Swap transactions will be 2x of that" +
"(because they need to be mined faster than any other transaction type for " +
"trades to execute). Units: BTC/kB",
DefaultValue: defaultFeeRateLimit * 1000 / 1e8,
},
{
Expand Down
11 changes: 8 additions & 3 deletions client/asset/zec/zec.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,9 @@ var (
Key: "feeratelimit",
DisplayName: "Highest acceptable fee rate",
Description: "This is the highest network fee rate you are willing to " +
"pay on swap transactions. If feeratelimit is lower than a market's " +
"maxfeerate, you will not be able to trade on that market with this " +
"wallet. Units: BTC/kB",
"pay for transactions, fee rate for Swap transactions will be 2x of that" +
"(because they need to be mined faster than any other transaction type for " +
"trades to execute). Units: BTC/kB",
DefaultValue: defaultFeeRateLimit * 1000 / 1e8,
},
{
Expand Down Expand Up @@ -350,6 +350,11 @@ func (w *zecWallet) FeeRate() (rate uint64, tooLow bool) {
return 5000, false // per logical action
}

// FeeRateSwap is same as FeeRate but for swaps.
func (w *zecWallet) FeeRateSwap() (rate uint64, tooLow bool) {
return w.FeeRate()
}

func (w *zecWallet) Connect(ctx context.Context) (*sync.WaitGroup, error) {
w.ctx = ctx

Expand Down
4 changes: 4 additions & 0 deletions client/core/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1189,6 +1189,10 @@ func (w *TFeeRater) FeeRate() (rate uint64, tooLow bool) {
return w.feeRate, false
}

func (w *TFeeRater) FeeRateSwap() (rate uint64, tooLow bool) {
return w.FeeRate()
}

type TLiveReconfigurer struct {
*TXCWallet
restart bool
Expand Down
2 changes: 1 addition & 1 deletion client/core/trade.go
Original file line number Diff line number Diff line change
Expand Up @@ -2391,7 +2391,7 @@ func (c *Core) swapMatchGroup(t *trackedTrade, matches []*matchTracker, errs *er
}

// calculate swap fee rate
swapFeeRate, feeRateTooLow := fromWallet.feeRate()
swapFeeRate, feeRateTooLow := fromWallet.feeRateSwap()
if swapFeeRate == 0 { // either not a FeeRater, or FeeRate failed
swapFeeRate = t.dc.bestBookFeeSuggestion(fromWallet.AssetID)
}
Expand Down
14 changes: 12 additions & 2 deletions client/core/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -691,8 +691,7 @@ func (w *xcWallet) setFeeState(feeRate uint64) {
})
}

// feeRate returns a fee rate for a FeeRater is available and generates a
// non-zero rate.
// feeRate returns a fee rate (if wallet is a FeeRater).
func (w *xcWallet) feeRate() (rate uint64, tooLow bool) {
if rater, is := w.Wallet.(asset.FeeRater); !is {
return 0, false
Expand All @@ -702,3 +701,14 @@ func (w *xcWallet) feeRate() (rate uint64, tooLow bool) {
}
return 0, false
}

// feeRate returns a fee rate for swaps (if wallet is a FeeRater).
func (w *xcWallet) feeRateSwap() (rate uint64, tooLow bool) {
if rater, is := w.Wallet.(asset.FeeRater); !is {
return 0, false
} else if r, tooLow := rater.FeeRateSwap(); r != 0 {
w.setFeeState(r)
return r, tooLow
}
return 0, false
}

0 comments on commit 00f0d2c

Please sign in to comment.