From d7ae6abfe844539f49269669dbf5893963a70195 Mon Sep 17 00:00:00 2001 From: Lucas Soriano del Pino Date: Wed, 6 Sep 2023 18:46:35 +1000 Subject: [PATCH 1/6] docs: Add funding rate ADR --- docs/007-funding-rate.adoc | 96 ++++++++++++++++++++++++++++++++++++++ docs/readme.adoc | 1 + 2 files changed, 97 insertions(+) create mode 100644 docs/007-funding-rate.adoc diff --git a/docs/007-funding-rate.adoc b/docs/007-funding-rate.adoc new file mode 100644 index 000000000..4067f0c23 --- /dev/null +++ b/docs/007-funding-rate.adoc @@ -0,0 +1,96 @@ += ADR 007 - Funding rate +Lucas Soriano +:toc: +:icons: font +:attributes: 2023-09-06 + +Status: Proposed + +== Decision + +The funding rate should be charged through a Lightning payment. +The funding rate *must* be paid before renewing the contract. +No funding rate needs to paid for the period of time _before_ the first renewal. + +== Context + +The funding rate is the cost of holding a perpetual swap position that is on the side of the market sentiment. +There needs to be a funding rate because that is what motivates traders to keep their perpetual swaps positions open _against_ the market sentiment. +The sign of the funding rate determines which of the two parties involved must pay the funding fee to keep their position open. + +In the 10101 ecosystem, the funding rate must come from the 10101 orderbook. +This value will be updated at a fixed interval e.g. every 8 hours. + +== Options considered + +=== Lightning payment before the DLC is renewed + +The renewal protocol will first require one of the two parties (coordinator and trader) to generate an invoice for the funding fee. +Which party generates the invoice (i.e. the receiver) is determined by the sign of the funding rate of the 10101 orderbook. + +Only after the invoice has been paid will the coordinator initiate the renew protocol to roll over the DLC between coordinator and trader. + +==== Pros + +- The DLC remains unaffected by the exchanged funding rate. +- Can be implemented without changes to our dependencies. +- Easy to track the funding fee payments. +- Fee is readily available to use after. + +==== Cons + +===== No execution guarantees + +There is no guarantee that the renew protocol will succeed after the funding fee payment is claimed. +Depending on the size of the position, it could be economically beneficial for a party to claim the funding fee paid by the other party and then force-close the channel. +As such, in some scenarios we would be trusting the counterparty to not run away with an unearned funding fee. + +With PTLCs we might be able to make the DLC channel update and the payment atomic, but that is not yet implemented. + +===== Funds required on Lightning channel + +To be able to pay the funding fee the payer will need to have sufficient outbound liquidity. +It might happen that a user will need to stop trading if their Lightning channel cannot cover the costs. + +They would still be able to close their position and open a new one. + +=== Adjust the CET payouts depending on the funding fee + +If the funding fee is `X` paid from `A` to `B`, for every `o_A`, CET output amount belonging to `A`, we would subtract `X`: `o_A - X`; and for every `o_B`, CET output belonging to `B`, we would add `X`: `o_B + X`. + +Unfortunately this doesn't work in all scenarios: + +- If a CET already only pays to `B` then the payout cannot be increased! +- If a CET has `o_A < X`, then it will not be possible to increase `o_B` sufficiently. + +As such, in some scenarios the fee will not be paid. +This problem might compound as the same position might be rolled over multiple times. +If the advantaged party ends up getting liquidated, the winning party will not see any funding fee. + +==== Pros + +- We already did something similar in ItchySats. +- The funding fee payment and the DLC renewal are atomic. + +==== Cons + +- Fee cannot be charged in all cases. +- Fee is only available after the DLC channel is settled. + +=== Use the dedicated `counter_payout` argument in the `rust-dlc` DLC channel renew protocol + +Looking at the `dlc_manager::Manager::renew_offer` API we noticed a `counter_payout` argument which we thought we could use to adjust the payouts between the two parties. +It turns out that this field is only used to choose a kind of symbolic payout for the DLC that is being replaced, so that it can be closed with that value. +In practice this is not really useful in itself as it has no effect on the new DLC{empty}footnote:[We should probably consider removing this unnecessary complexity from `rust-dlc`, particularly because consumers do not care to set this value.]. + +All in all, this solution simply does not work. + +== Consequences + +By choosing to pay the funding fee using the Lightning channel we get a simple solution to the problem. +This comes at the cost of atomicity, but the only solution that ensures atomicity does not work in all cases. +Furthermore, atomicity can be achieved once PTLCs are supported by `rust-lightning`. + +== Advice + +None thus far. diff --git a/docs/readme.adoc b/docs/readme.adoc index 68c3fb60c..c633cf5d1 100644 --- a/docs/readme.adoc +++ b/docs/readme.adoc @@ -13,3 +13,4 @@ This is an overview of all documents related to 10101. . link:../docs/004-solution-architecture.adoc[ADR-004 Solution Architecture] . link:../docs/005-mobile-app-routing.adoc[ADR-005 Mobile App Routing] . link:../docs/006-just-in-time-channels.adoc[ADR-006 Just-in-time channels] +. link:../docs/007-funding-rate.adoc[ADR-007 Funding rate] From 446c176073e5a90f41e5a1ed1f7628c5aad039d7 Mon Sep 17 00:00:00 2001 From: Lucas Soriano del Pino Date: Thu, 7 Sep 2023 15:08:27 +1000 Subject: [PATCH 2/6] docs: Add Philipp's advice to ADR 007 --- docs/007-funding-rate.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/007-funding-rate.adoc b/docs/007-funding-rate.adoc index 4067f0c23..8eb087a6f 100644 --- a/docs/007-funding-rate.adoc +++ b/docs/007-funding-rate.adoc @@ -93,4 +93,4 @@ Furthermore, atomicity can be achieved once PTLCs are supported by `rust-lightni == Advice -None thus far. +- Philipp on Sep 6, 2023: Adjusting the CET payouts depending on the funding fee is preferred over the Lightning payment before the DLC is renewed. From a3aaff9a818716185a7d72c1aa9109b2a8b980d3 Mon Sep 17 00:00:00 2001 From: Lucas Soriano del Pino Date: Thu, 7 Sep 2023 15:24:51 +1000 Subject: [PATCH 3/6] docs: Propose new decision in ADR 007 --- docs/007-funding-rate.adoc | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/docs/007-funding-rate.adoc b/docs/007-funding-rate.adoc index 8eb087a6f..d5e98666d 100644 --- a/docs/007-funding-rate.adoc +++ b/docs/007-funding-rate.adoc @@ -8,9 +8,8 @@ Status: Proposed == Decision -The funding rate should be charged through a Lightning payment. -The funding rate *must* be paid before renewing the contract. -No funding rate needs to paid for the period of time _before_ the first renewal. +Use the `ContractInput` argument when calling `dlc_manager::Manager::renew_offer`. +By setting new values of `offer_collateral` and `accept_collateral` on `ContractInput` we can rebalance the margins between the two parties, which will have the desired effect on the payouts. == Context @@ -23,6 +22,23 @@ This value will be updated at a fixed interval e.g. every 8 hours. == Options considered +=== Use the `ContractInput` argument in the `rust-dlc` DLC channel renew protocol + +By setting a new collateral distribution between trader and coordinator based on the funding fee, we will recreate the DLC with new margins. +The updated margin will have an effect on how the payout curve should look like. + +If the contract is _refunded_ (catastrophic error), the returned collateral will take into account all the funding fees exchanged. + +==== Pros + +- Atomic update to the DLC and funding fee exchange. +- Easy implementation. +- Funding fee exchange is reflected on the `refund_transaction`. + +==== Cons + +- Fee is only available after the DLC channel is settled. + === Lightning payment before the DLC is renewed The renewal protocol will first require one of the two parties (coordinator and trader) to generate an invoice for the funding fee. @@ -94,3 +110,4 @@ Furthermore, atomicity can be achieved once PTLCs are supported by `rust-lightni == Advice - Philipp on Sep 6, 2023: Adjusting the CET payouts depending on the funding fee is preferred over the Lightning payment before the DLC is renewed. +- Lucas on Sep 7, 2023: The renew protocol _can_ be used to solve this problem, just not in the way that we originally imagined. From a08e862fabbe2223038453d8300c833f3fed505b Mon Sep 17 00:00:00 2001 From: Martin Salas Date: Thu, 7 Sep 2023 15:23:01 -0400 Subject: [PATCH 4/6] Add Positions Details for Orders --- .../lib/features/trade/domain/direction.dart | 4 ++-- .../features/trade/position_list_item.dart | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/mobile/lib/features/trade/domain/direction.dart b/mobile/lib/features/trade/domain/direction.dart index e2c4646ad..ca4d7049e 100644 --- a/mobile/lib/features/trade/domain/direction.dart +++ b/mobile/lib/features/trade/domain/direction.dart @@ -27,9 +27,9 @@ enum Direction { String get keySuffix { switch (this) { case Direction.long: - return "long"; + return "Long"; case Direction.short: - return "short"; + return "Short"; } } diff --git a/mobile/lib/features/trade/position_list_item.dart b/mobile/lib/features/trade/position_list_item.dart index b7df25a5a..f5428e55c 100644 --- a/mobile/lib/features/trade/position_list_item.dart +++ b/mobile/lib/features/trade/position_list_item.dart @@ -94,6 +94,13 @@ class _PositionListItemState extends State { notNullPosition.contractSymbol.label, style: const TextStyle(fontWeight: FontWeight.bold), ), + const SizedBox( + width: 5, + ), + Text( + notNullPosition.direction.keySuffix, + style: const TextStyle(fontWeight: FontWeight.bold), + ), ], ), ], @@ -116,6 +123,13 @@ class _PositionListItemState extends State { : tradeTheme.profit), labelTextStyle: dataRowStyle, ), + ValueDataRow( + type: ValueType.text, + value: notNullPosition.direction.keySuffix, + label: "Direction", + valueTextStyle: dataRowStyle, + labelTextStyle: dataRowStyle, + ), ValueDataRow( type: ValueType.amount, value: notNullPosition.collateral, @@ -123,6 +137,13 @@ class _PositionListItemState extends State { valueTextStyle: dataRowStyle, labelTextStyle: dataRowStyle, ), + ValueDataRow( + type: ValueType.text, + value: notNullPosition.leverage.formatted(), + label: "Leverage", + valueTextStyle: dataRowStyle, + labelTextStyle: dataRowStyle, + ), ValueDataRow( type: ValueType.date, value: notNullPosition.expiry, From 0ffe90cde6781815dac8e02359a86aabf6b0e679 Mon Sep 17 00:00:00 2001 From: Philipp Hoenisch Date: Fri, 8 Sep 2023 11:18:20 +0200 Subject: [PATCH 5/6] fix: make sure the oracle is set --- .env.sample | 2 ++ justfile | 45 ++++++++++++++++++++++++++++++++++++--------- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/.env.sample b/.env.sample index 82a545df1..1d4f1c7b1 100644 --- a/.env.sample +++ b/.env.sample @@ -6,6 +6,8 @@ ESPLORA_ENDPOINT=http://35.189.57.114:3000 COORDINATOR_P2P_ENDPOINT=03507b924dae6595cfb78492489978127c5f1e3877848564de2015cd6d41375802@35.189.57.114:9045 COORDINATOR_PORT_HTTP=80 NETWORK=regtest +ORACLE_ENDPOINT=http://api.test.10101.finance:8081 +ORACLE_PUBKEY=5d12d79f575b8d99523797c46441c0549eb0defb6195fe8a080000cbe3ab3859 # Fastlane android variables ANDROID_SERVICE_ACCOUNT_KEY=/path/to/android-service-key.json diff --git a/justfile b/justfile index 729aff86d..49ec07243 100644 --- a/justfile +++ b/justfile @@ -11,6 +11,8 @@ pubspec := "$PWD/mobile/pubspec.yaml" public_regtest_coordinator := "03507b924dae6595cfb78492489978127c5f1e3877848564de2015cd6d41375802@35.189.57.114:9045" public_regtest_esplora := "http://35.189.57.114:3000" public_coordinator_http_port := "80" +public_regtest_oracle_endpoint := "http://api.test.10101.finance:8081" +public_regtest_oracle_pk := "5d12d79f575b8d99523797c46441c0549eb0defb6195fe8a080000cbe3ab3859" # command to get the local IP of this machine get_local_ip := if os() == "linux" { @@ -89,24 +91,42 @@ ios-release: run args="": #!/usr/bin/env bash - cd mobile && flutter run {{args}} --dart-define="COMMIT=$(git rev-parse HEAD)" --dart-define="BRANCH=$(git rev-parse --abbrev-ref HEAD)" \ - --dart-define="REGTEST_FAUCET=http://localhost:8080" --dart-define="HEALTH_CHECK_INTERVAL_SECONDS=2" \ + cd mobile && \ + flutter run {{args}} \ + --dart-define="COMMIT=$(git rev-parse HEAD)" \ + --dart-define="BRANCH=$(git rev-parse --abbrev-ref HEAD)" \ + --dart-define="REGTEST_FAUCET=http://localhost:8080" \ + --dart-define="HEALTH_CHECK_INTERVAL_SECONDS=2" # Run against our public regtest server run-regtest args="": #!/usr/bin/env bash - cd mobile && flutter run {{args}} --dart-define="COMMIT=$(git rev-parse HEAD)" --dart-define="BRANCH=$(git rev-parse --abbrev-ref HEAD)" \ - --dart-define="ESPLORA_ENDPOINT={{public_regtest_esplora}}" --dart-define="COORDINATOR_P2P_ENDPOINT={{public_regtest_coordinator}}" \ - --dart-define="COORDINATOR_PORT_HTTP={{public_coordinator_http_port}}" + cd mobile && \ + flutter run {{args}} \ + --dart-define="COMMIT=$(git rev-parse HEAD)" \ + --dart-define="BRANCH=$(git rev-parse --abbrev-ref HEAD)" \ + --dart-define="ESPLORA_ENDPOINT={{public_regtest_esplora}}" \ + --dart-define="COORDINATOR_P2P_ENDPOINT={{public_regtest_coordinator}}" \ + --dart-define="COORDINATOR_PORT_HTTP={{public_coordinator_http_port}}" \ + --dart-define="ORACLE_ENDPOINT={{public_regtest_oracle_endpoint}}" \ + --dart-define="ORACLE_PUBKEY={{public_regtest_oracle_pk}}" \ [unix] run-local-android args="": #!/usr/bin/env bash LOCAL_IP=$({{get_local_ip}}) echo "Android app will connect to $LOCAL_IP for 10101 services" - cd mobile && flutter run {{args}} --dart-define="COMMIT=$(git rev-parse HEAD)" --dart-define="BRANCH=$(git rev-parse --abbrev-ref HEAD)" \ - --dart-define="ESPLORA_ENDPOINT=http://${LOCAL_IP}:3000" --dart-define="COORDINATOR_P2P_ENDPOINT=02dd6abec97f9a748bf76ad502b004ce05d1b2d1f43a9e76bd7d85e767ffb022c9@${LOCAL_IP}:9045" \ - --dart-define="REGTEST_FAUCET=http://${LOCAL_IP}:8080" --dart-define="COORDINATOR_PORT_HTTP=8000" --flavor local + cd mobile && \ + flutter run {{args}} \ + --dart-define="COMMIT=$(git rev-parse HEAD)" \ + --dart-define="BRANCH=$(git rev-parse --abbrev-ref HEAD)" \ + --dart-define="ESPLORA_ENDPOINT=http://${LOCAL_IP}:3000" \ + --dart-define="COORDINATOR_P2P_ENDPOINT=02dd6abec97f9a748bf76ad502b004ce05d1b2d1f43a9e76bd7d85e767ffb022c9@${LOCAL_IP}:9045" \ + --dart-define="REGTEST_FAUCET=http://${LOCAL_IP}:8080" + --dart-define="COORDINATOR_PORT_HTTP=8000" \ + --dart-define="ORACLE_ENDPOINT=http://${LOCAL_IP}:8081" \ + --dart-define="ORACLE_PUBKEY=16f88cf7d21e6c0f46bcbc983a4e3b19726c6c98858cc31c83551a88fde171c0" \ + --flavor local fund args="": cargo run --example fund @@ -382,6 +402,8 @@ build-ipa args="": --dart-define="COMMIT=$(git rev-parse HEAD)" \ --dart-define="BRANCH=$(git rev-parse --abbrev-ref HEAD)" \ --dart-define="COORDINATOR_PORT_HTTP=${COORDINATOR_PORT_HTTP}" \ + --dart-define="ORACLE_ENDPOINT=${ORACLE_ENDPOINT}" \ + --dart-define="ORACLE_PUBKEY=${ORACLE_PUBKEY}" \ --build-number=${BUILD_NUMBER} \ {{args}} @@ -412,7 +434,10 @@ build-apk-regtest: --dart-define="BRANCH=$(git rev-parse --abbrev-ref HEAD)" \ --dart-define="ESPLORA_ENDPOINT={{public_regtest_esplora}}" \ --dart-define="COORDINATOR_P2P_ENDPOINT={{public_regtest_coordinator}}" \ - --dart-define="COORDINATOR_PORT_HTTP={{public_coordinator_http_port}}" --flavor demo + --dart-define="COORDINATOR_PORT_HTTP={{public_coordinator_http_port}}" \ + --dart-define="ORACLE_ENDPOINT={{public_regtest_oracle_endpoint}}" \ + --dart-define="ORACLE_PUBKEY={{public_regtest_oracle_pk}}" \ + --flavor demo release-apk-regtest: gen android-release build-apk-regtest @@ -431,6 +456,8 @@ build-app-bundle-regtest: --dart-define="ESPLORA_ENDPOINT={{public_regtest_esplora}}" \ --dart-define="COORDINATOR_P2P_ENDPOINT={{public_regtest_coordinator}}" \ --dart-define="COORDINATOR_PORT_HTTP={{public_coordinator_http_port}}" \ + --dart-define="ORACLE_ENDPOINT={{public_regtest_oracle_endpoint}}" \ + --dart-define="ORACLE_PUBKEY={{public_regtest_oracle_pk}}" \ --flavor demo From 1aa5e90c8dcb343eed7df7fc0a7f1425f38dd8df Mon Sep 17 00:00:00 2001 From: Restioson Date: Fri, 8 Sep 2023 16:24:43 +0200 Subject: [PATCH 6/6] fix: GoError: There is nothing to pop when paying with faucet Take 2?? --- mobile/lib/features/wallet/share_invoice_screen.dart | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/mobile/lib/features/wallet/share_invoice_screen.dart b/mobile/lib/features/wallet/share_invoice_screen.dart index cf5844ab0..1e23a7401 100644 --- a/mobile/lib/features/wallet/share_invoice_screen.dart +++ b/mobile/lib/features/wallet/share_invoice_screen.dart @@ -215,11 +215,13 @@ class _ShareInvoiceScreenState extends State { Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), child: ElevatedButton( - onPressed: () { - // Pop both create invoice screen and share invoice screen - GoRouter.of(context).pop(); - GoRouter.of(context).pop(); - }, + onPressed: _isPayInvoiceButtonDisabled + ? null + : () { + // Pop both create invoice screen and share invoice screen + GoRouter.of(context).pop(); + GoRouter.of(context).pop(); + }, style: ElevatedButton.styleFrom( shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(5.0))),