@@ -1969,8 +1969,13 @@ func (w *assetWallet) PreRedeem(req *asset.PreRedeemForm) (*asset.PreRedeem, err
19691969 return nil , err
19701970 }
19711971
1972- bestCase := nRedeem * req .FeeSuggestion
1973- worstCase := oneRedeem * req .Lots * req .FeeSuggestion
1972+ feeRate , _ , _ , err := w .feeRate (w .ctx , req .FeeSuggestion , true )
1973+ if err != nil {
1974+ return nil , fmt .Errorf ("unable to get fee rate: %v" , err )
1975+ }
1976+
1977+ bestCase := nRedeem * feeRate
1978+ worstCase := oneRedeem * req .Lots * feeRate
19741979 userOpRequired := false
19751980
19761981 w .bundlerMtx .RLock ()
@@ -2000,7 +2005,11 @@ func (w *assetWallet) SingleLotRedeemFees(assetVer uint32, feeSuggestion uint64)
20002005 if g == nil {
20012006 return 0 , fmt .Errorf ("no gases known for %d, constract version %d" , w .assetID , contractVersion (assetVer ))
20022007 }
2003- return g .Redeem * feeSuggestion , nil
2008+ feeRate , _ , _ , err := w .feeRate (w .ctx , feeSuggestion , true )
2009+ if err != nil {
2010+ return 0 , fmt .Errorf ("unable to get fee rate: %v" , err )
2011+ }
2012+ return g .Redeem * feeRate , nil
20042013}
20052014
20062015// coin implements the asset.Coin interface for ETH
@@ -2069,29 +2078,28 @@ func (w *ETHWallet) FundOrder(ord *asset.Order) (asset.Coins, []dex.Bytes, uint6
20692078 dex .BipIDSymbol (w .assetID ), ord .MaxFeeRate , dexeth .MinGasTipCap )
20702079 }
20712080
2072- if w .gasFeeLimit () < ord .MaxFeeRate {
2073- return nil , nil , 0 , fmt .Errorf (
2074- "%v: server's max fee rate %v higher than configured fee rate limit %v" ,
2075- dex .BipIDSymbol (w .assetID ), ord .MaxFeeRate , w .gasFeeLimit ())
2081+ feeRate , _ , _ , err := w .feeRate (w .ctx , ord .MaxFeeRate , true )
2082+ if err != nil {
2083+ return nil , nil , 0 , fmt .Errorf ("unable to get fee rate: %v" , err )
20762084 }
20772085
20782086 contractVer := contractVersion (ord .AssetVersion )
20792087
20802088 g , err := w .initGasEstimate (int (ord .MaxSwapCount ), contractVer ,
2081- ord .RedeemVersion , ord .RedeemAssetID , ord . MaxFeeRate )
2089+ ord .RedeemVersion , ord .RedeemAssetID , feeRate )
20822090 if err != nil {
20832091 return nil , nil , 0 , fmt .Errorf ("error estimating swap gas: %v" , err )
20842092 }
20852093
2086- ethToLock := ord . MaxFeeRate * g .Swap * ord .MaxSwapCount + ord .Value
2094+ ethToLock := feeRate * g .Swap * ord .MaxSwapCount + ord .Value
20872095 // Note: In a future refactor, we could lock the redemption funds here too
20882096 // and signal to the user so that they don't call `RedeemN`. This has the
20892097 // same net effect, but avoids a lockFunds -> unlockFunds for us and likely
20902098 // some work for the caller as well. We can't just always do it that way and
20912099 // remove RedeemN, since we can't guarantee that the redemption asset is in
20922100 // our fee-family. though it could still be an AccountRedeemer.
20932101 w .log .Debugf ("Locking %s to swap %s in up to %d swaps at a fee rate of %d gwei/gas using up to %d gas per swap" ,
2094- w .amtString (ethToLock ), w .amtString (ord .Value ), ord .MaxSwapCount , ord . MaxFeeRate , g .Swap )
2102+ w .amtString (ethToLock ), w .amtString (ord .Value ), ord .MaxSwapCount , feeRate , g .Swap )
20952103
20962104 coin := w .createFundingCoin (ethToLock )
20972105
@@ -2109,10 +2117,9 @@ func (w *TokenWallet) FundOrder(ord *asset.Order) (asset.Coins, []dex.Bytes, uin
21092117 dex .BipIDSymbol (w .assetID ), ord .MaxFeeRate , dexeth .MinGasTipCap )
21102118 }
21112119
2112- if w .gasFeeLimit () < ord .MaxFeeRate {
2113- return nil , nil , 0 , fmt .Errorf (
2114- "%v: server's max fee rate %v higher than configured fee rate limit %v" ,
2115- dex .BipIDSymbol (w .assetID ), ord .MaxFeeRate , w .gasFeeLimit ())
2120+ feeRate , _ , _ , err := w .feeRate (w .ctx , ord .MaxFeeRate , true )
2121+ if err != nil {
2122+ return nil , nil , 0 , fmt .Errorf ("unable to get fee rate: %v" , err )
21162123 }
21172124
21182125 approvalStatus , err := w .swapContractApprovalStatus (ord .AssetVersion )
@@ -2130,12 +2137,12 @@ func (w *TokenWallet) FundOrder(ord *asset.Order) (asset.Coins, []dex.Bytes, uin
21302137 }
21312138
21322139 g , err := w .initGasEstimate (int (ord .MaxSwapCount ), contractVersion (ord .AssetVersion ),
2133- ord .RedeemVersion , ord .RedeemAssetID , ord . MaxFeeRate )
2140+ ord .RedeemVersion , ord .RedeemAssetID , feeRate )
21342141 if err != nil {
21352142 return nil , nil , 0 , fmt .Errorf ("error estimating swap gas: %v" , err )
21362143 }
21372144
2138- ethToLock := ord . MaxFeeRate * g .Swap * ord .MaxSwapCount
2145+ ethToLock := feeRate * g .Swap * ord .MaxSwapCount
21392146 var success bool
21402147 if err = w .lockFunds (ord .Value , initiationReserve ); err != nil {
21412148 return nil , nil , 0 , fmt .Errorf ("error locking token funds: %v" , err )
@@ -2147,7 +2154,7 @@ func (w *TokenWallet) FundOrder(ord *asset.Order) (asset.Coins, []dex.Bytes, uin
21472154 }()
21482155
21492156 w .log .Debugf ("Locking %s to swap %s in up to %d swaps at a fee rate of %d gwei/gas using up to %d gas per swap" ,
2150- w .parent .amtString (ethToLock ), w .amtString (ord .Value ), ord .MaxSwapCount , ord . MaxFeeRate , g .Swap )
2157+ w .parent .amtString (ethToLock ), w .amtString (ord .Value ), ord .MaxSwapCount , feeRate , g .Swap )
21512158 if err := w .parent .lockFunds (ethToLock , initiationReserve ); err != nil {
21522159 return nil , nil , 0 , err
21532160 }
@@ -2161,21 +2168,20 @@ func (w *TokenWallet) FundOrder(ord *asset.Order) (asset.Coins, []dex.Bytes, uin
21612168// FundMultiOrder funds multiple orders in one shot. No special handling is
21622169// required for ETH as ETH does not over-lock during funding.
21632170func (w * ETHWallet ) FundMultiOrder (ord * asset.MultiOrder , maxLock uint64 ) ([]asset.Coins , [][]dex.Bytes , uint64 , error ) {
2164- if w .gasFeeLimit () < ord .MaxFeeRate {
2165- return nil , nil , 0 , fmt .Errorf (
2166- "%v: server's max fee rate %v higher than configured fee rate limit %v" ,
2167- dex .BipIDSymbol (w .assetID ), ord .MaxFeeRate , w .gasFeeLimit ())
2171+ feeRate , _ , _ , err := w .feeRate (w .ctx , ord .MaxFeeRate , true )
2172+ if err != nil {
2173+ return nil , nil , 0 , fmt .Errorf ("unable to get fee rate: %v" , err )
21682174 }
21692175
2170- g , err := w .initGasEstimate (1 , ord .AssetVersion , ord .RedeemVersion , ord .RedeemAssetID , ord . MaxFeeRate )
2176+ g , err := w .initGasEstimate (1 , ord .AssetVersion , ord .RedeemVersion , ord .RedeemAssetID , feeRate )
21712177 if err != nil {
21722178 return nil , nil , 0 , fmt .Errorf ("error estimating swap gas: %v" , err )
21732179 }
21742180
21752181 var totalToLock uint64
21762182 allCoins := make ([]asset.Coins , 0 , len (ord .Values ))
21772183 for _ , value := range ord .Values {
2178- toLock := ord . MaxFeeRate * g .Swap * value .MaxSwapCount + value .Value
2184+ toLock := feeRate * g .Swap * value .MaxSwapCount + value .Value
21792185 allCoins = append (allCoins , asset.Coins {w .createFundingCoin (toLock )})
21802186 totalToLock += toLock
21812187 }
@@ -2199,10 +2205,9 @@ func (w *ETHWallet) FundMultiOrder(ord *asset.MultiOrder, maxLock uint64) ([]ass
21992205// FundMultiOrder funds multiple orders in one shot. No special handling is
22002206// required for ETH as ETH does not over-lock during funding.
22012207func (w * TokenWallet ) FundMultiOrder (ord * asset.MultiOrder , maxLock uint64 ) ([]asset.Coins , [][]dex.Bytes , uint64 , error ) {
2202- if w .gasFeeLimit () < ord .MaxFeeRate {
2203- return nil , nil , 0 , fmt .Errorf (
2204- "%v: server's max fee rate %v higher than configured fee rate limit %v" ,
2205- dex .BipIDSymbol (w .assetID ), ord .MaxFeeRate , w .gasFeeLimit ())
2208+ feeRate , _ , _ , err := w .feeRate (w .ctx , ord .MaxFeeRate , true )
2209+ if err != nil {
2210+ return nil , nil , 0 , fmt .Errorf ("unable to get fee rate: %v" , err )
22062211 }
22072212
22082213 approvalStatus , err := w .swapContractApprovalStatus (ord .AssetVersion )
@@ -2220,15 +2225,15 @@ func (w *TokenWallet) FundMultiOrder(ord *asset.MultiOrder, maxLock uint64) ([]a
22202225 }
22212226
22222227 g , err := w .initGasEstimate (1 , ord .AssetVersion ,
2223- ord .RedeemVersion , ord .RedeemAssetID , ord . MaxFeeRate )
2228+ ord .RedeemVersion , ord .RedeemAssetID , feeRate )
22242229 if err != nil {
22252230 return nil , nil , 0 , fmt .Errorf ("error estimating swap gas: %v" , err )
22262231 }
22272232
22282233 var totalETHToLock , totalTokenToLock uint64
22292234 allCoins := make ([]asset.Coins , 0 , len (ord .Values ))
22302235 for _ , value := range ord .Values {
2231- ethToLock := ord . MaxFeeRate * g .Swap * value .MaxSwapCount
2236+ ethToLock := feeRate * g .Swap * value .MaxSwapCount
22322237 tokenToLock := value .Value
22332238 allCoins = append (allCoins , asset.Coins {w .createTokenFundingCoin (tokenToLock , ethToLock )})
22342239 totalETHToLock += ethToLock
@@ -2561,8 +2566,9 @@ var _ asset.Receipt = (*swapReceipt)(nil)
25612566// max fees that will possibly be used, since in ethereum with EIP-1559 we cannot
25622567// know exactly how much fees will be used.
25632568func (w * ETHWallet ) Swap (swaps * asset.Swaps ) ([]asset.Receipt , asset.Coin , uint64 , error ) {
2564- if swaps .FeeRate == 0 {
2565- return nil , nil , 0 , fmt .Errorf ("cannot send swap with with zero fee rate" )
2569+ feeRate , maxFeeRate , tipRate , err := w .feeRate (w .ctx , swaps .FeeRate , false )
2570+ if err != nil {
2571+ return nil , nil , 0 , fmt .Errorf ("unable to get fee rate: %v" , err )
25662572 }
25672573
25682574 fail := func (s string , a ... any ) ([]asset.Receipt , asset.Coin , uint64 , error ) {
@@ -2585,20 +2591,20 @@ func (w *ETHWallet) Swap(swaps *asset.Swaps) ([]asset.Receipt, asset.Coin, uint6
25852591
25862592 contractVer := contractVersion (swaps .AssetVersion )
25872593 n := len (swaps .Contracts )
2588- oneSwap , nSwap , err := w .swapGas (n , contractVer , swaps . FeeRate )
2594+ oneSwap , nSwap , err := w .swapGas (n , contractVer , feeRate )
25892595 if err != nil {
25902596 return fail ("error getting gas fees: %v" , err )
25912597 }
25922598 gasLimit := oneSwap * uint64 (n ) // naive unbatched, higher but not realistic
2593- fees := gasLimit * swaps . FeeRate
2599+ fees := gasLimit * feeRate
25942600 if swapVal + fees > reservedVal {
25952601 if n == 1 {
25962602 return fail ("unfunded swap: %d < %d" , reservedVal , swapVal + fees )
25972603 }
25982604 w .log .Warnf ("Unexpectedly low reserves for %d swaps: %d < %d" , n , reservedVal , swapVal + fees )
25992605 // Since this is a batch swap, attempt to use the realistic limits.
26002606 gasLimit = nSwap
2601- fees = gasLimit * swaps . FeeRate
2607+ fees = gasLimit * feeRate
26022608 if swapVal + fees > reservedVal {
26032609 // If the live gas estimate is giving us an unrealistically high
26042610 // value, we're in trouble, so we might consider a third fallback
@@ -2611,13 +2617,8 @@ func (w *ETHWallet) Swap(swaps *asset.Swaps) ([]asset.Receipt, asset.Coin, uint6
26112617 }
26122618 }
26132619
2614- maxFeeRate := dexeth .GweiToWei (swaps .FeeRate )
2615- _ , tipRate , err := w .currentNetworkFees (w .ctx )
2616- if err != nil {
2617- return fail ("Swap: failed to get network tip cap: %w" , err )
2618- }
2619-
2620- tx , err := w .initiate (w .ctx , w .assetID , swaps .Contracts , gasLimit , maxFeeRate , tipRate , contractVer )
2620+ maxFeeRateBig := dexeth .GweiToWei (maxFeeRate )
2621+ tx , err := w .initiate (w .ctx , w .assetID , swaps .Contracts , gasLimit , maxFeeRateBig , tipRate , contractVer )
26212622 if err != nil {
26222623 return fail ("Swap: initiate error: %w" , err )
26232624 }
@@ -2670,8 +2671,9 @@ func acToLocator(contractVer uint32, swap *asset.Contract, evmValue *big.Int, fr
26702671// max fees that will possibly be used, since in ethereum with EIP-1559 we cannot
26712672// know exactly how much fees will be used.
26722673func (w * TokenWallet ) Swap (swaps * asset.Swaps ) ([]asset.Receipt , asset.Coin , uint64 , error ) {
2673- if swaps .FeeRate == 0 {
2674- return nil , nil , 0 , fmt .Errorf ("cannot send swap with with zero fee rate" )
2674+ feeRate , maxFeeRate , tipRate , err := w .feeRate (w .ctx , swaps .FeeRate , false )
2675+ if err != nil {
2676+ return nil , nil , 0 , fmt .Errorf ("unable to get fee rate: %v" , err )
26752677 }
26762678
26772679 fail := func (s string , a ... any ) ([]asset.Receipt , asset.Coin , uint64 , error ) {
@@ -2699,37 +2701,32 @@ func (w *TokenWallet) Swap(swaps *asset.Swaps) ([]asset.Receipt, asset.Coin, uin
26992701
27002702 n := len (swaps .Contracts )
27012703 contractVer := contractVersion (swaps .AssetVersion )
2702- oneSwap , nSwap , err := w .swapGas (n , contractVer , swaps . FeeRate )
2704+ oneSwap , nSwap , err := w .swapGas (n , contractVer , feeRate )
27032705 if err != nil {
27042706 return fail ("error getting gas fees: %v" , err )
27052707 }
27062708
27072709 gasLimit := oneSwap * uint64 (n )
2708- fees := gasLimit * swaps . FeeRate
2710+ fees := gasLimit * feeRate
27092711 if fees > reservedParent {
27102712 if n == 1 {
27112713 return fail ("unfunded token swap fees: %d < %d" , reservedParent , fees )
27122714 }
27132715 // Since this is a batch swap, attempt to use the realistic limits.
27142716 w .log .Warnf ("Unexpectedly low reserves for %d swaps: %d < %d" , n , reservedVal , swapVal + fees )
27152717 gasLimit = nSwap
2716- fees = gasLimit * swaps . FeeRate
2718+ fees = gasLimit * feeRate
27172719 if fees > reservedParent {
27182720 return fail ("unfunded token swap fees: %d < %d" , reservedParent , fees )
27192721 } // See (*ETHWallet).Swap comments for a third option.
27202722 }
27212723
2722- maxFeeRate := dexeth .GweiToWei (swaps .FeeRate )
2723- _ , tipRate , err := w .currentNetworkFees (w .ctx )
2724- if err != nil {
2725- return fail ("Swap: failed to get network tip cap: %w" , err )
2726- }
2727-
2724+ maxFeeRateBig := dexeth .GweiToWei (maxFeeRate )
27282725 if w .netToken .SwapContracts [swaps .AssetVersion ] == nil {
27292726 return fail ("unable to find contract address for asset %d contract version %d" , w .assetID , swaps .AssetVersion )
27302727 }
27312728
2732- tx , err := w .initiate (w .ctx , w .assetID , swaps .Contracts , gasLimit , maxFeeRate , tipRate , contractVer )
2729+ tx , err := w .initiate (w .ctx , w .assetID , swaps .Contracts , gasLimit , maxFeeRateBig , tipRate , contractVer )
27332730 if err != nil {
27342731 return fail ("Swap: initiate error: %w" , err )
27352732 }
@@ -3088,17 +3085,6 @@ func (w *assetWallet) Redeem(form *asset.RedeemForm, feeWallet *assetWallet, non
30883085 return fail (fmt .Errorf ("no gas table" ))
30893086 }
30903087
3091- if feeWallet == nil {
3092- feeWallet = w
3093- }
3094- bal , err := feeWallet .Balance ()
3095- if err != nil {
3096- return fail (fmt .Errorf ("error getting balance in excessive gas fee recovery: %v" , err ))
3097- }
3098-
3099- gasLimit , gasFeeCap := g .Redeem * n , form .FeeSuggestion
3100- originalFundsReserved := gasLimit * gasFeeCap
3101-
31023088 /* We could get a gas estimate via RPC, but this will reveal the secret key
31033089 before submitting the redeem transaction. This is not OK for maker.
31043090 Disable for now.
@@ -3125,25 +3111,12 @@ func (w *assetWallet) Redeem(form *asset.RedeemForm, feeWallet *assetWallet, non
31253111 }
31263112 */
31273113
3128- // If the base fee is higher than the FeeSuggestion we attempt to increase
3129- // the gasFeeCap to 2*baseFee. If we don't have enough funds, we use the
3130- // funds we have available.
3131- baseFee , tipRate , err := w .currentNetworkFees (w .ctx )
3114+ feeRate , maxFeeRate , tipRate , err := w .feeRate (w .ctx , form .FeeSuggestion , false )
31323115 if err != nil {
3133- return fail (fmt .Errorf ("Error getting net fee state: %w" , err ))
3134- }
3135- baseFeeGwei := dexeth .WeiToGweiCeil (baseFee )
3136- if baseFeeGwei > form .FeeSuggestion {
3137- additionalFundsNeeded := (2 * baseFeeGwei * gasLimit ) - originalFundsReserved
3138- if bal .Available > additionalFundsNeeded {
3139- gasFeeCap = 2 * baseFeeGwei
3140- } else {
3141- gasFeeCap = (bal .Available + originalFundsReserved ) / gasLimit
3142- }
3143- w .log .Warnf ("base fee %d > server max fee rate %d. using %d as gas fee cap for redemption" , baseFeeGwei , form .FeeSuggestion , gasFeeCap )
3116+ return fail (fmt .Errorf ("unable to get fee rate: %v" , err ))
31443117 }
31453118
3146- tx , err := w .redeem (w .ctx , form .Redemptions , gasFeeCap , tipRate , gasLimit , contractVer )
3119+ tx , err := w .redeem (w .ctx , form .Redemptions , maxFeeRate , tipRate , g . Redeem * n , contractVer )
31473120 if err != nil {
31483121 return fail (fmt .Errorf ("Redeem: redeem error: %w" , err ))
31493122 }
@@ -3162,7 +3135,7 @@ func (w *assetWallet) Redeem(form *asset.RedeemForm, feeWallet *assetWallet, non
31623135
31633136 // This is still a fee estimate. The actual gas cost will be returned in the
31643137 // receipt.
3165- fees := g .RedeemN (len (form .Redemptions )) * form . FeeSuggestion
3138+ fees := g .RedeemN (len (form .Redemptions )) * feeRate
31663139 return txs , outputCoin , fees , nil
31673140}
31683141
@@ -4498,13 +4471,17 @@ func (w *assetWallet) Refund(_, contract dex.Bytes, feeRate uint64) (dex.Bytes,
44984471 return nil , fmt .Errorf ("Refund: swap with locator %x is not refundable" , locator )
44994472 }
45004473
4501- maxFeeRate := dexeth .GweiToWei (feeRate )
4474+ maxFeeRate := w .gasFeeLimit ()
4475+ if feeRate > maxFeeRate {
4476+ w .log .Warnf ("Refund fee rate higher than max fee. %d > %d" , feeRate , maxFeeRate )
4477+ }
4478+ maxFeeRateBig := dexeth .GweiToWei (maxFeeRate )
45024479 _ , tipRate , err := w .currentNetworkFees (w .ctx )
45034480 if err != nil {
45044481 return nil , fmt .Errorf ("Refund: failed to get network tip cap: %w" , err )
45054482 }
45064483
4507- tx , err := w .refund (locator , w .atomize (vector .Value ), maxFeeRate , tipRate , contractVer )
4484+ tx , err := w .refund (locator , w .atomize (vector .Value ), maxFeeRateBig , tipRate , contractVer )
45084485 if err != nil {
45094486 return nil , fmt .Errorf ("Refund: failed to call refund: %w" , err )
45104487 }
@@ -5123,6 +5100,32 @@ func (w *baseWallet) currentBaseFee(ctx context.Context) (*big.Int, error) {
51235100 return base , nil
51245101}
51255102
5103+ // feeRate returns the higher of suggested fee rate and the current on chain fee
5104+ // rate and caps with the maxFeeRate.
5105+ func (w * baseWallet ) feeRate (ctx context.Context , suggestedFeeRate uint64 , addBuffer bool ) (feeRate , maxFeeRate uint64 , tipRate * big.Int , err error ) {
5106+ maxFeeRate = w .gasFeeLimit ()
5107+ if maxFeeRate < suggestedFeeRate {
5108+ return 0 , 0 , nil , fmt .Errorf (
5109+ "server's max fee rate %v higher than configured fee rate limit %v" ,
5110+ suggestedFeeRate , maxFeeRate )
5111+ }
5112+ var estimate uint64
5113+ liveRateBig , tipRate , err := w .recommendedMaxFeeRate (ctx )
5114+ if err != nil {
5115+ return 0 , 0 , nil , fmt .Errorf ("unable to get recommended fee rate: %v" , err )
5116+ }
5117+ estimate , err = dexeth .WeiToGweiSafe (liveRateBig )
5118+ if err != nil {
5119+ return 0 , 0 , nil , fmt .Errorf ("unable to convert to gwei: %v" , err )
5120+ }
5121+ estimate = max (estimate , suggestedFeeRate )
5122+ // Add a %25 buffer for initial fund locking.
5123+ if addBuffer {
5124+ estimate = estimate * 5 / 4
5125+ }
5126+ return min (estimate , maxFeeRate ), maxFeeRate , tipRate , nil
5127+ }
5128+
51265129// currentNetworkFees give the current base fee rate (from the best header),
51275130// and recommended tip cap.
51285131func (w * baseWallet ) currentNetworkFees (ctx context.Context ) (baseRate , tipRate * big.Int , err error ) {
0 commit comments