@@ -3,6 +3,7 @@ package types
33import (
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
186249func AddUTXOToTx (tx * wire.MsgTx , utxo * btcbridgetypes.UTXO ) {
0 commit comments