diff --git a/coordinator/example-settings/prod-coordinator-settings.toml b/coordinator/example-settings/prod-coordinator-settings.toml index 5aa58cd49..b6d7ae413 100644 --- a/coordinator/example-settings/prod-coordinator-settings.toml +++ b/coordinator/example-settings/prod-coordinator-settings.toml @@ -12,6 +12,7 @@ min_quantity = 1 maintenance_margin_rate = 0.1 order_matching_fee_rate = 0.003 index_price_source = "Bitmex" +max_leverage = 5 [xxi] off_chain_sync_interval = 5 diff --git a/coordinator/example-settings/test-coordinator-settings.toml b/coordinator/example-settings/test-coordinator-settings.toml index 8729d607f..7793e5bc6 100644 --- a/coordinator/example-settings/test-coordinator-settings.toml +++ b/coordinator/example-settings/test-coordinator-settings.toml @@ -13,6 +13,7 @@ min_quantity = 1 maintenance_margin_rate = 0.1 order_matching_fee_rate = 0.003 index_price_source = "Test" +max_leverage = 5 [xxi] off_chain_sync_interval = 5 diff --git a/coordinator/src/orderbook/websocket.rs b/coordinator/src/orderbook/websocket.rs index bbdcbb58a..fee22e7c4 100644 --- a/coordinator/src/orderbook/websocket.rs +++ b/coordinator/src/orderbook/websocket.rs @@ -218,12 +218,18 @@ pub async fn websocket_connection(stream: WebSocket, state: Arc) { let liquidity_options = db::liquidity_options::get_all(&mut conn).unwrap_or_default(); - let (min_quantity, maintenance_margin_rate, order_matching_fee_rate) = { + let ( + min_quantity, + maintenance_margin_rate, + order_matching_fee_rate, + max_leverage, + ) = { let settings = state.settings.read().await; ( settings.min_quantity, settings.maintenance_margin_rate, settings.order_matching_fee_rate, + settings.max_leverage, ) }; @@ -239,6 +245,7 @@ pub async fn websocket_connection(stream: WebSocket, state: Arc) { maintenance_margin_rate, order_matching_fee_rate, referral_status, + max_leverage, })) .await { diff --git a/coordinator/src/settings.rs b/coordinator/src/settings.rs index e22ee9ae9..664a71bbb 100644 --- a/coordinator/src/settings.rs +++ b/coordinator/src/settings.rs @@ -102,6 +102,9 @@ pub struct Settings { /// Where to get the index price from. This value is used to calculate funding fees. pub index_price_source: IndexPriceSource, + + /// The max leverage a trader can take + pub max_leverage: u8, } impl Settings { @@ -162,6 +165,7 @@ impl Settings { maintenance_margin_rate: file.maintenance_margin_rate, order_matching_fee_rate: file.order_matching_fee_rate, index_price_source: file.index_price_source, + max_leverage: file.max_leverage, } } } @@ -190,6 +194,8 @@ pub struct SettingsFile { order_matching_fee_rate: f32, index_price_source: IndexPriceSource, + + max_leverage: u8, } impl From for SettingsFile { @@ -210,6 +216,7 @@ impl From for SettingsFile { maintenance_margin_rate: value.maintenance_margin_rate, order_matching_fee_rate: value.order_matching_fee_rate, index_price_source: value.index_price_source, + max_leverage: value.max_leverage, } } } @@ -246,6 +253,7 @@ mod tests { maintenance_margin_rate: 0.1, order_matching_fee_rate: 0.003, index_price_source: IndexPriceSource::Bitmex, + max_leverage: 5, }; let serialized = toml::to_string_pretty(&original).unwrap(); diff --git a/crates/xxi-node/src/commons/message.rs b/crates/xxi-node/src/commons/message.rs index 0f2456c7a..461143372 100644 --- a/crates/xxi-node/src/commons/message.rs +++ b/crates/xxi-node/src/commons/message.rs @@ -80,6 +80,7 @@ pub struct TenTenOneConfig { pub maintenance_margin_rate: f32, pub order_matching_fee_rate: f32, pub referral_status: ReferralStatus, + pub max_leverage: u8, } #[derive(Serialize, Clone, Deserialize, Debug)] diff --git a/mobile/lib/common/application/tentenone_config_change_notifier.dart b/mobile/lib/common/application/tentenone_config_change_notifier.dart index 6724a6c0f..7c263fc5c 100644 --- a/mobile/lib/common/application/tentenone_config_change_notifier.dart +++ b/mobile/lib/common/application/tentenone_config_change_notifier.dart @@ -10,6 +10,8 @@ class TenTenOneConfigChangeNotifier extends ChangeNotifier implements Subscriber List _liquidityOptions = []; ReferralStatus? _referralStatus; + // will be overwritten once we receive the authenticated event + int _maxLeverage = 5; TenTenOneConfigChangeNotifier(this.channelInfoService); @@ -19,6 +21,8 @@ class TenTenOneConfigChangeNotifier extends ChangeNotifier implements Subscriber ReferralStatus? get referralStatus => _referralStatus; + double get maxLeverage => _maxLeverage.toDouble(); + @override void notify(bridge.Event event) { if (event is bridge.Event_Authenticated) { @@ -27,6 +31,7 @@ class TenTenOneConfigChangeNotifier extends ChangeNotifier implements Subscriber _liquidityOptions.sort((a, b) => a.rank.compareTo(b.rank)); _referralStatus = ReferralStatus.from(event.field0.referralStatus); + _maxLeverage = event.field0.maxLeverage; super.notifyListeners(); } diff --git a/mobile/lib/common/domain/tentenone_config.dart b/mobile/lib/common/domain/tentenone_config.dart index b0f81cca1..2fbfc97ef 100644 --- a/mobile/lib/common/domain/tentenone_config.dart +++ b/mobile/lib/common/domain/tentenone_config.dart @@ -23,6 +23,7 @@ class TenTenOneConfig { numberOfTotalReferrals: 0, referralTier: 0, referralFeeBonus: 0, - bonusStatusType: bridge.BonusStatusType.None)); + bonusStatusType: bridge.BonusStatusType.None), + maxLeverage: 5); } } diff --git a/mobile/lib/features/trade/leverage_slider.dart b/mobile/lib/features/trade/leverage_slider.dart index ee00aa509..d0cc80069 100644 --- a/mobile/lib/features/trade/leverage_slider.dart +++ b/mobile/lib/features/trade/leverage_slider.dart @@ -1,13 +1,14 @@ import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:get_10101/common/application/tentenone_config_change_notifier.dart'; import 'package:get_10101/common/color.dart'; import 'package:get_10101/features/trade/trade_theme.dart'; import 'package:intl/intl.dart'; +import 'package:provider/provider.dart'; import 'package:syncfusion_flutter_sliders/sliders.dart'; import 'package:syncfusion_flutter_core/theme.dart' as slider_theme; const double minLeverage = 1.0; -const double maxLeverage = 5.0; /// Slider that allows the user to select a leverage between minLeverage and maxLeverage. /// It uses linear scale and fractional leverage values are rounded to the nearest integer. @@ -34,6 +35,11 @@ class _LeverageSliderState extends State { @override Widget build(BuildContext context) { + TenTenOneConfigChangeNotifier tentenoneConfigChangeNotifier = + context.read(); + + var maxLeverage = tentenoneConfigChangeNotifier.maxLeverage; + return InputDecorator( decoration: InputDecoration( border: const OutlineInputBorder(), diff --git a/mobile/native/src/api.rs b/mobile/native/src/api.rs index b999a2a0a..6b7486a4a 100644 --- a/mobile/native/src/api.rs +++ b/mobile/native/src/api.rs @@ -69,6 +69,7 @@ pub struct TenTenOneConfig { pub min_quantity: u64, pub maintenance_margin_rate: f32, pub referral_status: ReferralStatus, + pub max_leverage: u8, } impl From for TenTenOneConfig { @@ -82,6 +83,7 @@ impl From for TenTenOneConfig { min_quantity: value.min_quantity, maintenance_margin_rate: value.maintenance_margin_rate, referral_status: value.referral_status.into(), + max_leverage: value.max_leverage, } } } diff --git a/webapp/frontend/lib/services/trade_constraints_service.dart b/webapp/frontend/lib/services/trade_constraints_service.dart index ede634aed..b7e6742dc 100644 --- a/webapp/frontend/lib/services/trade_constraints_service.dart +++ b/webapp/frontend/lib/services/trade_constraints_service.dart @@ -64,6 +64,10 @@ class TradeConstraints { @JsonKey(name: 'channel_fee_reserve_sats') final int channelFeeReserveSats; + /// The max leverage the trader can take + @JsonKey(name: 'max_leverage') + final int maxLeverage; + const TradeConstraints({ required this.maxLocalBalanceSats, required this.maxCounterpartyBalanceSats, @@ -73,6 +77,7 @@ class TradeConstraints { required this.minMarginSats, required this.estimatedFundingTxFeeSats, required this.channelFeeReserveSats, + required this.maxLeverage, }); factory TradeConstraints.fromJson(Map json) => _$TradeConstraintsFromJson(json); diff --git a/webapp/frontend/lib/services/trade_constraints_service.g.dart b/webapp/frontend/lib/services/trade_constraints_service.g.dart index 99159aa17..540eeaac9 100644 --- a/webapp/frontend/lib/services/trade_constraints_service.g.dart +++ b/webapp/frontend/lib/services/trade_constraints_service.g.dart @@ -15,6 +15,7 @@ TradeConstraints _$TradeConstraintsFromJson(Map json) => TradeC minMarginSats: json['min_margin_sats'] as int, estimatedFundingTxFeeSats: json['estimated_funding_tx_fee_sats'] as int, channelFeeReserveSats: json['channel_fee_reserve_sats'] as int, + maxLeverage: json['max_leverage'] as int, ); Map _$TradeConstraintsToJson(TradeConstraints instance) => { @@ -26,4 +27,5 @@ Map _$TradeConstraintsToJson(TradeConstraints instance) => [Colors.green, Colors.deepOrange]; const LinearGradient gradient = LinearGradient(colors: gradientColors); const double minLeverage = 1.0; -const double maxLeverage = 5.0; /// Slider that allows the user to select a leverage between minLeverage and maxLeverage. /// It uses linear scale and fractional leverage values are rounded to the nearest integer. @@ -36,6 +37,12 @@ class _LeverageSliderState extends State { @override Widget build(BuildContext context) { + TradeConstraintsChangeNotifier tradeConstraintsChangeNotifier = + context.read(); + + double maxLeverage = + tradeConstraintsChangeNotifier.tradeConstraints?.maxLeverage.toDouble() ?? 5.0; + return InputDecorator( decoration: InputDecoration( border: const OutlineInputBorder(), diff --git a/webapp/src/api.rs b/webapp/src/api.rs index 94c85c8fb..267619115 100644 --- a/webapp/src/api.rs +++ b/webapp/src/api.rs @@ -18,6 +18,7 @@ use native::api::WalletHistoryItemType; use native::calculations::calculate_pnl; use native::channel_trade_constraints; use native::dlc; +use native::state::try_get_tentenone_config; use native::trade::order::FailureReason; use native::trade::order::InvalidSubchannelOffer; use rust_decimal::prelude::ToPrimitive; @@ -965,6 +966,7 @@ pub struct TradeConstraints { pub min_margin_sats: u64, pub estimated_funding_tx_fee_sats: u64, pub channel_fee_reserve_sats: u64, + pub max_leverage: u8, } #[utoipa::path( @@ -976,6 +978,7 @@ responses( )] pub async fn get_trade_constraints() -> Result, AppError> { let trade_constraints = channel_trade_constraints::channel_trade_constraints()?; + let ten_one_config = try_get_tentenone_config().context("Could not read 10101 config")?; let fee = dlc::estimated_funding_tx_fee()?; let channel_fee_reserve = dlc::estimated_fee_reserve()?; Ok(Json(TradeConstraints { @@ -987,6 +990,7 @@ pub async fn get_trade_constraints() -> Result, AppError> min_margin_sats: trade_constraints.min_margin, estimated_funding_tx_fee_sats: fee.to_sat(), channel_fee_reserve_sats: channel_fee_reserve.to_sat(), + max_leverage: ten_one_config.max_leverage, })) }