Skip to content
This repository has been archived by the owner on Nov 17, 2023. It is now read-only.

feature: set feelimit percentage in settings #315

Open
wants to merge 11 commits into
base: master
Choose a base branch
from

Conversation

chitowncrispy
Copy link
Contributor

@chitowncrispy chitowncrispy commented Nov 11, 2019

Description

The user is now able to set a fee limit percentage for any on-chain payments that they make or invoices that they fulfill. When the user goes to make an on-chain payment which has a fee above their threshold they are present a warning and are given the options to cancel or proceed with payment. When the user fulfills an invoice they are also presented a warning and are given the options to cancel or proceed but there is some additional precaution taken. On the LND API, we are now specifying a fee limit for the invoice fulfillment which guarantees that the fee will not be greater than a certain percentage.

Most of the logic for this feature is in the SendViewController.swift and SendViewModel.swift. The logic for determining the fee limit to eventually set on the API call can be found in the determineSendStatus function. A new series of callbacks and delegation takes place in order to process payments within this MVC pair.

Motivation and Context

This is to address issue #278.

How Has This Been Tested?

On Testnet I have done a series of payments on-chain as well as fulfilled different invoices.

On-chain I have sent small payments that have triggered the fee limit alert and they have processed fine. I have also tested that we can cancel the payment as if we were unwilling to pay the large fees and that also worked fine. Finally, I have sent payments large enough to not trigger the fee limit alert and they have also processed fine.

On the lightning network, I have tested that we can cancel the invoice fulfillment as if we were unwilling to pay the large fees and that worked fine. I also fulfilled an invoice for 100 sats which is right at our threshold amount and I observed that the fee limit set for that payment was 100%. The actual fee ended up much lower but I observed that the API call was capped. I also fulfilled small invoices for just over 100 sats which triggered the alert and those were sent without a fee limit as we have outlined in the logic. Finally, I fulfilled invoices that were large enough to not trigger a fee alert and I observed that the fee limit was sent correctly to what the user had specified in the settings.

I was also able to change the fee limit selected in the settings and see that persisted between different app launches.

Screenshots (if appropriate):

Screen Shot 2019-11-11 at 3 01 44 PM

Screen Shot 2019-11-11 at 3 01 18 PM

Screen Shot 2019-11-11 at 3 02 14 PM

Screen Shot 2019-11-11 at 3 02 52 PM

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • All new and existing tests passed.

@codecov-io
Copy link

codecov-io commented Nov 11, 2019

Codecov Report

Merging #315 into master will increase coverage by 3.71%.
The diff coverage is 61.71%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #315      +/-   ##
==========================================
+ Coverage   10.71%   14.42%   +3.71%     
==========================================
  Files         247      250       +3     
  Lines        9803    10237     +434     
==========================================
+ Hits         1050     1477     +427     
- Misses       8753     8760       +7
Impacted Files Coverage Δ
...ngs/Items/LightningRequestExpirySettingsItem.swift 0% <0%> (ø) ⬆️
...xtensions/Localizable/ExpiryTime+Localizable.swift 0% <0%> (ø) ⬆️
...brary/Scenes/Settings/SettingsViewController.swift 0% <0%> (ø) ⬆️
...y/Scenes/ModalDetail/Send/SendViewController.swift 0% <0%> (ø) ⬆️
Lightning/Services/TransactionService.swift 18.75% <0%> (ø) ⬆️
Library/Extensions/UIAlertController.swift 0% <0%> (ø) ⬆️
Library/Generated/strings.swift 14.89% <0%> (+7.91%) ⬆️
...izable/PaymentFeeLimitPercentage+Localizable.swift 0% <0%> (ø)
...s/Items/LightningPaymentFeeLimitSettingsItem.swift 0% <0%> (ø)
Library/Scenes/Settings/Settings.swift 75.34% <100%> (+75.34%) ⬆️
... and 14 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update efa92e2...eac918e. Read the comment docs.


import Foundation

public enum PaymentFeeLimitPercentage: Int, Codable, CaseIterable {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this used in SwiftLnd or can it be moved to Library?

Copy link
Contributor Author

@chitowncrispy chitowncrispy Nov 11, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. I moved it to Library

@ottosuess
Copy link
Member

Apple recommends not using yes or no for button titles.

Give alert buttons succinct, logical titles. The best button titles consist of one or two words that describe the result of selecting the button. As with all button titles, use title-style capitalization and no ending punctuation. To the extent possible, use verbs and verb phrases that relate directly to the alert title and message—for example, View All, Reply, or Ignore. Use OK for simple acceptance. Avoid using Yes and No.

(https://developer.apple.com/design/human-interface-guidelines/ios/views/alerts/)

maybe we can change them to "cancel" and "send" or "pay"?

@ottosuess
Copy link
Member

@JimmyMow what do you think about displaying the warning for on-chain payments?

With lightning payment it makes sense because you don't know the actual fee before sending and the fee grows in relation to the amount you send.
With on-chain payments you see the actual fee before you send and it is only partially related to the amount you send.

Idk if a % based limit makes sense for on-chain. But i also don't see reasons against it. 😀

@ottosuess
Copy link
Member

ottosuess commented Nov 14, 2019

Talked to @JimmyMow about whether to show warnings for on-chain transactions or not.

The reason why we need the warning is to protect users from getting tricked into overpaying fees for lightning payments.

That's why we came to the conclusion that it just complicates UX if we display the warning for on-chain as well.

@chitowncrispy
Copy link
Contributor Author

Added unit tests for the SendViewModel

import Foundation

public enum PaymentFeeLimitPercentage: Int, Codable, CaseIterable {
case zero = 0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if i understand this correctly zero actually means "none"?
in the concrete implementation of lnd the value 0 means that there is no fee limit. but the enum should abstract concrete implementation details away. imo calling it none would make it a bit easier to understand.

super.init()

Settings.shared.lightningRequestExpiry
.observeNext { [isSelectedOption] currenteExpiryTime in
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👌

@@ -45,4 +45,20 @@ extension UIAlertController {

return alertController
}

static func feeLimitAlertController(message: String, sendAction: @escaping () -> Void) -> UIAlertController {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like my UIAlertController extension any more 😅

Imo it would make sense to remove it and put the Alerts in the ViewControllers where they are actually used.
(as long as both Alerts are only used in one place).
Otherwise the code just gets separated without much benefit. just makes it harder to reason about it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My other PR also has an alert added to this controller. I don't mind where they live but I would rather open an issue to refactor the alerts out of this extension and address it after the PRs are merged rather than refactor both PRs.

case .zero:
return L10n.PaymentFeeLimitPercentage.none
default:
return "\(self.rawValue)%"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use the NumberFormatter() from the SendViewController here. Maybe put it in its own extension file.
There are many different ways to format the percent sign (https://en.wikipedia.org/wiki/Percent_sign#Correct_style) 😅

let subtitleText = Observable<String?>(nil)
let isSubtitleTextWarning = Observable(false)
let sendStatus = Subject<Int?, SendError>()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So far i've tried avoiding use of ReactiveKit and stuck to Bond for several reasons:

  • I wan't to limit the use of 3rd party libraries for security reasons and replace bond & reactive kit by the new native combine framework in a few years.
  • I want the app to be approachable to every iOS dev so i don't want to many custom libraries that add additional hurdles for getting into the code.
  • it's just too different to regular mvc / mvvc stuff and not very readable (for me). I once built a reactive app, went on holidays, and when i came back i couldn't understand anything i did and had to start from scratch. 😅

i'm fine merging this like it is. but in general i believe that reactive code has more drawbacks than benefits (at least in the specific case of zap iOS).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I completely understand your points but I think there are a few things worth considering.

  • The Bond framework is built on top of ReactiveKit which inherently means we are using ReactiveKit wherever Bond is used.
  • The combine framework seems eerily similar to a Reactive framework. I feel like going all Reactive would be the best path forward if the goal is to refactor everything out to use that in the future.
  • I didn't use Bond in particular here because the way we seem to be using Bond is with an Observable which doesn't support the error state. We also don't have a direct UI item that changes based upon the result of this "function."

I feel like in order to make it easiest on contributors going forward we either define a clearer code architecture/styling guide detailing how to do "delegation/Reactive" programming or we begin the refactor now to pull out ReactiveKit as well as Bond. It's just hard to mandate against its use if we have it pulled in as a framework IMO.

if feePercent > Decimal(Settings.shared.lightningPaymentFeeLimit.value.rawValue) {
self.sendStatus.receive(event: .failed(.feePercentageGreaterThanUserLimit(FeeInfo(feePercentage: feePercent, userFeeLimitPercentage: Settings.shared.lightningPaymentFeeLimit.value.rawValue, sendFeeLimitPercentage: 100))))
} else {
self.sendStatus.receive(Settings.shared.lightningPaymentFeeLimit.value.rawValue)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you had a version with delegation before, right? to be honest i would prefer that. it's just more common and easier to understand.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't have a version with delegation. I wouldn't mind merging this as is and then we can have a discussion/define a clearer code styling/architecture guide and then refactor this later to however you see most fit.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants