Skip to content

Commit b8b6d66

Browse files
committed
optimize settlement tx fee logic
1 parent 68d9bee commit b8b6d66

3 files changed

Lines changed: 76 additions & 11 deletions

File tree

x/liquidation/module/abci.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,15 +83,16 @@ func handleCompletedLiquidations(ctx sdk.Context, k keeper.Keeper) error {
8383
}
8484

8585
// get fee rate
86+
// NOTE: the fee rate validity is not necessary here
87+
// if it is 0 or too high, we use the reserved network fee duration liquidation, which is sufficient to relay the tx by design
8688
feeRate := k.BtcBridgeKeeper().GetFeeRate(ctx)
8789
if err := k.BtcBridgeKeeper().CheckFeeRate(ctx, feeRate); err != nil {
88-
k.Logger(ctx).Warn("Failed to get fee rate to handle liquidation", "err", err)
89-
return nil
90+
k.Logger(ctx).Warn("Failed to get valid fee rate to handle liquidation", "err", err)
9091
}
9192

9293
for _, liquidation := range liquidations {
9394
// build settlement tx
94-
settlementTx, txHash, sigHashes, changeAmount, err := types.BuildSettlementTransaction(liquidation, k.GetLiquidationRecords(ctx, liquidation.Id), k.ProtocolLiquidationFeeCollector(ctx), feeRate.Value)
95+
settlementTx, txHash, sigHashes, changeAmount, err := types.BuildSettlementTransaction(liquidation, k.GetLiquidationRecords(ctx, liquidation.Id), k.ProtocolLiquidationFeeCollector(ctx), feeRate.Value, types.LiquidationNetworkFeeReserve)
9596
if err != nil {
9697
k.Logger(ctx).Error("Failed to build settlement transaction", "liquidation id", liquidation.Id, "fee rate", feeRate.Value, "err", err)
9798
continue

x/liquidation/types/bitcoin.go

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package types
33
import (
44
"bytes"
55
"encoding/base64"
6+
"errors"
67

78
"github.com/btcsuite/btcd/btcutil"
89
"github.com/btcsuite/btcd/btcutil/psbt"
@@ -29,7 +30,8 @@ const (
2930
)
3031

3132
// BuildSettlementTransaction builds the settlement tx for the given liquidation and records
32-
func BuildSettlementTransaction(liquidation *Liquidation, records []*LiquidationRecord, protocolFeeCollector string, feeRate int64) (string, *chainhash.Hash, []string, int64, error) {
33+
// If the fee rate is 0 or too high, the reserved network fee will be used instead
34+
func BuildSettlementTransaction(liquidation *Liquidation, records []*LiquidationRecord, protocolFeeCollector string, feeRate int64, reservedNetworkFee int64) (string, *chainhash.Hash, []string, int64, error) {
3335
liquidationCet, err := psbt.NewFromRawBytes(bytes.NewReader([]byte(liquidation.LiquidationCet)), true)
3436
if err != nil {
3537
return "", nil, nil, 0, err
@@ -44,7 +46,7 @@ func BuildSettlementTransaction(liquidation *Liquidation, records []*Liquidation
4446
PubKeyScript: txOut.PkScript,
4547
}
4648

47-
settlementTxPsbt, changeAmount, err := BuildBatchTransferPsbt([]*btcbridgetypes.UTXO{utxo}, records, protocolFeeCollector, liquidation.ProtocolLiquidationFee.Amount.Int64(), feeRate, liquidation.Debtor)
49+
settlementTxPsbt, changeAmount, err := BuildBatchTransferPsbt([]*btcbridgetypes.UTXO{utxo}, records, protocolFeeCollector, liquidation.ProtocolLiquidationFee.Amount.Int64(), feeRate, reservedNetworkFee, liquidation.Debtor)
4850
if err != nil {
4951
return "", nil, nil, 0, err
5052
}
@@ -71,7 +73,7 @@ func BuildSettlementTransaction(liquidation *Liquidation, records []*Liquidation
7173
}
7274

7375
// BuildBatchTransferPsbt builds the psbt to perform batch transfer to liquidators, protocol fee collector and debtor(if remaining)
74-
func BuildBatchTransferPsbt(utxos []*btcbridgetypes.UTXO, records []*LiquidationRecord, protocolFeeCollector string, protocolFee int64, feeRate int64, change string) (*psbt.Packet, int64, error) {
76+
func BuildBatchTransferPsbt(utxos []*btcbridgetypes.UTXO, records []*LiquidationRecord, protocolFeeCollector string, protocolFee int64, feeRate int64, reservedNetworkFee int64, change string) (*psbt.Packet, int64, error) {
7577
chainCfg := bitcoin.Network
7678

7779
txOuts := make([]*wire.TxOut, 0)
@@ -115,9 +117,23 @@ func BuildBatchTransferPsbt(utxos []*btcbridgetypes.UTXO, records []*Liquidation
115117
return nil, 0, err
116118
}
117119

118-
unsignedTx, changeAmount, err := BuildUnsignedTransaction(utxos, txOuts, feeRate, changePkScript)
119-
if err != nil {
120-
return nil, 0, err
120+
var unsignedTx *wire.MsgTx
121+
var changeAmount int64
122+
123+
// try fee rate mode first
124+
if feeRate != 0 {
125+
unsignedTx, changeAmount, err = BuildUnsignedTransaction(utxos, txOuts, feeRate, changePkScript)
126+
if err != nil && !errors.Is(err, ErrInsufficientUTXOs) {
127+
return nil, 0, err
128+
}
129+
}
130+
131+
// try fee mode then
132+
if unsignedTx == nil {
133+
unsignedTx, changeAmount, err = BuildUnsignedTransactionWithFee(utxos, txOuts, reservedNetworkFee, changePkScript)
134+
if err != nil {
135+
return nil, 0, err
136+
}
121137
}
122138

123139
p, err := psbt.NewFromUnsignedTx(unsignedTx)
@@ -167,7 +183,7 @@ func BuildUnsignedTransaction(utxos []*btcbridgetypes.UTXO, txOuts []*wire.TxOut
167183
if changeAmount < 0 {
168184
feeWithoutChange := btcbridgetypes.GetTxVirtualSize(tx, utxos) * feeRate
169185
if inAmount-outAmount-feeWithoutChange < 0 {
170-
return nil, 0, errorsmod.Wrap(ErrFailedToBuildTx, "insufficient utxos")
186+
return nil, 0, ErrInsufficientUTXOs
171187
}
172188
}
173189

@@ -181,6 +197,53 @@ func BuildUnsignedTransaction(utxos []*btcbridgetypes.UTXO, txOuts []*wire.TxOut
181197
return tx, changeAmount, nil
182198
}
183199

200+
// BuildUnsignedTransactionWithFee is similar to BuildUnsignedTransaction, except that it uses the specified fee instead of the fee rate
201+
func BuildUnsignedTransactionWithFee(utxos []*btcbridgetypes.UTXO, txOuts []*wire.TxOut, fee int64, changePkScript []byte) (*wire.MsgTx, int64, error) {
202+
tx := wire.NewMsgTx(TxVersion)
203+
204+
inAmount := int64(0)
205+
outAmount := int64(0)
206+
207+
for _, utxo := range utxos {
208+
AddUTXOToTx(tx, utxo)
209+
inAmount += int64(utxo.Amount)
210+
}
211+
212+
for _, out := range txOuts {
213+
tx.AddTxOut(out)
214+
outAmount += out.Value
215+
}
216+
217+
tx.AddTxOut(wire.NewTxOut(0, changePkScript))
218+
219+
changeAmount := inAmount - outAmount - fee
220+
if changeAmount > 0 {
221+
tx.TxOut[len(tx.TxOut)-1].Value = changeAmount
222+
if btcbridgetypes.IsDustOut(tx.TxOut[len(tx.TxOut)-1]) {
223+
tx.TxOut = tx.TxOut[0 : len(tx.TxOut)-1]
224+
changeAmount = 0
225+
}
226+
} else {
227+
tx.TxOut = tx.TxOut[0 : len(tx.TxOut)-1]
228+
229+
if changeAmount < 0 {
230+
return nil, 0, ErrInsufficientUTXOs
231+
}
232+
233+
changeAmount = 0
234+
}
235+
236+
if fee < btcbridgetypes.GetTxVirtualSize(tx, utxos) {
237+
return nil, 0, errorsmod.Wrap(ErrFailedToBuildTx, "too low fee rate")
238+
}
239+
240+
if err := btcbridgetypes.CheckTransactionWeight(tx, utxos); err != nil {
241+
return nil, 0, err
242+
}
243+
244+
return tx, changeAmount, nil
245+
}
246+
184247
// AddUTXOToTx adds the given utxo to the specified tx
185248
// Make sure the utxo is valid
186249
func AddUTXOToTx(tx *wire.MsgTx, utxo *btcbridgetypes.UTXO) {

x/liquidation/types/errors.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@ var (
1515

1616
ErrInvalidParams = errorsmod.Register(ModuleName, 2100, "invalid params")
1717

18-
ErrFailedToBuildTx = errorsmod.Register(ModuleName, 3100, "failed to build transaction")
18+
ErrInsufficientUTXOs = errorsmod.Register(ModuleName, 3100, "insufficient utxos")
19+
ErrFailedToBuildTx = errorsmod.Register(ModuleName, 3101, "failed to build transaction")
1920
)

0 commit comments

Comments
 (0)