Skip to content
This repository was archived by the owner on Jun 1, 2026. It is now read-only.

Commit 7489c6c

Browse files
committed
Restore small BTC-family swap reserves
1 parent a572f75 commit 7489c6c

4 files changed

Lines changed: 76 additions & 11 deletions

File tree

‎crates/swapper/src/chainflip/provider.rs‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ where
349349
#[cfg(test)]
350350
mod tests {
351351
use super::*;
352-
use crate::SwapperQuoteAsset;
352+
use crate::{Options, SwapperQuoteAsset};
353353
use primitives::AssetId;
354354

355355
#[test]
@@ -378,13 +378,18 @@ mod tests {
378378
let request = QuoteRequest {
379379
from_asset: SwapperQuoteAsset::from(AssetId::from_chain(Chain::Bitcoin)),
380380
to_asset: SwapperQuoteAsset::from(AssetId::from_chain(Chain::Ethereum)),
381+
value: "89100".to_string(),
382+
options: Options {
383+
use_max_amount: true,
384+
..Default::default()
385+
},
381386
..QuoteRequest::mock(Chain::Bitcoin, None)
382387
};
383388

384389
let quote_request = build_quote_request(&request).unwrap();
385390

386-
assert_eq!(quote_request.from_value, "1000000");
387-
assert_eq!(quote_request.quote_request.amount, "1000000");
391+
assert_eq!(quote_request.from_value, "74100");
392+
assert_eq!(quote_request.quote_request.amount, "74100");
388393
assert_eq!(quote_request.quote_request.src_chain, "Bitcoin");
389394
assert_eq!(quote_request.quote_request.src_asset, "BTC");
390395
assert_eq!(quote_request.quote_request.dest_chain, "Ethereum");

‎crates/swapper/src/fees/reserve.rs‎

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::{QuoteRequest, SwapperError};
66

77
pub 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+
}

‎crates/swapper/src/near_intents/provider.rs‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -395,14 +395,14 @@ mod tests {
395395

396396
#[test]
397397
fn max_quote_keeps_transfer_amount() {
398-
let mut request = QuoteRequest::mock(Chain::Tron, None);
398+
let mut request = QuoteRequest::mock(Chain::Bitcoin, None);
399399
request.to_asset = SwapperQuoteAsset::from(AssetId::from_chain(Chain::Near));
400-
request.value = "37000000".to_string();
400+
request.value = "89100".to_string();
401401
request.options.use_max_amount = true;
402402

403403
let quote_request = NearIntents::<RpcClient>::build_quote_request(&request, SwapType::FlexInput, true).unwrap();
404404

405-
assert_eq!(quote_request.amount, "37000000");
405+
assert_eq!(quote_request.amount, "89100");
406406
}
407407

408408
#[test]

‎crates/swapper/src/thorchain/provider.rs‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,24 @@ mod tests {
243243
assert!(has_zcash);
244244
}
245245

246+
#[test]
247+
fn test_quote_input_value_bitcoin_max_passes_value_through() {
248+
let from_asset = THORChainAsset::from_asset_id(Chain::Bitcoin.as_ref()).unwrap();
249+
let request = QuoteRequest {
250+
from_asset: SwapperQuoteAsset::from(Chain::Bitcoin.as_asset_id()),
251+
to_asset: SwapperQuoteAsset::from(Chain::Solana.as_asset_id()),
252+
wallet_address: "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4".to_string(),
253+
destination_address: "11111111111111111111111111111111".to_string(),
254+
value: "89100".to_string(),
255+
options: Options {
256+
use_max_amount: true,
257+
..Default::default()
258+
},
259+
};
260+
261+
assert_eq!(quote_input_value(&from_asset, &request).unwrap(), "89100");
262+
}
263+
246264
#[tokio::test]
247265
async fn test_get_quote_data_uses_quote_from_value() {
248266
let provider = Arc::new(ProviderMock::new(String::new()));

0 commit comments

Comments
 (0)