Skip to content

Commit

Permalink
Add warning when sending to address with issue
Browse files Browse the repository at this point in the history
  • Loading branch information
ealymbaev committed Feb 6, 2025
1 parent 8e01a51 commit 2961bac
Show file tree
Hide file tree
Showing 8 changed files with 165 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "[email protected]",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "[email protected]",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,31 @@ enum AddressSecurityIssueType: CaseIterable, Identifiable {
self
}

var title: String {
var checkTitle: String {
switch self {
case .phishing: return "send.address.phishing_check".localized
case .sanctioned: return "send.address.blacklist_check".localized
}
}

var preSendTitle: String {
switch self {
case .phishing: return "send.address.phishing.pre_send.title".localized
case .sanctioned: return "send.address.blacklist.pre_send.title".localized
}
}

var preSendDescription: String {
switch self {
case .phishing: return "send.address.phishing.pre_send.description".localized
case .sanctioned: return "send.address.blacklist.pre_send.description".localized
}
}

var caution: CautionNew {
switch self {
case .phishing: return CautionNew(title: "send.address.phishing.title".localized, text: "send.address.phishing.description".localized, type: .error)
case .sanctioned: return CautionNew(title: "send.address.blacklist.title".localized, text: "send.address.blacklist.description".localized, type: .error)
case .phishing: return CautionNew(title: "send.address.phishing.caution.title".localized, text: "send.address.phishing.caution.description".localized, type: .error)
case .sanctioned: return CautionNew(title: "send.address.blacklist.caution.title".localized, text: "send.address.blacklist.caution.description".localized, type: .error)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ struct AddressView: View {
private var onFinish: (ResolvedAddress) -> Void

@Environment(\.presentationMode) private var presentationMode
@State var subscriptionPresented = false

init(wallet: Wallet, address: String? = nil, onFinish: @escaping (ResolvedAddress) -> Void) {
_viewModel = StateObject(wrappedValue: AddressViewModel(wallet: wallet, address: address))
Expand Down Expand Up @@ -48,7 +49,7 @@ struct AddressView: View {
ListSection {
VStack(spacing: 0) {
ForEach(viewModel.issueTypes) { type in
checkView(title: type.title, state: viewModel.checkStates[type] ?? .notAvailable)
checkView(title: type.checkTitle, state: viewModel.checkStates[type] ?? .notAvailable)
}
}
}
Expand Down Expand Up @@ -90,6 +91,9 @@ struct AddressView: View {
.disabled(disabled)
.buttonStyle(PrimaryButtonStyle(style: .yellow))
}
.sheet(isPresented: $subscriptionPresented) {
PurchasesView()
}
}

@ViewBuilder private func row(contact: AddressViewModel.Contact) -> some View {
Expand All @@ -110,8 +114,8 @@ struct AddressView: View {
@ViewBuilder private func checkView(title: String, state: AddressViewModel.CheckState) -> some View {
HStack(spacing: .margin8) {
HStack(spacing: 2) {
Image("star_premium_20").themeIcon(color: .themeJacob)
Text(title).textSubhead2()
Image("crown_20").themeIcon(color: .themeJacob)
}

Spacer()
Expand All @@ -131,6 +135,13 @@ struct AddressView: View {
}
.padding(.horizontal, .margin16)
.frame(minHeight: 40)
.contentShape(Rectangle())
.onTapGesture {
switch state {
case .locked: subscriptionPresented = true
default: ()
}
}
}

private func buttonState() -> (String, Bool, Bool) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,29 @@ import Foundation
import MarketKit

class AddressViewModel: ObservableObject {
private let purchaseManager = App.shared.purchaseManager
private let wallet: Wallet
let issueTypes: [AddressSecurityIssueType]
let contacts: [Contact]
let recentContact: Contact?
private var cancellables = Set<AnyCancellable>()

private var premiumEnabled: Bool {
didSet {
syncAddressState()
}
}

@Published var address: String = ""
@Published var addressResult: AddressInput.Result = .idle {
didSet {
syncAddressState()
}
}

@Published var state: State = .empty
@Published private(set) var state: State = .empty

@Published var checkStates = [AddressSecurityIssueType: CheckState]() {
@Published private(set) var checkStates = [AddressSecurityIssueType: CheckState]() {
didSet {
syncValidState()
}
Expand Down Expand Up @@ -46,11 +53,19 @@ class AddressViewModel: ObservableObject {

self.contacts = contacts

premiumEnabled = purchaseManager.subscription != nil

defer {
if let address {
self.address = address
}
}

purchaseManager.$subscription
.sink { [weak self] subscription in
self?.premiumEnabled = subscription != nil
}
.store(in: &cancellables)
}

private func syncAddressState() {
Expand All @@ -60,7 +75,15 @@ class AddressViewModel: ObservableObject {
case .loading, .invalid:
state = .invalid
case let .valid(success):
check(address: success.address)
if premiumEnabled {
check(address: success.address)
} else {
for type in issueTypes {
checkStates[type] = .locked
}

state = .valid(resolvedAddress: ResolvedAddress(address: address, issueTypes: []))
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ struct PreSendView: View {
@State private var settingsPresented = false
@State private var confirmPresented = false

@State private var addressSecurityIssueType: AddressSecurityIssueType?
@State private var sheetHeight: CGFloat = .zero

@State private var issueTypes = [AddressSecurityIssueType]()

init(wallet: Wallet, resolvedAddress: ResolvedAddress, amount: Decimal? = nil, addressVisible: Bool = true, onDismiss: @escaping () -> Void) {
_viewModel = StateObject(wrappedValue: PreSendViewModel(wallet: wallet, resolvedAddress: resolvedAddress, amount: amount))
self.addressVisible = addressVisible
Expand Down Expand Up @@ -89,6 +94,56 @@ struct PreSendView: View {
}
}
}
.sheet(item: $addressSecurityIssueType) { issueType in
VStack(spacing: 0) {
HStack(spacing: .margin16) {
Image("warning_2_24").themeIcon(color: .themeLucian)

Text(issueType.preSendTitle).themeHeadline2()

Button(action: {
addressSecurityIssueType = nil
}) {
Image("close_3_24")
}
}
.padding(.horizontal, .margin32)
.padding(.vertical, .margin24)

HighlightedTextView(text: issueType.preSendDescription, style: .alert)
.padding(.horizontal, .margin16)

VStack(spacing: .margin12) {
Button(action: {
print("SEND")
// viewModel.sendAnyway = true
addressSecurityIssueType = nil
handleNext()
}) {
Text("send.send_anyway".localized)
}
.buttonStyle(PrimaryButtonStyle(style: .red))

Button(action: {
addressSecurityIssueType = nil
}) {
Text("button.cancel".localized)
}
.buttonStyle(PrimaryButtonStyle(style: .transparent))
}
.padding(EdgeInsets(top: .margin24, leading: .margin24, bottom: .margin16, trailing: .margin24))
}
.fixedSize(horizontal: false, vertical: true)
.overlay {
GeometryReader { geometry in
Color.clear.preference(key: InnerHeightPreferenceKey.self, value: geometry.size.height)
}
}
.onPreferenceChange(InnerHeightPreferenceKey.self) { newHeight in
sheetHeight = newHeight
}
.presentationDetents([.height(sheetHeight)])
}
.accentColor(.themeJacob)
}

Expand Down Expand Up @@ -196,11 +251,11 @@ struct PreSendView: View {
} content: {
Text("send.confirmation.to".localized).textSubhead2()

Spacer()

Text(viewModel.resolvedAddress.address)
.textSubhead1(color: .themeLeah)
.multilineTextAlignment(.trailing)
.textSubhead2(color: .themeLeah)
.multilineTextAlignment(.leading)

Spacer()

if !viewModel.resolvedAddress.issueTypes.isEmpty {
Image.warningIcon
Expand All @@ -226,7 +281,8 @@ struct PreSendView: View {
let (title, disabled, showProgress) = buttonState()

Button(action: {
confirmPresented = true
issueTypes = viewModel.resolvedAddress.issueTypes
handleNext()
}) {
HStack(spacing: .margin8) {
if showProgress {
Expand All @@ -250,6 +306,14 @@ struct PreSendView: View {
}
}

private func handleNext() {
if let issueType = issueTypes.popLast() {
addressSecurityIssueType = issueType
} else {
confirmPresented = true
}
}

private func balanceValue() -> String? {
guard let availableBalance = viewModel.availableBalance else {
return nil
Expand Down Expand Up @@ -289,3 +353,10 @@ extension PreSendView {
case fiatAmount
}
}

struct InnerHeightPreferenceKey: PreferenceKey {
static let defaultValue: CGFloat = .zero
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
}
17 changes: 11 additions & 6 deletions UnstoppableWallet/UnstoppableWallet/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -535,20 +535,25 @@
"send.token_syncing" = "Token Syncing";
"send.token_not_synced" = "Token Not Synced";
"send.insufficient_balance" = "Insufficient Balance";
"send.send_anyway" = "Send Anyway";

"send.address.enter_address" = "Enter Address";
"send.address.invalid_address" = "Invalid Address";
"send.address.recent" = "Recent";
"send.address.contacts" = "Contacts";
"send.address.checking" = "Checking";
"send.address.phishing_check" = "Phishing Check";
"send.address.blacklist_check" = "Blacklist Check";
"send.address.check.clear" = "Clear";
"send.address.check.detected" = "Detected";
"send.address.phishing.title" = "Phishing Address Detected!";
"send.address.phishing.description" = "The address you entered may be fraudulent. Scammers often alter the first and last characters. Verify the full address carefully before proceeding to avoid loss.";
"send.address.blacklist.title" = "Blacklisted Address Detected!";
"send.address.blacklist.description" = "The address you entered is flagged as blacklisted. Transactions to this address could lead to loss or other risks. Please verify the recipient's details before proceeding.";
"send.address.phishing_check" = "Phishing Check";
"send.address.phishing.caution.title" = "Phishing Address Detected!";
"send.address.phishing.caution.description" = "The address you entered may be fraudulent. Scammers often alter the first and last characters. Verify the full address carefully before proceeding to avoid loss.";
"send.address.phishing.pre_send.title" = "Phishing Address!";
"send.address.phishing.pre_send.description" = "You are about to send funds to a suspicious address that may be fraudulent. Please be aware that this could result in a permanent loss of your tokens. By proceeding, you acknowledge the risks involved. Are you sure you want to continue?";
"send.address.blacklist_check" = "Blacklist Check";
"send.address.blacklist.caution.title" = "Blacklisted Address!";
"send.address.blacklist.caution.description" = "The address you entered is flagged as blacklisted. Transactions to this address could lead to loss or other risks. Please verify the recipient's details before proceeding.";
"send.address.blacklist.pre_send.title" = "Blacklisted Address Detected!";
"send.address.blacklist.pre_send.description" = "You are about to send funds to an address that is blacklisted. Transactions to this address could result in loss or other risks. By proceeding, you acknowledge the potential dangers. Are you sure you want to continue?";

// Donate

Expand Down

0 comments on commit 2961bac

Please sign in to comment.