Skip to content
This repository has been archived by the owner on May 10, 2024. It is now read-only.

Fix #7827: Sign In With Ethereum #8319

Merged
merged 6 commits into from
Nov 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions Sources/BraveWallet/Crypto/Accounts/AccountView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,27 @@ struct AccountView: View {
var address: String
/// The account name describing what the account is for
var name: String
/// The shape of the blockie used
var blockieShape: Blockie.Shape = .circle

@ScaledMetric private var avatarSize = 40.0
private let maxAvatarSize: CGFloat = 80.0
/// Corner radius only applied when `blockShape` is `rectangle`.
@ScaledMetric var cornerRadius = 4

var body: some View {
HStack {
Blockie(address: address)
.frame(width: min(avatarSize, maxAvatarSize), height: min(avatarSize, maxAvatarSize))
Group {
if blockieShape == .rectangle {
Blockie(address: address, shape: blockieShape)
.frame(width: min(avatarSize, maxAvatarSize), height: min(avatarSize, maxAvatarSize))
.clipShape(RoundedRectangle(cornerRadius: cornerRadius))
} else {
Blockie(address: address, shape: blockieShape)
.frame(width: min(avatarSize, maxAvatarSize), height: min(avatarSize, maxAvatarSize))
.clipShape(Circle())
}
}
VStack(alignment: .leading, spacing: 2) {
Text(name)
.fontWeight(.semibold)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,37 @@ extension String {
guard let number = String.numberFormatterWithCurrentLocale.number(from: self) else { return self }
return String.numberFormatterUsLocale.string(from: number) ?? self
}

var hasUnknownUnicode: Bool {
// same requirement as desktop. Valid: [0, 127]
for c in unicodeScalars {
let ci = Int(c.value)
if ci > 127 {
return true
}
}
return false
}

var hasConsecutiveNewLines: Bool {
// return true if string has two or more consecutive newline chars
return range(of: "\\n{3,}", options: .regularExpression) != nil
}

var printableWithUnknownUnicode: String {
var result = ""
for c in unicodeScalars {
let ci = Int(c.value)
if let unicodeScalar = Unicode.Scalar(ci) {
if ci == 10 { // will keep newline char as it is
result += "\n"
} else {
// ascii char will be displayed as it is
// unknown (> 127) will be displayed as hex-encoded
result += unicodeScalar.escaped(asASCII: true)
}
}
}
return result
}
}
52 changes: 52 additions & 0 deletions Sources/BraveWallet/OriginInfoFavicon.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright 2023 The Brave Authors. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

import SwiftUI
import BraveCore

/// Displays the favicon for the OriginInfo, or the Brave Wallet logo for BraveWallet origin.
struct OriginInfoFavicon: View {

let originInfo: BraveWallet.OriginInfo

@ScaledMetric var faviconSize: CGFloat = 48
let maxFaviconSize: CGFloat = 96

var body: some View {
Group {
if originInfo.isBraveWalletOrigin {
Image("wallet-brave-icon", bundle: .module)
.resizable()
.aspectRatio(contentMode: .fit)
.padding(4)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color(.braveDisabled))
} else {
if let url = URL(string: originInfo.originSpec) {
FaviconReader(url: url) { image in
if let image = image {
Image(uiImage: image)
.resizable()
} else {
globeFavicon
}
}
} else {
globeFavicon
}
}
}
.frame(width: min(faviconSize, maxFaviconSize), height: min(faviconSize, maxFaviconSize))
.clipShape(RoundedRectangle(cornerRadius: 4, style: .continuous))
}

private var globeFavicon: some View {
Image(systemName: "globe")
.resizable()
.aspectRatio(contentMode: .fit)
.padding(8)
.background(Color(.braveDisabled))
}
}
2 changes: 1 addition & 1 deletion Sources/BraveWallet/Panels/RequestContainerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ struct RequestContainerView<DismissContent: ToolbarContent>: View {
onDismiss: onDismiss
)
case let .signMessage(requests):
SignatureRequestView(
SignMessageRequestContainerView(
requests: requests,
keyringStore: keyringStore,
cryptoStore: cryptoStore,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
// Copyright 2023 The Brave Authors. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

import SwiftUI
import BraveStrings
import BraveCore
import DesignSystem

/// View for showing `SignMessageRequest` for ethSiweData
struct SignInWithEthereumView: View {

let account: BraveWallet.AccountInfo
let originInfo: BraveWallet.OriginInfo
let message: BraveWallet.SIWEMessage
var action: (_ approved: Bool) -> Void

@State private var isShowingDetails: Bool = false
@Environment(\.sizeCategory) private var sizeCategory

var body: some View {
ScrollView {
VStack(spacing: 10) {
faviconAndOrigin

messageContainer

buttonsContainer
.padding(.top)
.opacity(sizeCategory.isAccessibilityCategory ? 0 : 1)
.accessibility(hidden: sizeCategory.isAccessibilityCategory)
}
.padding()
}
.overlay(alignment: .bottom) {
if sizeCategory.isAccessibilityCategory {
buttonsContainer
.frame(maxWidth: .infinity)
.padding(.top)
.background(
LinearGradient(
stops: [
.init(color: Color(.braveGroupedBackground).opacity(0), location: 0),
.init(color: Color(.braveGroupedBackground).opacity(1), location: 0.05),
.init(color: Color(.braveGroupedBackground).opacity(1), location: 1),
],
startPoint: .top,
endPoint: .bottom
)
.ignoresSafeArea()
.allowsHitTesting(false)
)
}
}
.background(Color(braveSystemName: .containerHighlight))
.navigationTitle(Strings.Wallet.signInWithBraveWallet)
}

private var faviconAndOrigin: some View {
VStack(spacing: 8) {
OriginInfoFavicon(originInfo: originInfo)
Text(verbatim: originInfo.eTldPlusOne)
Text(originInfo: originInfo)
.font(.caption)
.foregroundColor(Color(.braveLabel))
.multilineTextAlignment(.center)
}
}

private var messageContainer: some View {
VStack(alignment: .leading, spacing: 10) {
AddressView(address: account.address) {
AccountView(
address: account.address,
name: account.name,
blockieShape: .rectangle
kylehickinson marked this conversation as resolved.
Show resolved Hide resolved
)
}

// 'You are signing into xyz. Brave Wallet will share your wallet address with xyz.'
Text(String.localizedStringWithFormat(
Strings.Wallet.signInWithBraveWalletMessage,
originInfo.eTldPlusOne, originInfo.eTldPlusOne
))

NavigationLink(
destination: SignInWithEthereumDetailsView(
originInfo: originInfo,
message: message
)
) {
Text(Strings.Wallet.seeDetailsButtonTitle)
.fontWeight(.semibold)
.foregroundColor(Color(braveSystemName: .textInteractive))
.contentShape(Rectangle())
}

if let statement = message.statement, let resources = message.resources {
Divider()

VStack(alignment: .leading, spacing: 6) {
Text(Strings.Wallet.siweMessageLabel)
.font(.headline)
Text(verbatim: statement)
.textSelection(.enabled)
.font(.subheadline)
}

VStack(alignment: .leading, spacing: 6) {
Text(Strings.Wallet.siweResourcesLabel)
.font(.headline)
ForEach(resources.indices, id: \.self) { index in
if let resource = resources[safe: index] {
Text(verbatim: resource.absoluteString)
.textSelection(.enabled)
.font(.subheadline)
}
}
}
}
}
.padding()
.foregroundColor(Color(braveSystemName: .textPrimary))
.multilineTextAlignment(.leading)
.background(
Color(braveSystemName: .containerBackground)
.cornerRadius(12)
)
}

@ViewBuilder private var buttonsContainer: some View {
if sizeCategory.isAccessibilityCategory {
VStack {
buttons
}
} else {
HStack {
buttons
}
}
}

@ViewBuilder private var buttons: some View {
Button(action: { // cancel
action(false)
}) {
Text(Strings.cancelButtonTitle)
}
.buttonStyle(BraveOutlineButtonStyle(size: .large))
Button(action: { // approve
action(true)
}) {
Text(Strings.Wallet.siweSignInButtonTitle)
}
.buttonStyle(BraveFilledButtonStyle(size: .large))
}
}

/// The view pushed when user taps to view request details.
private struct SignInWithEthereumDetailsView: View {

let originInfo: BraveWallet.OriginInfo
let message: BraveWallet.SIWEMessage

var body: some View {
ScrollView {
LazyVStack {
LazyVStack {
Group { // Max view count on `LazyVStack`
detailRow(title: Strings.Wallet.siweOriginLabel, value: Text(originInfo: originInfo))
Divider()
detailRow(title: Strings.Wallet.siweAddressLabel, value: Text(verbatim: message.address))
if let statement = message.statement {
Divider()
detailRow(title: Strings.Wallet.siweStatementLabel, value: Text(verbatim: statement))
}
Divider()
detailRow(title: Strings.Wallet.siweURILabel, value: Text(verbatim: message.uri.absoluteString))
}
Group { // Max view count on `LazyVStack`
Divider()
detailRow(title: Strings.Wallet.siweVersionLabel, value: Text(verbatim: "\(message.version)"))
Divider()
detailRow(title: Strings.Wallet.siweChainIDLabel, value: Text(verbatim: "\(message.chainId)"))
Divider()
detailRow(title: Strings.Wallet.siweIssuedAtLabel, value: Text(verbatim: message.issuedAt))
if let expirationTime = message.expirationTime {
Divider()
detailRow(title: Strings.Wallet.siweExpirationTimeLabel, value: Text(verbatim: expirationTime))
}
Divider()
detailRow(title: Strings.Wallet.siweNonceLabel, value: Text(verbatim: message.nonce))
if let resources = message.resources {
Divider()
detailRow(
title: Strings.Wallet.siweResourcesLabel,
value: Text(verbatim: resources.map(\.absoluteString).joined(separator: "\n"))
)
}
}
}
.frame(maxWidth: .infinity)
}
.padding(16)
.multilineTextAlignment(.leading)
}
.navigationTitle(Strings.Wallet.siweDetailsTitle)
.navigationBarTitleDisplayMode(.inline)
.background(Color(braveSystemName: .containerHighlight))
}

private func detailRow(title: String, value: String) -> some View {
detailRow(title: title, value: Text(verbatim: value))
}

private func detailRow(title: String, value: Text) -> some View {
HStack(spacing: 12) {
Text(title)
.fontWeight(.semibold)
.foregroundColor(Color(braveSystemName: .textSecondary))
.frame(width: 100, alignment: .leading)
value
.foregroundColor(Color(braveSystemName: .textPrimary))
.textSelection(.enabled)
Spacer()
}
.padding(.vertical, 8)
.frame(maxWidth: .infinity)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ struct SignMessageErrorView: View {
.padding(.top, 16)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color(braveSystemName: .containerBackground).ignoresSafeArea())
.background(Color(braveSystemName: .containerHighlight).ignoresSafeArea())
.navigationTitle(Strings.Wallet.securityRiskDetectedTitle)
.navigationBarTitleDisplayMode(.inline)
}
Expand Down
Loading
Loading