Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

siwe fallback #77

Merged
merged 15 commits into from
Jun 20, 2024
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ let package = Package(
dependencies: [
.package(
url: "https://github.com/WalletConnect/WalletConnectSwiftV2",
.upToNextMinor(from: "1.19.1")
.revision("997be80a6f86be4ba340ac8121acde24fc428520")
llbartekll marked this conversation as resolved.
Show resolved Hide resolved
),
.package(
url: "https://github.com/WalletConnect/QRCode",
Expand Down
37 changes: 29 additions & 8 deletions Sample/Example.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

/* Begin PBXBuildFile section */
23F6FD03919B41DE98CAFCD3 /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = BD206AA550964C49AE94A3CA /* Sentry */; };
84733CCD2C1B2134001B2850 /* Atlantis in Frameworks */ = {isa = PBXBuildFile; productRef = 84733CCC2C1B2134001B2850 /* Atlantis */; };
84733CD22C1B21D4001B2850 /* SwiftMessages in Frameworks */ = {isa = PBXBuildFile; productRef = 84733CD12C1B21D4001B2850 /* SwiftMessages */; };
84871D572C1B1E58005C1B50 /* AlertPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84871D562C1B1E58005C1B50 /* AlertPresenter.swift */; };
84F3EFBB2BA86FA6005FCFAE /* DefaultCryptoProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F3EFBA2BA86FA6005FCFAE /* DefaultCryptoProvider.swift */; };
84F3EFBE2BA87760005FCFAE /* Web3 in Frameworks */ = {isa = PBXBuildFile; productRef = 84F3EFBD2BA87760005FCFAE /* Web3 */; };
84F3EFC02BA87760005FCFAE /* Web3ContractABI in Frameworks */ = {isa = PBXBuildFile; productRef = 84F3EFBF2BA87760005FCFAE /* Web3ContractABI */; };
Expand All @@ -18,7 +21,6 @@
CF25F3A42B40C7070030B3DC /* Web3ModalUI in Frameworks */ = {isa = PBXBuildFile; productRef = CF25F3A32B40C7070030B3DC /* Web3ModalUI */; };
CF3B9AD22ACDBA3A00984D53 /* Web3ModalUI in Frameworks */ = {isa = PBXBuildFile; productRef = CF3B9AD12ACDBA3A00984D53 /* Web3ModalUI */; };
CFA99B922AD0549F00EB5331 /* WCSocketFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFA99B912AD0549F00EB5331 /* WCSocketFactory.swift */; };
CFD6A70F2ADE8DE2002B402C /* Atlantis in Frameworks */ = {isa = PBXBuildFile; productRef = CFD6A70E2ADE8DE2002B402C /* Atlantis */; };
CFD720782A9CC60600636CAF /* ExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFD720772A9CC60600636CAF /* ExampleApp.swift */; };
CFD7207A2A9CC60600636CAF /* ComponentLibraryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFD720792A9CC60600636CAF /* ComponentLibraryView.swift */; };
CFD7207C2A9CC60700636CAF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CFD7207B2A9CC60700636CAF /* Assets.xcassets */; };
Expand All @@ -27,6 +29,7 @@
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
84871D562C1B1E58005C1B50 /* AlertPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertPresenter.swift; sourceTree = "<group>"; };
84F3EFBA2BA86FA6005FCFAE /* DefaultCryptoProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultCryptoProvider.swift; sourceTree = "<group>"; };
CF0BCCE42AB0886400A2866C /* ContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
CF533D032ADD411A00B3441C /* web3modal-swift */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = "web3modal-swift"; path = ..; sourceTree = "<group>"; };
Expand All @@ -48,15 +51,16 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
CFD6A70F2ADE8DE2002B402C /* Atlantis in Frameworks */,
84F3EFC02BA87760005FCFAE /* Web3ContractABI in Frameworks */,
CF25F3A42B40C7070030B3DC /* Web3ModalUI in Frameworks */,
CF3B9AD22ACDBA3A00984D53 /* Web3ModalUI in Frameworks */,
84F3EFBE2BA87760005FCFAE /* Web3 in Frameworks */,
84733CD22C1B21D4001B2850 /* SwiftMessages in Frameworks */,
CF25F3A22B40C7070030B3DC /* Web3Modal in Frameworks */,
84F3EFC22BA87760005FCFAE /* Web3PromiseKit in Frameworks */,
23F6FD03919B41DE98CAFCD3 /* Sentry in Frameworks */,
84FEB1382C0DAE210018CB53 /* Starscream in Frameworks */,
84733CCD2C1B2134001B2850 /* Atlantis in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -96,6 +100,7 @@
CFD7207D2A9CC60700636CAF /* Example.entitlements */,
CFD7207E2A9CC60700636CAF /* Preview Content */,
CFEAAF092B6C0B3A001565F5 /* InputConfig.swift */,
84871D562C1B1E58005C1B50 /* AlertPresenter.swift */,
);
path = Example;
sourceTree = "<group>";
Expand Down Expand Up @@ -134,14 +139,15 @@
name = Example;
packageProductDependencies = (
CF3B9AD12ACDBA3A00984D53 /* Web3ModalUI */,
CFD6A70E2ADE8DE2002B402C /* Atlantis */,
BD206AA550964C49AE94A3CA /* Sentry */,
CF25F3A12B40C7070030B3DC /* Web3Modal */,
CF25F3A32B40C7070030B3DC /* Web3ModalUI */,
84F3EFBD2BA87760005FCFAE /* Web3 */,
84F3EFBF2BA87760005FCFAE /* Web3ContractABI */,
84F3EFC12BA87760005FCFAE /* Web3PromiseKit */,
84FEB1372C0DAE210018CB53 /* Starscream */,
84733CCC2C1B2134001B2850 /* Atlantis */,
84733CD12C1B21D4001B2850 /* SwiftMessages */,
);
productName = Example;
productReference = CFD720742A9CC60600636CAF /* Example.app */;
Expand Down Expand Up @@ -177,6 +183,7 @@
F4A0329B6CFF49E682D3DFE7 /* XCRemoteSwiftPackageReference "sentry-cocoa" */,
84D9CCC12B9708E4001EDEE3 /* XCRemoteSwiftPackageReference "Starscream" */,
84F3EFBC2BA87760005FCFAE /* XCRemoteSwiftPackageReference "Web3" */,
84733CD02C1B21D4001B2850 /* XCRemoteSwiftPackageReference "SwiftMessages" */,
);
productRefGroup = CFD720752A9CC60600636CAF /* Products */;
projectDirPath = "";
Expand Down Expand Up @@ -223,6 +230,7 @@
buildActionMask = 2147483647;
files = (
CFEAAF0A2B6C0B3A001565F5 /* InputConfig.swift in Sources */,
84871D572C1B1E58005C1B50 /* AlertPresenter.swift in Sources */,
CFD7207A2A9CC60600636CAF /* ComponentLibraryView.swift in Sources */,
CFD720782A9CC60600636CAF /* ExampleApp.swift in Sources */,
CF0BCCE52AB0886400A2866C /* ContentView.swift in Sources */,
Expand Down Expand Up @@ -483,6 +491,14 @@
/* End XCLocalSwiftPackageReference section */

/* Begin XCRemoteSwiftPackageReference section */
84733CD02C1B21D4001B2850 /* XCRemoteSwiftPackageReference "SwiftMessages" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/SwiftKickMobile/SwiftMessages";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 10.0.0;
};
};
84D9CCC12B9708E4001EDEE3 /* XCRemoteSwiftPackageReference "Starscream" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/daltoniam/Starscream";
Expand Down Expand Up @@ -518,6 +534,16 @@
/* End XCRemoteSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
84733CCC2C1B2134001B2850 /* Atlantis */ = {
isa = XCSwiftPackageProductDependency;
package = CFD6A70D2ADE8DE2002B402C /* XCRemoteSwiftPackageReference "atlantis" */;
productName = Atlantis;
};
84733CD12C1B21D4001B2850 /* SwiftMessages */ = {
isa = XCSwiftPackageProductDependency;
package = 84733CD02C1B21D4001B2850 /* XCRemoteSwiftPackageReference "SwiftMessages" */;
productName = SwiftMessages;
};
84F3EFBD2BA87760005FCFAE /* Web3 */ = {
isa = XCSwiftPackageProductDependency;
package = 84F3EFBC2BA87760005FCFAE /* XCRemoteSwiftPackageReference "Web3" */;
Expand Down Expand Up @@ -555,11 +581,6 @@
isa = XCSwiftPackageProductDependency;
productName = Web3ModalUI;
};
CFD6A70E2ADE8DE2002B402C /* Atlantis */ = {
isa = XCSwiftPackageProductDependency;
package = CFD6A70D2ADE8DE2002B402C /* XCRemoteSwiftPackageReference "atlantis" */;
productName = Atlantis;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = CFD7206C2A9CC60600636CAF /* Project object */;
Expand Down
35 changes: 35 additions & 0 deletions Sample/Example/AlertPresenter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import Foundation
import SwiftMessages
import UIKit

struct AlertPresenter {
enum MessageType {
case warning
case error
case info
case success
}

static func present(message: String, type: AlertPresenter.MessageType) {
DispatchQueue.main.async {
let view = MessageView.viewFromNib(layout: .cardView)
switch type {
case .warning:
view.configureTheme(.warning, iconStyle: .subtle)
case .error:
view.configureTheme(.error, iconStyle: .subtle)
case .info:
view.configureTheme(.info, iconStyle: .subtle)
case .success:
view.configureTheme(.success, iconStyle: .subtle)
}
view.button?.isHidden = true
view.layoutMarginAdditions = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
view.configureContent(title: "", body: message)
var config = SwiftMessages.Config()
config.presentationStyle = .top
config.duration = .seconds(seconds: 1.5)
SwiftMessages.show(config: config, view: view)
}
}
}
19 changes: 19 additions & 0 deletions Sample/Example/ExampleApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,25 @@ class ExampleApp: App {
Sign.instance.setLogging(level: .debug)
Networking.instance.setLogging(level: .debug)
Relay.instance.setLogging(level: .debug)

Web3Modal.instance.authResponsePublisher.sink { (id: RPCID, result: Result<(Session?, [Cacao]), AuthError>) in
switch result {
case .success((_, _)):
AlertPresenter.present(message: "User authenticated", type: .success)
case .failure(let error):
AlertPresenter.present(message: "User authentication error: \(error)", type: .error)

}
}.store(in: &disposeBag)

Web3Modal.instance.SIWEAuthenticationPublisher.sink { result in
switch result {
case .success((let message, let signature)):
AlertPresenter.present(message: "User authenticated", type: .success)
case .failure(let error):
AlertPresenter.present(message: "User authentication error: \(error)", type: .error)
}
}.store(in: &disposeBag)
}

var body: some Scene {
Expand Down
3 changes: 2 additions & 1 deletion Sources/Web3Modal/Core/Web3Modal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ public class Web3Modal {
store: store,
w3mApiInteractor: w3mApiInteractor,
signInteractor: signInteractor,
blockchainApiInteractor: blockchainApiInteractor
blockchainApiInteractor: blockchainApiInteractor,
supportsAuthenticatedSession: (config.authRequestParams != nil)
)

Task {
Expand Down
6 changes: 6 additions & 0 deletions Sources/Web3Modal/Core/Web3ModalClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ public class Web3ModalClient {
return analyticsService.isAnalyticsEnabled
}

public var SIWEAuthenticationPublisher: AnyPublisher<Result<(message: String, signature: String), SIWEAuthenticationError>, Never> {
return SIWEAuthenticationPublisherSubject.eraseToAnyPublisher()
}

internal let SIWEAuthenticationPublisherSubject = PassthroughSubject<Result<(message: String, signature: String), SIWEAuthenticationError>, Never>()

// MARK: - Private Properties

private let signClient: SignClient
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,58 @@ struct WalletDetailView: View {

walletInfo()

if viewModel.wallet.isInstalled == true {
if viewModel.wallet.isInstalled == true && !store.SIWEFallbackState {
copyLink()
}

if
viewModel.preferredPlatform == .mobile,
viewModel.wallet.isInstalled != true,
viewModel.wallet.appStore != nil
{
appStoreRow()
}

if store.SIWEFallbackState {
siweFallbackButtons()
}
}
.padding(.horizontal, Spacing.s)
.padding(.bottom, Spacing.xl + 17)
}


private func siweFallbackButtons() -> some View {
HStack {
Button(action: {
Task {
try await viewModel.cancel()
}
}) {
Text("Cancel")
.foregroundColor(.black)
.padding()
.frame(maxWidth: .infinity)
.background(Color.gray.opacity(0.2))
.cornerRadius(8)
}

Button(action: {
Task {
try await viewModel.signSIWE()
}
}) {
Text("Sign")
.foregroundColor(.white)
.padding()
.frame(maxWidth: .infinity)
.background(Color.blue)
.cornerRadius(8)
}
}
.padding(.horizontal, 20)
.padding(.bottom, 20)
}

private func picker() -> some View {
W3MPicker(
WalletDetailViewModel.Platform.allCases,
Expand Down Expand Up @@ -149,28 +185,30 @@ struct WalletDetailView: View {
walletImage()
.padding(.top, 40)
.padding(.bottom, Spacing.xl)
Text(store.retryShown ? "Connection declined" : "Continue in \(viewModel.wallet.name)")

Text(store.SIWEFallbackState ? "Web3Modal needs to connect to your wallet." : (store.retryShown ? "Connection declined" : "Continue in \(viewModel.wallet.name)"))
.font(.paragraph600)
.foregroundColor(store.retryShown ? .Error100 : .Foreground100)
.padding(.bottom, Spacing.xs)

Text(
store.retryShown
? "Connection can be declined if a previous request is still active"
: viewModel.preferredPlatform == .browser ? "Open and continue in a new browser tab" : "Accept connection request in the wallet"
store.SIWEFallbackState
? "Sign this message to prove you own this wallet and proceed. Cancelling will disconnect you."
: (store.retryShown
? "Connection can be declined if a previous request is still active"
: viewModel.preferredPlatform == .browser ? "Open and continue in a new browser tab" : "Accept connection request in the wallet")
)
.font(.small500)
.foregroundColor(.Foreground200)
.multilineTextAlignment(.center)
.padding(.bottom, Spacing.l)

if store.retryShown || viewModel.preferredPlatform == .browser {
retryButton()
}
}
}

func appStoreRow() -> some View {
HStack(spacing: 0) {
Text("Don't have \(viewModel.wallet.name)?")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ class WalletDetailViewModel: ObservableObject {
let router: Router
let store: Store
let signInteractor: SignInteractor



@Published var preferredPlatform: Platform = .mobile

private var disposeBag = Set<AnyCancellable>()
Expand Down Expand Up @@ -55,7 +56,43 @@ class WalletDetailViewModel: ObservableObject {
}
.store(in: &disposeBag)
}


func cancel() async throws {
store.SIWEFallbackState = false
guard let topic = store.session?.topic else { return }
try await Web3Modal.instance.disconnect(topic: topic)
}

func signSIWE() async throws {
DispatchQueue.main.async { [weak self] in
self?.store.SIWEFallbackState = false
}
guard let account = store.account?.account(),
let authRequestParams = Web3Modal.config.authRequestParams,
let topic = Web3Modal.instance.getSessions().first?.topic,
let chain = Web3Modal.instance.getSelectedChain(),
let blockchain = Blockchain(namespace: chain.chainNamespace, reference: chain.chainReference)
else { return }

let authPayload = AuthPayload(requestParams: authRequestParams, iat: DefaultIATProvider().iat)
let siweMessage = try Sign.instance.formatAuthMessage(payload: authPayload, account: account)


let rpcRequest = try Request(topic: topic, method: "personal_sign", params: AnyCodable([siweMessage, account.address]), chainId: blockchain)
try await Sign.instance.request(params: rpcRequest)

store.siweRequestId = rpcRequest.id
store.siweMessage = siweMessage

DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.navigateToDeepLink(
wallet: self.wallet,
preferBrowser: preferredPlatform == .browser
)
}
}

func handle(_ event: Event) {
switch event {
case .didTapCopy:
Expand Down
2 changes: 1 addition & 1 deletion Sources/Web3Modal/Sheets/Web3ModalView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ struct Web3ModalView_Previews: PreviewProvider {
store: Store(),
w3mApiInteractor: W3MAPIInteractor(store: Store()),
signInteractor: SignInteractor(store: Store()),
blockchainApiInteractor: BlockchainAPIInteractor(store: Store())
blockchainApiInteractor: BlockchainAPIInteractor(store: Store()), supportsAuthenticatedSession: false
))
.previewLayout(.sizeThatFits)
}
Expand Down
Loading
Loading