@@ -6,6 +6,7 @@ use crate::{QuoteRequest, SwapperError};
66
77pub static RESERVED_NATIVE_FEES : LazyLock < HashMap < Chain , & ' static str > > = LazyLock :: new ( || {
88 HashMap :: from ( [
9+ // Gas holdback for max-send source transactions.
910 ( Chain :: Near , "50000000000000000000000" ) , // 0.05 NEAR
1011 ( Chain :: Ethereum , "1000000000000000" ) , // 0.001 ETH
1112 ( Chain :: Arbitrum , "300000000000000" ) , // 0.0003 ARB ETH
@@ -20,15 +21,10 @@ pub static RESERVED_NATIVE_FEES: LazyLock<HashMap<Chain, &'static str>> = LazyLo
2021 ( Chain :: Solana , "20000" ) , // 0.00002 SOL
2122 ( Chain :: Ton , "20000000" ) , // 0.02 TON
2223 ( Chain :: Tron , "20000000" ) , // 20 TRX
23- ( Chain :: Bitcoin , "40000" ) , // 0.0004 BTC
24- ( Chain :: Zcash , "1000000" ) , // 0.01 ZEC
25- ( Chain :: Doge , "500000000" ) , // 5 DOGE
2624 ( Chain :: Xrp , "2000000" ) , // 2 XRP
2725 ( Chain :: Cardano , "2000000" ) , // 2 ADA
2826 ( Chain :: Aptos , "20000000" ) , // 0.2 APT
2927 ( Chain :: Stellar , "100000" ) , // 0.01 XLM
30- ( Chain :: Litecoin , "100000" ) , // 0.001 LTC
31- ( Chain :: BitcoinCash , "100000" ) , // 0.001 BCH
3228 ( Chain :: Monad , "5000000000000000" ) , // 0.005 MON
3329 ( Chain :: XLayer , "5000000000000000" ) , // 0.005 OKB
3430 ( Chain :: Plasma , "5000000000000000" ) , // 0.005 XPL
@@ -38,6 +34,12 @@ pub static RESERVED_NATIVE_FEES: LazyLock<HashMap<Chain, &'static str>> = LazyLo
3834 ( Chain :: Injective , "1300000000000000" ) , // 0.0013 INJ
3935 ( Chain :: Sei , "1300000" ) , // 1.3 SEI
4036 ( Chain :: Noble , "25000" ) , // 0.025 USDC
37+ // UTXO fee-vs-slippage buffer for amount-sensitive max swaps.
38+ ( Chain :: Bitcoin , "15000" ) , // ~300 vB * ~50 sat/vB peak
39+ ( Chain :: Litecoin , "15000" ) , // ~300 vB * ~50 lit/vB peak
40+ ( Chain :: BitcoinCash , "10000" ) , // ~300 B * ~30 sat/B peak
41+ ( Chain :: Doge , "10000000" ) , // 0.1 DOGE
42+ ( Chain :: Zcash , "30000" ) , // 3x ZIP-317 marginal fee
4143 ] )
4244} ) ;
4345
@@ -65,3 +67,43 @@ pub fn quote_value_after_reserve_by_chain(request: &QuoteRequest) -> Result<Stri
6567 } ;
6668 quote_value_after_reserve ( request, reserved)
6769}
70+
71+ #[ cfg( test) ]
72+ mod tests {
73+ use primitives:: AssetId ;
74+
75+ use super :: * ;
76+ use crate :: { Options , SwapperQuoteAsset } ;
77+
78+ #[ test]
79+ fn btc_family_chains_have_buffer_reserve ( ) {
80+ assert_eq ! ( reserved_tx_fees( Chain :: Bitcoin ) , Some ( "15000" ) ) ;
81+ assert_eq ! ( reserved_tx_fees( Chain :: Litecoin ) , Some ( "15000" ) ) ;
82+ assert_eq ! ( reserved_tx_fees( Chain :: BitcoinCash ) , Some ( "10000" ) ) ;
83+ assert_eq ! ( reserved_tx_fees( Chain :: Doge ) , Some ( "10000000" ) ) ;
84+ assert_eq ! ( reserved_tx_fees( Chain :: Zcash ) , Some ( "30000" ) ) ;
85+ }
86+
87+ #[ test]
88+ fn btc_max_quote_applies_reserve ( ) {
89+ let request = QuoteRequest {
90+ from_asset : SwapperQuoteAsset :: from ( AssetId :: from_chain ( Chain :: Bitcoin ) ) ,
91+ to_asset : SwapperQuoteAsset :: from ( AssetId :: from_chain ( Chain :: Solana ) ) ,
92+ wallet_address : "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4" . into ( ) ,
93+ destination_address : "11111111111111111111111111111111" . into ( ) ,
94+ value : "89100" . to_string ( ) ,
95+ options : Options {
96+ use_max_amount : true ,
97+ ..Default :: default ( )
98+ } ,
99+ } ;
100+
101+ assert_eq ! ( quote_value_after_reserve_by_chain( & request) . unwrap( ) , "74100" ) ;
102+ }
103+
104+ #[ test]
105+ fn btc_reserve_exceeds_typical_planner_fee ( ) {
106+ let reserve: u64 = reserved_tx_fees ( Chain :: Bitcoin ) . unwrap ( ) . parse ( ) . unwrap ( ) ;
107+ assert ! ( reserve >= 12_000 , "Bitcoin reserve {reserve} too small to absorb peak-fee planner cost" ) ;
108+ }
109+ }
0 commit comments