diff --git a/.env.Showcase b/.env.Showcase
new file mode 100644
index 000000000..c08277921
--- /dev/null
+++ b/.env.Showcase
@@ -0,0 +1,3 @@
+SCHEME = "Showcase"
+APP_IDENTIFIER = "com.walletconnect.chat"
+APPLE_ID = "1634760092"
\ No newline at end of file
diff --git a/.env.WalletApp b/.env.WalletApp
new file mode 100644
index 000000000..5ac0d15e7
--- /dev/null
+++ b/.env.WalletApp
@@ -0,0 +1,3 @@
+SCHEME = "WalletApp"
+APP_IDENTIFIER = "com.walletconnect.walletapp"
+APPLE_ID = "1667351690"
\ No newline at end of file
diff --git a/.github/actions/ci/action.yml b/.github/actions/ci/action.yml
index e3f1b5e5f..1c105f783 100644
--- a/.github/actions/ci/action.yml
+++ b/.github/actions/ci/action.yml
@@ -19,13 +19,7 @@ runs:
- name: Run tests
if: inputs.type == 'unit-tests'
shell: bash
- run: "xcodebuild \
- -project Example/ExampleApp.xcodeproj \
- -scheme WalletConnect \
- -clonedSourcePackagesDirPath SourcePackagesCache \
- -destination 'platform=iOS Simulator,name=iPhone 13' \
- -derivedDataPath DerivedDataCache \
- test"
+ run: make unit_tests
# Integration tests
- name: Run integration tests
@@ -34,46 +28,23 @@ runs:
env:
RELAY_ENDPOINT: ${{ inputs.relay-endpoint }}
PROJECT_ID: ${{ inputs.project-id }}
- run: "xcodebuild \
- -project Example/ExampleApp.xcodeproj \
- -scheme IntegrationTests \
- -clonedSourcePackagesDirPath SourcePackagesCache \
- -destination 'platform=iOS Simulator,name=iPhone 13' \
- -derivedDataPath DerivedDataCache \
- RELAY_HOST='$RELAY_ENDPOINT' \
- PROJECT_ID='$PROJECT_ID' \
- test"
+ run: make integration_tests RELAY_HOST=$RELAY_ENDPOINT PROJECT_ID=$PROJECT_ID
# Wallet build
- name: Build Example Wallet
if: inputs.type == 'build-example-wallet'
shell: bash
- run: "xcodebuild \
- -project Example/ExampleApp.xcodeproj \
- -scheme Wallet \
- -clonedSourcePackagesDirPath SourcePackagesCache \
- -destination 'platform=iOS Simulator,name=iPhone 13' \
- -derivedDataPath DerivedDataCache"
+ run: make build_wallet
# DApp build
- name: Build Example Dapp
if: inputs.type == 'build-example-dapp'
shell: bash
- run: "xcodebuild \
- -project Example/ExampleApp.xcodeproj \
- -scheme DApp \
- -clonedSourcePackagesDirPath SourcePackagesCache \
- -destination 'platform=iOS Simulator,name=iPhone 13' \
- -derivedDataPath DerivedDataCache"
+ run: make build_dapp
# UI tests
- name: UI Tests
if: inputs.type == 'ui-tests'
shell: bash
- run: "xcodebuild \
- -project Example/ExampleApp.xcodeproj \
- -scheme UITests \
- -derivedDataPath DerivedDataCache \
- -clonedSourcePackagesDirPath SourcePackagesCache \
- -destination 'platform=iOS Simulator,name=iPhone 13' test"
+ run: make ui_tests
continue-on-error: true
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a71b4ce7e..e1846f591 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -42,37 +42,9 @@ jobs:
- name: Resolve Dependencies
shell: bash
- run: "
- xcodebuild -resolvePackageDependencies -project Example/ExampleApp.xcodeproj -scheme DApp -clonedSourcePackagesDirPath SourcePackagesCache -derivedDataPath DerivedDataCache -destination 'platform=iOS Simulator,name=iPhone 13'; \
- xcodebuild -resolvePackageDependencies -project Example/ExampleApp.xcodeproj -scheme WalletConnect -clonedSourcePackagesDirPath SourcePackagesCache -derivedDataPath DerivedDataCache -destination 'platform=iOS Simulator,name=iPhone 13'"
+ run: make resolve_packages
- uses: ./.github/actions/ci
with:
type: ${{ matrix.test-type }}
project-id: ${{ secrets.PROJECT_ID }}
-
- test-ui:
- if: github.ref == 'refs/heads/main'
- runs-on: macos-latest
- strategy:
- matrix:
- test-type: [ui-tests]
-
- steps:
- - uses: actions/checkout@v2
-
- - name: Setup Xcode Version
- uses: maxim-lobanov/setup-xcode@v1
-
- - uses: actions/cache@v2
- with:
- path: |
- .build
- SourcePackagesCache
- key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }}
- restore-keys: |
- ${{ runner.os }}-spm-
-
- - uses: ./.github/actions/ci
- with:
- type: ${{ matrix.test-type }}
diff --git a/.gitignore b/.gitignore
index dad29299f..64e06f6b1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,12 +14,18 @@ Package.resolved
/*.xcodeproj
# Fastlane
-*/fastlane/report.xml
-*/fastlane/Preview.html
-*/fastlane/screenshots/**/*.png
-*/fastlane/test_output
-*/fastlane/README.md
+fastlane/report.xml
+fastlane/Preview.html
+fastlane/screenshots/**/*.png
+fastlane/test_output
+fastlane/README.md
# Configuration
Configuration.xcconfig
+# Cache
+SourcePackagesCache
+DerivedDataCache
+
+# Artifacts
+*.ipa
\ No newline at end of file
diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectUtilsTests.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectUtilsTests.xcscheme
deleted file mode 100644
index 3cf5ef95b..000000000
--- a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectUtilsTests.xcscheme
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Example/DApp/Info.plist b/Example/DApp/Info.plist
index 07ba0e705..ec622dae1 100644
--- a/Example/DApp/Info.plist
+++ b/Example/DApp/Info.plist
@@ -2,10 +2,14 @@
- PROJECT_ID
- $(PROJECT_ID)
+ CFBundleVersion
+ 7
+ CFBundleShortVersionString
+ $(MARKETING_VERSION)
ITSAppUsesNonExemptEncryption
+ PROJECT_ID
+ $(PROJECT_ID)
UIApplicationSceneManifest
UIApplicationSupportsMultipleScenes
diff --git a/Example/DApp/Sign/Accounts/AccountsViewController.swift b/Example/DApp/Sign/Accounts/AccountsViewController.swift
index 8a7d256fa..d08e5287c 100644
--- a/Example/DApp/Sign/Accounts/AccountsViewController.swift
+++ b/Example/DApp/Sign/Accounts/AccountsViewController.swift
@@ -1,5 +1,7 @@
import UIKit
import WalletConnectSign
+import WalletConnectPush
+import Combine
struct AccountDetails {
let chain: String
@@ -12,7 +14,9 @@ final class AccountsViewController: UIViewController, UITableViewDataSource, UIT
let session: Session
var accountsDetails: [AccountDetails] = []
var onDisconnect: (() -> Void)?
+ var pushSubscription: PushSubscription?
+ private var publishers = [AnyCancellable]()
private let accountsView: AccountsView = {
AccountsView()
}()
@@ -34,12 +38,21 @@ final class AccountsViewController: UIViewController, UITableViewDataSource, UIT
super.viewDidLoad()
navigationItem.title = "Accounts"
+
navigationItem.rightBarButtonItem = UIBarButtonItem(
title: "Disconnect",
style: .plain,
target: self,
action: #selector(disconnect)
)
+
+ navigationItem.leftBarButtonItem = UIBarButtonItem(
+ title: "Push Test",
+ style: .plain,
+ target: self,
+ action: #selector(pushTest)
+ )
+
accountsView.tableView.dataSource = self
accountsView.tableView.delegate = self
session.namespaces.values.forEach { namespace in
@@ -49,6 +62,27 @@ final class AccountsViewController: UIViewController, UITableViewDataSource, UIT
}
}
+ func proposePushSubscription() {
+ let account = session.namespaces.values.first!.accounts.first!
+
+ Task(priority: .high){ try! await Push.dapp.request(account: account, topic: session.pairingTopic)}
+ Push.dapp.responsePublisher.sink { (id: RPCID, result: Result) in
+ switch result {
+ case .success(let subscription):
+ self.pushSubscription = subscription
+ case .failure(let error):
+ print(error)
+ }
+ }.store(in: &publishers)
+ }
+
+ @objc
+ private func pushTest() {
+ guard let pushTopic = pushSubscription?.topic else {return}
+ let message = PushMessage(title: "Push Message", body: "He,y this is a message from the swift client", icon: "", url: "")
+ Task(priority: .userInitiated) { try! await Push.dapp.notify(topic: pushTopic, message: message) }
+ }
+
@objc
private func disconnect() {
Task {
diff --git a/Example/DApp/Sign/Connect/ConnectViewController.swift b/Example/DApp/Sign/Connect/ConnectViewController.swift
index 3e3da5422..c7fc8bcbe 100644
--- a/Example/DApp/Sign/Connect/ConnectViewController.swift
+++ b/Example/DApp/Sign/Connect/ConnectViewController.swift
@@ -91,7 +91,7 @@ class ConnectViewController: UIViewController, UITableViewDataSource, UITableVie
"eth_sendTransaction",
"personal_sign",
"eth_signTypedData"
- ], events: [], extensions: nil
+ ], events: []
),
"solana": ProposalNamespace(
chains: [
@@ -100,7 +100,7 @@ class ConnectViewController: UIViewController, UITableViewDataSource, UITableVie
methods: [
"solana_signMessage",
"solana_signTransaction"
- ], events: [], extensions: nil
+ ], events: []
)
]
Task {
diff --git a/Example/DApp/Sign/SelectChain/SelectChainViewController.swift b/Example/DApp/Sign/SelectChain/SelectChainViewController.swift
index be310e2f9..83a73f0a8 100644
--- a/Example/DApp/Sign/SelectChain/SelectChainViewController.swift
+++ b/Example/DApp/Sign/SelectChain/SelectChainViewController.swift
@@ -46,7 +46,7 @@ class SelectChainViewController: UIViewController, UITableViewDataSource {
"eth_sendTransaction",
"personal_sign",
"eth_signTypedData"
- ], events: [], extensions: nil
+ ], events: []
),
"solana": ProposalNamespace(
chains: [
@@ -55,7 +55,7 @@ class SelectChainViewController: UIViewController, UITableViewDataSource {
methods: [
"solana_signMessage",
"solana_signTransaction"
- ], events: [], extensions: nil
+ ], events: []
)
]
Task {
diff --git a/Example/DApp/Sign/SignCoordinator.swift b/Example/DApp/Sign/SignCoordinator.swift
index 51e70078e..55344a902 100644
--- a/Example/DApp/Sign/SignCoordinator.swift
+++ b/Example/DApp/Sign/SignCoordinator.swift
@@ -48,11 +48,12 @@ final class SignCoordinator {
Sign.instance.sessionSettlePublisher
.receive(on: DispatchQueue.main)
.sink { [unowned self] session in
- showAccountsScreen(session)
+ let vc = showAccountsScreen(session)
+ vc.proposePushSubscription()
}.store(in: &publishers)
if let session = Sign.instance.getSessions().first {
- showAccountsScreen(session)
+ _ = showAccountsScreen(session)
} else {
showSelectChainScreen()
}
@@ -63,13 +64,14 @@ final class SignCoordinator {
navigationController.viewControllers = [controller]
}
- private func showAccountsScreen(_ session: Session) {
+ private func showAccountsScreen(_ session: Session) -> AccountsViewController {
let controller = AccountsViewController(session: session)
controller.onDisconnect = { [unowned self] in
showSelectChainScreen()
}
navigationController.presentedViewController?.dismiss(animated: false)
navigationController.viewControllers = [controller]
+ return controller
}
private func presentResponse(for response: Response) {
diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj
index d5d883e65..dd044d0bc 100644
--- a/Example/ExampleApp.xcodeproj/project.pbxproj
+++ b/Example/ExampleApp.xcodeproj/project.pbxproj
@@ -57,12 +57,26 @@
84CE645527A29D4D00142511 /* ResponseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE645427A29D4C00142511 /* ResponseViewController.swift */; };
84CEC64628D89D6B00D081A8 /* PairingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CEC64528D89D6B00D081A8 /* PairingTests.swift */; };
84D2A66628A4F51E0088AE09 /* AuthTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D2A66528A4F51E0088AE09 /* AuthTests.swift */; };
+ 84DB38F32983CDAE00BFEE37 /* PushRegisterer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DB38F22983CDAE00BFEE37 /* PushRegisterer.swift */; };
84DDB4ED28ABB663003D66ED /* WalletConnectAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 84DDB4EC28ABB663003D66ED /* WalletConnectAuth */; };
+ 84E6B84A29787A8000428BAF /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E6B84929787A8000428BAF /* NotificationService.swift */; };
+ 84E6B84E29787A8000428BAF /* PNDecryptionService.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 84E6B84729787A8000428BAF /* PNDecryptionService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
+ 84E6B85429787AAE00428BAF /* WalletConnectPush in Frameworks */ = {isa = PBXBuildFile; productRef = 84E6B85329787AAE00428BAF /* WalletConnectPush */; };
+ 84E6B8582981624F00428BAF /* PushRequestModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E6B8572981624F00428BAF /* PushRequestModule.swift */; };
+ 84E6B85B298162EF00428BAF /* PushRequestPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E6B85A298162EF00428BAF /* PushRequestPresenter.swift */; };
+ 84E6B85D298162F700428BAF /* PushRequestRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E6B85C298162F700428BAF /* PushRequestRouter.swift */; };
+ 84E6B85F2981630000428BAF /* PushRequestInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E6B85E2981630000428BAF /* PushRequestInteractor.swift */; };
+ 84E6B8612981630C00428BAF /* PushRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E6B8602981630C00428BAF /* PushRequestView.swift */; };
+ 84E6B86329816A7900428BAF /* WalletConnectPush in Frameworks */ = {isa = PBXBuildFile; productRef = 84E6B86229816A7900428BAF /* WalletConnectPush */; };
+ 84E6B8652981720400428BAF /* WalletConnectPush in Frameworks */ = {isa = PBXBuildFile; productRef = 84E6B8642981720400428BAF /* WalletConnectPush */; };
84F568C2279582D200D0A289 /* Signer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C1279582D200D0A289 /* Signer.swift */; };
84F568C42795832A00D0A289 /* EthereumTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C32795832A00D0A289 /* EthereumTransaction.swift */; };
84FE684628ACDB4700C893FF /* RequestParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FE684528ACDB4700C893FF /* RequestParams.swift */; };
A50C036528AAD32200FE72D3 /* ClientDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50C036428AAD32200FE72D3 /* ClientDelegate.swift */; };
A50F3946288005B200064555 /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50F3945288005B200064555 /* Types.swift */; };
+ A518A98729683FB60035247E /* Web3InboxViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A518A98429683FB60035247E /* Web3InboxViewController.swift */; };
+ A518A98829683FB60035247E /* Web3InboxModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = A518A98529683FB60035247E /* Web3InboxModule.swift */; };
+ A518A98929683FB60035247E /* Web3InboxRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A518A98629683FB60035247E /* Web3InboxRouter.swift */; };
A518B31428E33A6500A2CE93 /* InputConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A518B31328E33A6500A2CE93 /* InputConfig.swift */; };
A51AC0D928E436A3001BACF9 /* InputConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51AC0D828E436A3001BACF9 /* InputConfig.swift */; };
A51AC0DD28E43727001BACF9 /* InputConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51AC0DB28E436E6001BACF9 /* InputConfig.swift */; };
@@ -93,6 +107,7 @@
A5629AE828772A0100094373 /* InviteViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5629AE728772A0100094373 /* InviteViewModel.swift */; };
A5629AEA2877F2D600094373 /* WalletConnectChat in Frameworks */ = {isa = PBXBuildFile; productRef = A5629AE92877F2D600094373 /* WalletConnectChat */; };
A5629AF22877F75100094373 /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = A5629AF12877F75100094373 /* Starscream */; };
+ A574B3582964560C00C2BB91 /* Web3Inbox in Frameworks */ = {isa = PBXBuildFile; productRef = A574B3572964560C00C2BB91 /* Web3Inbox */; };
A578FA322873036400AA7720 /* InputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A578FA312873036400AA7720 /* InputView.swift */; };
A578FA35287304A300AA7720 /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = A578FA34287304A300AA7720 /* Color.swift */; };
A578FA372873D8EE00AA7720 /* UIColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = A578FA362873D8EE00AA7720 /* UIColor.swift */; };
@@ -269,6 +284,13 @@
remoteGlobalIDString = 84B767752954554A00E92316;
remoteInfo = PushDecryptionService;
};
+ 84E6B84C29787A8000428BAF /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 764E1D3426F8D3FC00A1FB15 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 84E6B84629787A8000428BAF;
+ remoteInfo = PNDecryptionService;
+ };
A5A4FC7D2840C5D400BBEC1E /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 764E1D3426F8D3FC00A1FB15 /* Project object */;
@@ -297,6 +319,17 @@
name = "Embed Foundation Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
+ 84E6B85229787A8000428BAF /* Embed Foundation Extensions */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 13;
+ files = (
+ 84E6B84E29787A8000428BAF /* PNDecryptionService.appex in Embed Foundation Extensions */,
+ );
+ name = "Embed Foundation Extensions";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
@@ -332,6 +365,8 @@
845B8D8B2934B36C0084A966 /* Account.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Account.swift; sourceTree = ""; };
8460DCFB274F98A10081F94C /* RequestViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestViewController.swift; sourceTree = ""; };
8485617E295307C20064877B /* PushNotificationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushNotificationTests.swift; sourceTree = ""; };
+ 849A4F18298281E300E61ACE /* WalletAppRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WalletAppRelease.entitlements; sourceTree = ""; };
+ 849A4F19298281F100E61ACE /* PNDecryptionServiceRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = PNDecryptionServiceRelease.entitlements; sourceTree = ""; };
849D7A92292E2169006A2BD4 /* PushTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushTests.swift; sourceTree = ""; };
84AA01DA28CF0CD7005D48D8 /* XCTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCTest.swift; sourceTree = ""; };
84B767762954554A00E92316 /* PushDecryptionService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = PushDecryptionService.appex; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -353,11 +388,25 @@
84CE645427A29D4C00142511 /* ResponseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResponseViewController.swift; sourceTree = ""; };
84CEC64528D89D6B00D081A8 /* PairingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PairingTests.swift; sourceTree = ""; };
84D2A66528A4F51E0088AE09 /* AuthTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthTests.swift; sourceTree = ""; };
+ 84DB38F029828A7C00BFEE37 /* WalletApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WalletApp.entitlements; sourceTree = ""; };
+ 84DB38F129828A7F00BFEE37 /* PNDecryptionService.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = PNDecryptionService.entitlements; sourceTree = ""; };
+ 84DB38F22983CDAE00BFEE37 /* PushRegisterer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushRegisterer.swift; sourceTree = ""; };
+ 84E6B84729787A8000428BAF /* PNDecryptionService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = PNDecryptionService.appex; sourceTree = BUILT_PRODUCTS_DIR; };
+ 84E6B84929787A8000428BAF /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; };
+ 84E6B84B29787A8000428BAF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 84E6B8572981624F00428BAF /* PushRequestModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushRequestModule.swift; sourceTree = ""; };
+ 84E6B85A298162EF00428BAF /* PushRequestPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushRequestPresenter.swift; sourceTree = ""; };
+ 84E6B85C298162F700428BAF /* PushRequestRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushRequestRouter.swift; sourceTree = ""; };
+ 84E6B85E2981630000428BAF /* PushRequestInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushRequestInteractor.swift; sourceTree = ""; };
+ 84E6B8602981630C00428BAF /* PushRequestView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushRequestView.swift; sourceTree = ""; };
84F568C1279582D200D0A289 /* Signer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Signer.swift; sourceTree = ""; };
84F568C32795832A00D0A289 /* EthereumTransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EthereumTransaction.swift; sourceTree = ""; };
84FE684528ACDB4700C893FF /* RequestParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestParams.swift; sourceTree = ""; };
A50C036428AAD32200FE72D3 /* ClientDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClientDelegate.swift; sourceTree = ""; };
A50F3945288005B200064555 /* Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Types.swift; sourceTree = ""; };
+ A518A98429683FB60035247E /* Web3InboxViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Web3InboxViewController.swift; sourceTree = ""; };
+ A518A98529683FB60035247E /* Web3InboxModule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Web3InboxModule.swift; sourceTree = ""; };
+ A518A98629683FB60035247E /* Web3InboxRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Web3InboxRouter.swift; sourceTree = ""; };
A518B31328E33A6500A2CE93 /* InputConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputConfig.swift; sourceTree = ""; };
A51AC0D828E436A3001BACF9 /* InputConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputConfig.swift; sourceTree = ""; };
A51AC0DB28E436E6001BACF9 /* InputConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputConfig.swift; sourceTree = ""; };
@@ -559,11 +608,20 @@
files = (
8448F1D427E4726F0000B866 /* WalletConnect in Frameworks */,
A54195A52934E83F0035AD19 /* Web3 in Frameworks */,
+ 84E6B8652981720400428BAF /* WalletConnectPush in Frameworks */,
A5D85228286333E300DAF5C3 /* Starscream in Frameworks */,
A5BB7FA328B6A50400707FC6 /* WalletConnectAuth in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 84E6B84429787A8000428BAF /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 84E6B86329816A7900428BAF /* WalletConnectPush in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
A58E7CE528729F550082D443 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -571,6 +629,7 @@
A5629AEA2877F2D600094373 /* WalletConnectChat in Frameworks */,
A59FAEC928B7B93A002BB66F /* Web3 in Frameworks */,
A5629AF22877F75100094373 /* Starscream in Frameworks */,
+ A574B3582964560C00C2BB91 /* Web3Inbox in Frameworks */,
A59F877628B5462900A9CD80 /* WalletConnectAuth in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -604,6 +663,7 @@
C5133A78294125CC00A8314C /* Web3 in Frameworks */,
C5B2F7052970573D000DBA0E /* SolanaSwift in Frameworks */,
C55D349929630D440004314A /* Web3Wallet in Frameworks */,
+ 84E6B85429787AAE00428BAF /* WalletConnectPush in Frameworks */,
C56EE255293F569A004840D1 /* Starscream in Frameworks */,
C56EE27B293F56F8004840D1 /* WalletConnectAuth in Frameworks */,
);
@@ -662,6 +722,7 @@
A58E7CE928729F550082D443 /* Showcase */,
84B767772954554A00E92316 /* PushDecryptionService */,
C56EE21C293F55ED004840D1 /* WalletApp */,
+ 84E6B84829787A8000428BAF /* PNDecryptionService */,
764E1D3D26F8D3FC00A1FB15 /* Products */,
764E1D5326F8DAC800A1FB15 /* Frameworks */,
764E1D5626F8DB6000A1FB15 /* WalletConnectSwiftV2 */,
@@ -678,6 +739,7 @@
A58E7CE828729F550082D443 /* Showcase.app */,
84B767762954554A00E92316 /* PushDecryptionService.appex */,
C56EE21B293F55ED004840D1 /* WalletApp.app */,
+ 84E6B84729787A8000428BAF /* PNDecryptionService.appex */,
);
name = Products;
sourceTree = "";
@@ -825,6 +887,29 @@
path = Auth;
sourceTree = "";
};
+ 84E6B84829787A8000428BAF /* PNDecryptionService */ = {
+ isa = PBXGroup;
+ children = (
+ 84DB38F129828A7F00BFEE37 /* PNDecryptionService.entitlements */,
+ 849A4F19298281F100E61ACE /* PNDecryptionServiceRelease.entitlements */,
+ 84E6B84929787A8000428BAF /* NotificationService.swift */,
+ 84E6B84B29787A8000428BAF /* Info.plist */,
+ );
+ path = PNDecryptionService;
+ sourceTree = "";
+ };
+ 84E6B8592981625A00428BAF /* PushRequest */ = {
+ isa = PBXGroup;
+ children = (
+ 84E6B8572981624F00428BAF /* PushRequestModule.swift */,
+ 84E6B85A298162EF00428BAF /* PushRequestPresenter.swift */,
+ 84E6B85C298162F700428BAF /* PushRequestRouter.swift */,
+ 84E6B85E2981630000428BAF /* PushRequestInteractor.swift */,
+ 84E6B8602981630C00428BAF /* PushRequestView.swift */,
+ );
+ path = PushRequest;
+ sourceTree = "";
+ };
A50F3944288005A700064555 /* Types */ = {
isa = PBXGroup;
children = (
@@ -925,6 +1010,16 @@
path = Chat;
sourceTree = "";
};
+ A574B3592964570000C2BB91 /* Web3Inbox */ = {
+ isa = PBXGroup;
+ children = (
+ A518A98529683FB60035247E /* Web3InboxModule.swift */,
+ A518A98629683FB60035247E /* Web3InboxRouter.swift */,
+ A518A98429683FB60035247E /* Web3InboxViewController.swift */,
+ );
+ path = Web3Inbox;
+ sourceTree = "";
+ };
A578FA332873049400AA7720 /* Style */ = {
isa = PBXGroup;
children = (
@@ -1024,6 +1119,7 @@
A58E7D062872A4390082D443 /* PresentationLayer */ = {
isa = PBXGroup;
children = (
+ A574B3592964570000C2BB91 /* Web3Inbox */,
A59F876828B53E6400A9CD80 /* Chat */,
);
path = PresentationLayer;
@@ -1354,6 +1450,8 @@
C56EE21C293F55ED004840D1 /* WalletApp */ = {
isa = PBXGroup;
children = (
+ 84DB38F029828A7C00BFEE37 /* WalletApp.entitlements */,
+ 849A4F18298281E300E61ACE /* WalletAppRelease.entitlements */,
C56EE25C293F56D6004840D1 /* Common */,
C56EE27E293F5756004840D1 /* ApplicationLayer */,
C56EE29E293F577B004840D1 /* PresentationLayer */,
@@ -1373,6 +1471,7 @@
C5F32A2A2954812900A6476E /* ConnectionDetails */,
C56EE236293F566A004840D1 /* Scan */,
C56EE22A293F5668004840D1 /* Wallet */,
+ 84E6B8592981625A00428BAF /* PushRequest */,
);
path = Wallet;
sourceTree = "";
@@ -1474,6 +1573,7 @@
C56EE280293F5757004840D1 /* Application.swift */,
C56EE27F293F5757004840D1 /* AppDelegate.swift */,
C56EE281293F5757004840D1 /* SceneDelegate.swift */,
+ 84DB38F22983CDAE00BFEE37 /* PushRegisterer.swift */,
);
path = ApplicationLayer;
sourceTree = "";
@@ -1611,11 +1711,32 @@
A5D85227286333E300DAF5C3 /* Starscream */,
A5BB7FA228B6A50400707FC6 /* WalletConnectAuth */,
A54195A42934E83F0035AD19 /* Web3 */,
+ 84E6B8642981720400428BAF /* WalletConnectPush */,
);
productName = DApp;
productReference = 84CE641C27981DED00142511 /* DApp.app */;
productType = "com.apple.product-type.application";
};
+ 84E6B84629787A8000428BAF /* PNDecryptionService */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 84E6B84F29787A8000428BAF /* Build configuration list for PBXNativeTarget "PNDecryptionService" */;
+ buildPhases = (
+ 84E6B84329787A8000428BAF /* Sources */,
+ 84E6B84429787A8000428BAF /* Frameworks */,
+ 84E6B84529787A8000428BAF /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = PNDecryptionService;
+ packageProductDependencies = (
+ 84E6B86229816A7900428BAF /* WalletConnectPush */,
+ );
+ productName = PNDecryptionService;
+ productReference = 84E6B84729787A8000428BAF /* PNDecryptionService.appex */;
+ productType = "com.apple.product-type.app-extension";
+ };
A58E7CE728729F550082D443 /* Showcase */ = {
isa = PBXNativeTarget;
buildConfigurationList = A58E7CFB28729F550082D443 /* Build configuration list for PBXNativeTarget "Showcase" */;
@@ -1634,6 +1755,7 @@
A5629AF12877F75100094373 /* Starscream */,
A59F877528B5462900A9CD80 /* WalletConnectAuth */,
A59FAEC828B7B93A002BB66F /* Web3 */,
+ A574B3572964560C00C2BB91 /* Web3Inbox */,
);
productName = Showcase;
productReference = A58E7CE828729F550082D443 /* Showcase.app */;
@@ -1691,10 +1813,12 @@
C56EE217293F55ED004840D1 /* Sources */,
C56EE218293F55ED004840D1 /* Frameworks */,
C56EE219293F55ED004840D1 /* Resources */,
+ 84E6B85229787A8000428BAF /* Embed Foundation Extensions */,
);
buildRules = (
);
dependencies = (
+ 84E6B84D29787A8000428BAF /* PBXTargetDependency */,
);
name = WalletApp;
packageProductDependencies = (
@@ -1704,6 +1828,7 @@
C5133A77294125CC00A8314C /* Web3 */,
C55D349829630D440004314A /* Web3Wallet */,
C5B2F7042970573D000DBA0E /* SolanaSwift */,
+ 84E6B85329787AAE00428BAF /* WalletConnectPush */,
);
productName = ChatWallet;
productReference = C56EE21B293F55ED004840D1 /* WalletApp.app */;
@@ -1727,6 +1852,9 @@
84CE641B27981DED00142511 = {
CreatedOnToolsVersion = 13.2;
};
+ 84E6B84629787A8000428BAF = {
+ CreatedOnToolsVersion = 14.2;
+ };
A58E7CE728729F550082D443 = {
CreatedOnToolsVersion = 13.3;
};
@@ -1766,6 +1894,7 @@
A58E7CE728729F550082D443 /* Showcase */,
84B767752954554A00E92316 /* PushDecryptionService */,
C56EE21A293F55ED004840D1 /* WalletApp */,
+ 84E6B84629787A8000428BAF /* PNDecryptionService */,
);
};
/* End PBXProject section */
@@ -1796,6 +1925,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 84E6B84529787A8000428BAF /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
A58E7CE628729F550082D443 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -1900,6 +2036,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 84E6B84329787A8000428BAF /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 84E6B84A29787A8000428BAF /* NotificationService.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
A58E7CE428729F550082D443 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -1938,14 +2082,17 @@
A58E7D0C2872A45B0082D443 /* MainModule.swift in Sources */,
A5C2021C287E1FD8007E3188 /* ImportInteractor.swift in Sources */,
A58E7D0D2872A45B0082D443 /* MainPresenter.swift in Sources */,
+ A518A98829683FB60035247E /* Web3InboxModule.swift in Sources */,
A5C20219287E1FD8007E3188 /* ImportModule.swift in Sources */,
A5629AD62876CC5700094373 /* InviteInteractor.swift in Sources */,
A5629AE12876CC6E00094373 /* InviteListInteractor.swift in Sources */,
A58E7CED28729F550082D443 /* SceneDelegate.swift in Sources */,
A5C2020F287D9DEE007E3188 /* WelcomeView.swift in Sources */,
+ A518A98929683FB60035247E /* Web3InboxRouter.swift in Sources */,
A5C20226287EB099007E3188 /* AccountNameResolver.swift in Sources */,
A5C2020D287D9DEE007E3188 /* WelcomeRouter.swift in Sources */,
A578FA372873D8EE00AA7720 /* UIColor.swift in Sources */,
+ A518A98729683FB60035247E /* Web3InboxViewController.swift in Sources */,
A5C2021D287E1FD8007E3188 /* ImportView.swift in Sources */,
A5C2021A287E1FD8007E3188 /* ImportPresenter.swift in Sources */,
A5629AE828772A0100094373 /* InviteViewModel.swift in Sources */,
@@ -2024,7 +2171,9 @@
files = (
C5D4603A29687A5700302C7E /* DefaultSocketFactory.swift in Sources */,
C53AA4362941251C008EA57C /* DefaultSignerFactory.swift in Sources */,
+ 84E6B8582981624F00428BAF /* PushRequestModule.swift in Sources */,
C55D3480295DD7140004314A /* AuthRequestPresenter.swift in Sources */,
+ 84E6B85B298162EF00428BAF /* PushRequestPresenter.swift in Sources */,
C5F32A2E2954814A00A6476E /* ConnectionDetailsRouter.swift in Sources */,
C55D3482295DD7140004314A /* AuthRequestInteractor.swift in Sources */,
C55D34B12965FB750004314A /* SessionProposalInteractor.swift in Sources */,
@@ -2054,6 +2203,8 @@
C55D3493295DFA750004314A /* WelcomeModule.swift in Sources */,
C56EE270293F56D7004840D1 /* String.swift in Sources */,
C56EE279293F56D7004840D1 /* Color.swift in Sources */,
+ 84E6B8612981630C00428BAF /* PushRequestView.swift in Sources */,
+ 84E6B85F2981630000428BAF /* PushRequestInteractor.swift in Sources */,
C55D3483295DD7140004314A /* AuthRequestView.swift in Sources */,
C56EE243293F566D004840D1 /* ScanView.swift in Sources */,
C56EE288293F5757004840D1 /* ThirdPartyConfigurator.swift in Sources */,
@@ -2077,9 +2228,11 @@
C55D3489295DD8CA0004314A /* PasteUriModule.swift in Sources */,
C55D3494295DFA750004314A /* WelcomePresenter.swift in Sources */,
C5B2F6F929705293000DBA0E /* SessionRequestPresenter.swift in Sources */,
+ 84DB38F32983CDAE00BFEE37 /* PushRegisterer.swift in Sources */,
C5B2F6FB297055B0000DBA0E /* ETHSigner.swift in Sources */,
C56EE274293F56D7004840D1 /* SceneViewController.swift in Sources */,
C55D3496295DFA750004314A /* WelcomeInteractor.swift in Sources */,
+ 84E6B85D298162F700428BAF /* PushRequestRouter.swift in Sources */,
C5B2F6FC297055B0000DBA0E /* SOLSigner.swift in Sources */,
C55D348D295DD8CA0004314A /* PasteUriView.swift in Sources */,
C5F32A2C2954814200A6476E /* ConnectionDetailsModule.swift in Sources */,
@@ -2101,6 +2254,11 @@
target = 84B767752954554A00E92316 /* PushDecryptionService */;
targetProxy = 84B7677B2954554A00E92316 /* PBXContainerItemProxy */;
};
+ 84E6B84D29787A8000428BAF /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 84E6B84629787A8000428BAF /* PNDecryptionService */;
+ targetProxy = 84E6B84C29787A8000428BAF /* PBXContainerItemProxy */;
+ };
A5A4FC7E2840C5D400BBEC1E /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 84CE641B27981DED00142511 /* DApp */;
@@ -2176,6 +2334,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 7;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
@@ -2200,6 +2359,7 @@
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
@@ -2238,6 +2398,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 7;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -2257,6 +2418,7 @@
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
VALIDATE_PRODUCT = YES;
+ VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
@@ -2269,7 +2431,7 @@
CODE_SIGN_ENTITLEMENTS = Wallet.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 13;
+ CURRENT_PROJECT_VERSION = 7;
DEVELOPMENT_TEAM = W5R8AG9K22;
INFOPLIST_FILE = ExampleApp/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
@@ -2277,7 +2439,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 0.0.1;
+ MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.example;
PRODUCT_NAME = "WalletConnect Wallet";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -2295,7 +2457,7 @@
CODE_SIGN_ENTITLEMENTS = Wallet.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 13;
+ CURRENT_PROJECT_VERSION = 7;
DEVELOPMENT_TEAM = W5R8AG9K22;
INFOPLIST_FILE = ExampleApp/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
@@ -2303,7 +2465,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 0.0.1;
+ MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.example;
PRODUCT_NAME = "WalletConnect Wallet";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -2319,7 +2481,7 @@
CODE_SIGN_ENTITLEMENTS = PushDecryptionService/PushDecryptionService.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 1;
+ CURRENT_PROJECT_VERSION = 7;
DEVELOPMENT_TEAM = W5R8AG9K22;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = PushDecryptionService/Info.plist;
@@ -2348,7 +2510,7 @@
CODE_SIGN_ENTITLEMENTS = PushDecryptionService/PushDecryptionService.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 1;
+ CURRENT_PROJECT_VERSION = 7;
DEVELOPMENT_TEAM = W5R8AG9K22;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = PushDecryptionService/Info.plist;
@@ -2378,7 +2540,7 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 13;
+ CURRENT_PROJECT_VERSION = 7;
DEVELOPMENT_TEAM = W5R8AG9K22;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = DApp/Info.plist;
@@ -2394,7 +2556,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 0.0.1;
+ MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.dapp;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -2412,7 +2574,7 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 13;
+ CURRENT_PROJECT_VERSION = 7;
DEVELOPMENT_TEAM = W5R8AG9K22;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = DApp/Info.plist;
@@ -2428,7 +2590,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 0.0.1;
+ MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.dapp;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -2438,6 +2600,64 @@
};
name = Release;
};
+ 84E6B85029787A8000428BAF /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CODE_SIGN_ENTITLEMENTS = PNDecryptionService/PNDecryptionService.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = W5R8AG9K22;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = PNDecryptionService/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = PNDecryptionService;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ IPHONEOS_DEPLOYMENT_TARGET = 16.2;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@executable_path/../../Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.walletapp.PNDecryptionService;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SKIP_INSTALL = YES;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 84E6B85129787A8000428BAF /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CODE_SIGN_ENTITLEMENTS = PNDecryptionService/PNDecryptionServiceRelease.entitlements;
+ CODE_SIGN_IDENTITY = "Apple Development";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = W5R8AG9K22;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = PNDecryptionService/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = PNDecryptionService;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ IPHONEOS_DEPLOYMENT_TARGET = 16.2;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@executable_path/../../Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.walletapp.PNDecryptionService;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SKIP_INSTALL = YES;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
A58E7CF928729F550082D443 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -2445,7 +2665,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 1;
+ CURRENT_PROJECT_VERSION = 7;
DEVELOPMENT_TEAM = W5R8AG9K22;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = Showcase/Other/Info.plist;
@@ -2475,7 +2695,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 1;
+ CURRENT_PROJECT_VERSION = 7;
DEVELOPMENT_TEAM = W5R8AG9K22;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = Showcase/Other/Info.plist;
@@ -2503,11 +2723,10 @@
buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 1;
+ CURRENT_PROJECT_VERSION = 7;
DEVELOPMENT_TEAM = W5R8AG9K22;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
- MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.UITests;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
@@ -2522,11 +2741,10 @@
buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 1;
+ CURRENT_PROJECT_VERSION = 7;
DEVELOPMENT_TEAM = W5R8AG9K22;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
- MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.UITests;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
@@ -2541,7 +2759,7 @@
buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 1;
+ CURRENT_PROJECT_VERSION = 7;
DEVELOPMENT_TEAM = W5R8AG9K22;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
@@ -2550,7 +2768,6 @@
);
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
- MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.IntegrationTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
@@ -2564,11 +2781,10 @@
buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 1;
+ CURRENT_PROJECT_VERSION = 7;
DEVELOPMENT_TEAM = W5R8AG9K22;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
- MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.IntegrationTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
@@ -2580,12 +2796,14 @@
C56EE226293F55EE004840D1 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CODE_SIGN_ENTITLEMENTS = WalletApp/WalletApp.entitlements;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 1;
- DEVELOPMENT_TEAM = RQ4NX29V75;
+ CURRENT_PROJECT_VERSION = 7;
+ DEVELOPMENT_TEAM = W5R8AG9K22;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = WalletApp/Other/Info.plist;
@@ -2610,12 +2828,15 @@
C56EE227293F55EE004840D1 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CODE_SIGN_ENTITLEMENTS = WalletApp/WalletAppRelease.entitlements;
+ CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 1;
- DEVELOPMENT_TEAM = RQ4NX29V75;
+ CURRENT_PROJECT_VERSION = 7;
+ DEVELOPMENT_TEAM = W5R8AG9K22;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = WalletApp/Other/Info.plist;
@@ -2631,6 +2852,7 @@
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.walletapp;
PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@@ -2676,6 +2898,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ 84E6B84F29787A8000428BAF /* Build configuration list for PBXNativeTarget "PNDecryptionService" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 84E6B85029787A8000428BAF /* Debug */,
+ 84E6B85129787A8000428BAF /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
A58E7CFB28729F550082D443 /* Build configuration list for PBXNativeTarget "Showcase" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@@ -2774,6 +3005,18 @@
isa = XCSwiftPackageProductDependency;
productName = WalletConnectAuth;
};
+ 84E6B85329787AAE00428BAF /* WalletConnectPush */ = {
+ isa = XCSwiftPackageProductDependency;
+ productName = WalletConnectPush;
+ };
+ 84E6B86229816A7900428BAF /* WalletConnectPush */ = {
+ isa = XCSwiftPackageProductDependency;
+ productName = WalletConnectPush;
+ };
+ 84E6B8642981720400428BAF /* WalletConnectPush */ = {
+ isa = XCSwiftPackageProductDependency;
+ productName = WalletConnectPush;
+ };
A54195A42934E83F0035AD19 /* Web3 */ = {
isa = XCSwiftPackageProductDependency;
package = A5AE354528A1A2AC0059AE8A /* XCRemoteSwiftPackageReference "Web3" */;
@@ -2793,6 +3036,10 @@
package = A5D85224286333D500DAF5C3 /* XCRemoteSwiftPackageReference "Starscream" */;
productName = Starscream;
};
+ A574B3572964560C00C2BB91 /* Web3Inbox */ = {
+ isa = XCSwiftPackageProductDependency;
+ productName = Web3Inbox;
+ };
A59F877528B5462900A9CD80 /* WalletConnectAuth */ = {
isa = XCSwiftPackageProductDependency;
productName = WalletConnectAuth;
diff --git a/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
deleted file mode 100644
index 4dc474e71..000000000
--- a/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
+++ /dev/null
@@ -1,88 +0,0 @@
-{
- "object": {
- "pins": [
- {
- "package": "BigInt",
- "repositoryURL": "https://github.com/attaswift/BigInt.git",
- "state": {
- "branch": null,
- "revision": "0ed110f7555c34ff468e72e1686e59721f2b0da6",
- "version": "5.3.0"
- }
- },
- {
- "package": "CryptoSwift",
- "repositoryURL": "https://github.com/krzyzanowskim/CryptoSwift.git",
- "state": {
- "branch": null,
- "revision": "19b3c3ceed117c5cc883517c4e658548315ba70b",
- "version": "1.6.0"
- }
- },
- {
- "package": "PromiseKit",
- "repositoryURL": "https://github.com/mxcl/PromiseKit.git",
- "state": {
- "branch": null,
- "revision": "43772616c46a44a9977e41924ae01d0e55f2f9ca",
- "version": "6.18.1"
- }
- },
- {
- "package": "secp256k1",
- "repositoryURL": "https://github.com/Boilertalk/secp256k1.swift.git",
- "state": {
- "branch": null,
- "revision": "cd187c632fb812fd93711a9f7e644adb7e5f97f0",
- "version": "0.1.7"
- }
- },
- {
- "package": "SolanaSwift",
- "repositoryURL": "https://github.com/flypaper0/solana-swift",
- "state": {
- "branch": "feature/available-13",
- "revision": "a98811518e0a90c2dfc60c30cfd3ec85c33b6790",
- "version": null
- }
- },
- {
- "package": "Starscream",
- "repositoryURL": "https://github.com/daltoniam/Starscream",
- "state": {
- "branch": null,
- "revision": "a063fda2b8145a231953c20e7a646be254365396",
- "version": "3.1.2"
- }
- },
- {
- "package": "Task_retrying",
- "repositoryURL": "https://github.com/bigearsenal/task-retrying-swift.git",
- "state": {
- "branch": null,
- "revision": "645eaaf207a6f39ab4b469558d916ae23df199b5",
- "version": "1.0.3"
- }
- },
- {
- "package": "TweetNacl",
- "repositoryURL": "https://github.com/bitmark-inc/tweetnacl-swiftwrap.git",
- "state": {
- "branch": null,
- "revision": "f8fd111642bf2336b11ef9ea828510693106e954",
- "version": "1.1.0"
- }
- },
- {
- "package": "Web3",
- "repositoryURL": "https://github.com/WalletConnect/Web3.swift",
- "state": {
- "branch": null,
- "revision": "569255adcfff0b37e4cb8004aea29d0e2d6266df",
- "version": "1.0.2"
- }
- }
- ]
- },
- "version": 1
-}
diff --git a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/IntegrationTests.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/IntegrationTests.xcscheme
index 6c42dec52..177cd0cec 100644
--- a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/IntegrationTests.xcscheme
+++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/IntegrationTests.xcscheme
@@ -5,6 +5,22 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/PushDecryptionService.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/PushDecryptionService.xcscheme
new file mode 100644
index 000000000..59e2134d6
--- /dev/null
+++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/PushDecryptionService.xcscheme
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/UITests.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/UITests.xcscheme
index a8ccba84c..e454d658f 100644
--- a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/UITests.xcscheme
+++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/UITests.xcscheme
@@ -5,6 +5,22 @@
+
+
+
+
+
+
+ ReferencedContainer = "container:..">
+ BlueprintIdentifier = "Web3Inbox"
+ BuildableName = "Web3Inbox"
+ BlueprintName = "Web3Inbox"
+ ReferencedContainer = "container:..">
+ BlueprintIdentifier = "WalletConnectPush"
+ BuildableName = "WalletConnectPush"
+ BlueprintName = "WalletConnectPush"
+ ReferencedContainer = "container:..">
+ BlueprintIdentifier = "WalletConnectVerify"
+ BuildableName = "WalletConnectVerify"
+ BlueprintName = "WalletConnectVerify"
+ ReferencedContainer = "container:..">
+ BlueprintIdentifier = "WalletConnectPairing"
+ BuildableName = "WalletConnectPairing"
+ BlueprintName = "WalletConnectPairing"
+ ReferencedContainer = "container:..">
+ BlueprintIdentifier = "WalletConnectAuth"
+ BuildableName = "WalletConnectAuth"
+ BlueprintName = "WalletConnectAuth"
+ ReferencedContainer = "container:..">
+ BlueprintIdentifier = "Web3Wallet"
+ BuildableName = "Web3Wallet"
+ BlueprintName = "Web3Wallet"
+ ReferencedContainer = "container:..">
-
-
-
-
+ BlueprintIdentifier = "WalletConnectRouter"
+ BuildableName = "WalletConnectRouter"
+ BlueprintName = "WalletConnectRouter"
+ ReferencedContainer = "container:..">
+ BlueprintIdentifier = "WalletConnectEcho"
+ BuildableName = "WalletConnectEcho"
+ BlueprintName = "WalletConnectEcho"
+ ReferencedContainer = "container:..">
+ BlueprintIdentifier = "WalletConnectNetworking"
+ BuildableName = "WalletConnectNetworking"
+ BlueprintName = "WalletConnectNetworking"
+ ReferencedContainer = "container:..">
+ BlueprintIdentifier = "WalletConnectChat"
+ BuildableName = "WalletConnectChat"
+ BlueprintName = "WalletConnectChat"
+ ReferencedContainer = "container:..">
@@ -188,7 +174,7 @@
BlueprintIdentifier = "WalletConnect"
BuildableName = "WalletConnect"
BlueprintName = "WalletConnect"
- ReferencedContainer = "container:">
+ ReferencedContainer = "container:..">
@@ -196,30 +182,20 @@
skipped = "NO">
-
-
-
-
+ BlueprintIdentifier = "AuthTests"
+ BuildableName = "AuthTests"
+ BlueprintName = "AuthTests"
+ ReferencedContainer = "container:..">
+ BlueprintIdentifier = "ChatTests"
+ BuildableName = "ChatTests"
+ BlueprintName = "ChatTests"
+ ReferencedContainer = "container:..">
+ ReferencedContainer = "container:..">
+ BlueprintIdentifier = "JSONRPCTests"
+ BuildableName = "JSONRPCTests"
+ BlueprintName = "JSONRPCTests"
+ ReferencedContainer = "container:..">
+ BlueprintIdentifier = "RelayerTests"
+ BuildableName = "RelayerTests"
+ BlueprintName = "RelayerTests"
+ ReferencedContainer = "container:..">
+ BlueprintIdentifier = "VerifyTests"
+ BuildableName = "VerifyTests"
+ BlueprintName = "VerifyTests"
+ ReferencedContainer = "container:..">
+ BlueprintIdentifier = "WalletConnectKMSTests"
+ BuildableName = "WalletConnectKMSTests"
+ BlueprintName = "WalletConnectKMSTests"
+ ReferencedContainer = "container:..">
+ ReferencedContainer = "container:..">
+
+
+
+
+
+
+
+
+
+
+
+
@@ -307,7 +313,7 @@
BlueprintIdentifier = "WalletConnect"
BuildableName = "WalletConnect"
BlueprintName = "WalletConnect"
- ReferencedContainer = "container:">
+ ReferencedContainer = "container:..">
diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectAuth.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectAuth.xcscheme
similarity index 94%
rename from .swiftpm/xcode/xcshareddata/xcschemes/WalletConnectAuth.xcscheme
rename to Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectAuth.xcscheme
index 10b74b10a..3a5aff949 100644
--- a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectAuth.xcscheme
+++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectAuth.xcscheme
@@ -17,7 +17,7 @@
BlueprintIdentifier = "WalletConnectAuth"
BuildableName = "WalletConnectAuth"
BlueprintName = "WalletConnectAuth"
- ReferencedContainer = "container:">
+ ReferencedContainer = "container:..">
@@ -35,7 +35,7 @@
BlueprintIdentifier = "AuthTests"
BuildableName = "AuthTests"
BlueprintName = "AuthTests"
- ReferencedContainer = "container:">
+ ReferencedContainer = "container:..">
@@ -63,7 +63,7 @@
BlueprintIdentifier = "WalletConnectAuth"
BuildableName = "WalletConnectAuth"
BlueprintName = "WalletConnectAuth"
- ReferencedContainer = "container:">
+ ReferencedContainer = "container:..">
diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectChat.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectChat.xcscheme
similarity index 94%
rename from .swiftpm/xcode/xcshareddata/xcschemes/WalletConnectChat.xcscheme
rename to Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectChat.xcscheme
index c04003b78..ae2c82e99 100644
--- a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectChat.xcscheme
+++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectChat.xcscheme
@@ -17,7 +17,7 @@
BlueprintIdentifier = "WalletConnectChat"
BuildableName = "WalletConnectChat"
BlueprintName = "WalletConnectChat"
- ReferencedContainer = "container:">
+ ReferencedContainer = "container:..">
@@ -35,7 +35,7 @@
BlueprintIdentifier = "ChatTests"
BuildableName = "ChatTests"
BlueprintName = "ChatTests"
- ReferencedContainer = "container:">
+ ReferencedContainer = "container:..">
@@ -63,7 +63,7 @@
BlueprintIdentifier = "WalletConnectChat"
BuildableName = "WalletConnectChat"
BlueprintName = "WalletConnectChat"
- ReferencedContainer = "container:">
+ ReferencedContainer = "container:..">
diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/ChatTests.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectEcho.xcscheme
similarity index 62%
rename from .swiftpm/xcode/xcshareddata/xcschemes/ChatTests.xcscheme
rename to Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectEcho.xcscheme
index a7cb31007..341890963 100644
--- a/.swiftpm/xcode/xcshareddata/xcschemes/ChatTests.xcscheme
+++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectEcho.xcscheme
@@ -1,10 +1,26 @@
+
+
+
+
+
+
-
-
-
-
+
+
+
+
diff --git a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectNetworking.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectNetworking.xcscheme
new file mode 100644
index 000000000..4d9ac47b7
--- /dev/null
+++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectNetworking.xcscheme
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectPairing.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectPairing.xcscheme
new file mode 100644
index 000000000..dc1c7488b
--- /dev/null
+++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectPairing.xcscheme
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/RelayerTests.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectPush.xcscheme
similarity index 62%
rename from .swiftpm/xcode/xcshareddata/xcschemes/RelayerTests.xcscheme
rename to Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectPush.xcscheme
index a887ff162..48c7aad62 100644
--- a/.swiftpm/xcode/xcshareddata/xcschemes/RelayerTests.xcscheme
+++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectPush.xcscheme
@@ -1,10 +1,26 @@
+
+
+
+
+
+
-
-
-
-
+
+
+
+
diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectKMSTests.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectRouter.xcscheme
similarity index 61%
rename from .swiftpm/xcode/xcshareddata/xcschemes/WalletConnectKMSTests.xcscheme
rename to Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectRouter.xcscheme
index d88c50634..a7c2791a6 100644
--- a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectKMSTests.xcscheme
+++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectRouter.xcscheme
@@ -1,10 +1,26 @@
+
+
+
+
+
+
-
-
-
-
+
+
+
+
diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectVerify.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectVerify.xcscheme
similarity index 95%
rename from .swiftpm/xcode/xcshareddata/xcschemes/WalletConnectVerify.xcscheme
rename to Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectVerify.xcscheme
index b5ef0e1c9..5b4aa4030 100644
--- a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectVerify.xcscheme
+++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectVerify.xcscheme
@@ -17,7 +17,7 @@
BlueprintIdentifier = "WalletConnectVerify"
BuildableName = "WalletConnectVerify"
BlueprintName = "WalletConnectVerify"
- ReferencedContainer = "container:">
+ ReferencedContainer = "container:..">
@@ -53,7 +53,7 @@
BlueprintIdentifier = "WalletConnectVerify"
BuildableName = "WalletConnectVerify"
BlueprintName = "WalletConnectVerify"
- ReferencedContainer = "container:">
+ ReferencedContainer = "container:..">
diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/AuthTests.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/Web3Inbox.xcscheme
similarity index 63%
rename from .swiftpm/xcode/xcshareddata/xcschemes/AuthTests.xcscheme
rename to Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/Web3Inbox.xcscheme
index b6a508a85..ae55238ff 100644
--- a/.swiftpm/xcode/xcshareddata/xcschemes/AuthTests.xcscheme
+++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/Web3Inbox.xcscheme
@@ -1,10 +1,26 @@
+
+
+
+
+
+
-
-
-
-
+
+
+
+
diff --git a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/Web3Wallet.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/Web3Wallet.xcscheme
new file mode 100644
index 000000000..8776c0962
--- /dev/null
+++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/Web3Wallet.xcscheme
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Example/ExampleApp/Info.plist b/Example/ExampleApp/Info.plist
index 97535e087..c1f94161e 100644
--- a/Example/ExampleApp/Info.plist
+++ b/Example/ExampleApp/Info.plist
@@ -2,10 +2,6 @@
- PROJECT_ID
- $(PROJECT_ID)
- SIMULATOR_IDENTIFIER
- $(SIMULATOR_IDENTIFIER)
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
CFBundleDisplayName
@@ -36,13 +32,17 @@
CFBundleVersion
- $(CURRENT_PROJECT_VERSION)
+ 7
ITSAppUsesNonExemptEncryption
LSRequiresIPhoneOS
NSCameraUsageDescription
Allow the app to scan for QR codes
+ PROJECT_ID
+ $(PROJECT_ID)
+ SIMULATOR_IDENTIFIER
+ $(SIMULATOR_IDENTIFIER)
UIApplicationSceneManifest
UIApplicationSupportsMultipleScenes
diff --git a/Example/ExampleApp/SessionDetails/SessionDetailViewModel.swift b/Example/ExampleApp/SessionDetails/SessionDetailViewModel.swift
index d43a5ce3d..6e047d692 100644
--- a/Example/ExampleApp/SessionDetails/SessionDetailViewModel.swift
+++ b/Example/ExampleApp/SessionDetails/SessionDetailViewModel.swift
@@ -175,8 +175,7 @@ private extension SessionNamespace {
self.init(
accounts: accounts ?? namespace.accounts,
methods: methods ?? namespace.methods,
- events: events ?? namespace.events,
- extensions: namespace.extensions
+ events: events ?? namespace.events
)
}
}
diff --git a/Example/ExampleApp/Wallet/WalletViewController.swift b/Example/ExampleApp/Wallet/WalletViewController.swift
index da1d92cad..0eaef4cac 100644
--- a/Example/ExampleApp/Wallet/WalletViewController.swift
+++ b/Example/ExampleApp/Wallet/WalletViewController.swift
@@ -223,11 +223,7 @@ extension WalletViewController: ProposalViewControllerDelegate {
let proposalNamespace = $0.value
let accounts = Set(proposalNamespace.chains.compactMap { Account($0.absoluteString + ":\(self.accounts[$0.namespace]!)") })
- let extensions: [SessionNamespace.Extension]? = proposalNamespace.extensions?.map { element in
- let accounts = Set(element.chains.compactMap { Account($0.absoluteString + ":\(self.accounts[$0.namespace]!)") })
- return SessionNamespace.Extension(accounts: accounts, methods: element.methods, events: element.events)
- }
- let sessionNamespace = SessionNamespace(accounts: accounts, methods: proposalNamespace.methods, events: proposalNamespace.events, extensions: extensions)
+ let sessionNamespace = SessionNamespace(accounts: accounts, methods: proposalNamespace.methods, events: proposalNamespace.events)
sessionNamespaces[caip2Namespace] = sessionNamespace
}
approve(proposalId: proposal.id, namespaces: sessionNamespaces)
diff --git a/Example/IntegrationTests/Chat/ChatTests.swift b/Example/IntegrationTests/Chat/ChatTests.swift
index 0f7d770ce..a10fb95e2 100644
--- a/Example/IntegrationTests/Chat/ChatTests.swift
+++ b/Example/IntegrationTests/Chat/ChatTests.swift
@@ -12,25 +12,26 @@ final class ChatTests: XCTestCase {
var registry: KeyValueRegistry!
private var publishers = [AnyCancellable]()
+ let inviteeAccount = Account(chainIdentifier: "eip155:1", address: "0x3627523167367216556273151")!
+ let inviterAccount = Account(chainIdentifier: "eip155:1", address: "0x36275231673672234423f")!
+
override func setUp() {
registry = KeyValueRegistry()
- invitee = makeClient(prefix: "🦖 Registered")
- inviter = makeClient(prefix: "🍄 Inviter")
+ invitee = makeClient(prefix: "🦖 Registered", account: inviteeAccount)
+ inviter = makeClient(prefix: "🍄 Inviter", account: inviterAccount)
}
- func makeClient(prefix: String) -> ChatClient {
+ func makeClient(prefix: String, account: Account) -> ChatClient {
let logger = ConsoleLogger(suffix: prefix, loggingLevel: .debug)
let keychain = KeychainStorageMock()
let relayClient = RelayClient(relayHost: InputConfig.relayHost, projectId: InputConfig.projectId, keychainStorage: keychain, socketFactory: DefaultSocketFactory(), logger: logger)
- return ChatClientFactory.create(registry: registry, relayClient: relayClient, kms: KeyManagementService(keychain: keychain), logger: logger, keyValueStorage: RuntimeKeyValueStorage())
+ return ChatClientFactory.create(account: account, registry: registry, relayClient: relayClient, kms: KeyManagementService(keychain: keychain), logger: logger, keyValueStorage: RuntimeKeyValueStorage())
}
func testInvite() async {
let inviteExpectation = expectation(description: "invitation expectation")
- let inviteeAccount = Account(chainIdentifier: "eip155:1", address: "0x3627523167367216556273151")!
- let inviterAccount = Account(chainIdentifier: "eip155:1", address: "0x36275231673672234423f")!
try! await invitee.register(account: inviteeAccount)
- try! await inviter.invite(peerAccount: inviteeAccount, openingMessage: "", account: inviterAccount)
+ try! await inviter.invite(peerAccount: inviteeAccount, openingMessage: "")
invitee.invitePublisher.sink { _ in
inviteExpectation.fulfill()
}.store(in: &publishers)
@@ -40,12 +41,10 @@ final class ChatTests: XCTestCase {
func testAcceptAndCreateNewThread() {
let newThreadInviterExpectation = expectation(description: "new thread on inviting client expectation")
let newThreadinviteeExpectation = expectation(description: "new thread on invitee client expectation")
- let inviteeAccount = Account(chainIdentifier: "eip155:1", address: "0x3627523167367216556273151")!
- let inviterAccount = Account(chainIdentifier: "eip155:1", address: "0x36275231673672234423f")!
Task(priority: .high) {
try! await invitee.register(account: inviteeAccount)
- try! await inviter.invite(peerAccount: inviteeAccount, openingMessage: "opening message", account: inviterAccount)
+ try! await inviter.invite(peerAccount: inviteeAccount, openingMessage: "opening message")
}
invitee.invitePublisher.sink { [unowned self] invite in
@@ -67,12 +66,9 @@ final class ChatTests: XCTestCase {
let messageExpectation = expectation(description: "message received")
messageExpectation.expectedFulfillmentCount = 4 // expectedFulfillmentCount 4 because onMessage() called on send too
- let inviteeAccount = Account(chainIdentifier: "eip155:1", address: "0x3627523167367216556273151")!
- let inviterAccount = Account(chainIdentifier: "eip155:1", address: "0x36275231673672234423f")!
-
Task(priority: .high) {
try! await invitee.register(account: inviteeAccount)
- try! await inviter.invite(peerAccount: inviteeAccount, openingMessage: "opening message", account: inviterAccount)
+ try! await inviter.invite(peerAccount: inviteeAccount, openingMessage: "opening message")
}
invitee.invitePublisher.sink { [unowned self] invite in
diff --git a/Example/IntegrationTests/Chat/RegistryTests.swift b/Example/IntegrationTests/Chat/RegistryTests.swift
index ab665f828..ea21c9375 100644
--- a/Example/IntegrationTests/Chat/RegistryTests.swift
+++ b/Example/IntegrationTests/Chat/RegistryTests.swift
@@ -6,13 +6,4 @@ import WalletConnectUtils
final class RegistryTests: XCTestCase {
- func testRegistry() async throws {
- let client = HTTPNetworkClient(host: "keys.walletconnect.com")
- let registry = KeyserverRegistryProvider(client: client)
- let account = Account("eip155:1:" + Data.randomBytes(count: 16).toHexString())!
- let pubKey = SigningPrivateKey().publicKey.hexRepresentation
- try await registry.register(account: account, pubKey: pubKey)
- let resolvedKey = try await registry.resolve(account: account)
- XCTAssertEqual(resolvedKey, pubKey)
- }
}
diff --git a/Example/IntegrationTests/Pairing/PairingTests.swift b/Example/IntegrationTests/Pairing/PairingTests.swift
index 8390e4e38..b0f51314a 100644
--- a/Example/IntegrationTests/Pairing/PairingTests.swift
+++ b/Example/IntegrationTests/Pairing/PairingTests.swift
@@ -69,7 +69,7 @@ final class PairingTests: XCTestCase {
let (pairingClient, networkingInteractor, keychain, keyValueStorage) = makeClientDependencies(prefix: prefix)
let pushLogger = ConsoleLogger(suffix: prefix + " [Push]", loggingLevel: .debug)
walletPairingClient = pairingClient
- let echoClient = EchoClientFactory.create(projectId: "", clientId: "")
+ let echoClient = EchoClientFactory.create(projectId: "", clientId: "", echoHost: "echo.walletconnect.com")
walletPushClient = WalletPushClientFactory.create(logger: pushLogger,
keyValueStorage: keyValueStorage,
keychainStorage: keychain,
diff --git a/Example/IntegrationTests/Push/PushTests.swift b/Example/IntegrationTests/Push/PushTests.swift
index 123549b51..a8619ba30 100644
--- a/Example/IntegrationTests/Push/PushTests.swift
+++ b/Example/IntegrationTests/Push/PushTests.swift
@@ -69,7 +69,7 @@ final class PushTests: XCTestCase {
let (pairingClient, networkingInteractor, keychain, keyValueStorage) = makeClientDependencies(prefix: prefix)
let pushLogger = ConsoleLogger(suffix: prefix + " [Push]", loggingLevel: .debug)
walletPairingClient = pairingClient
- let echoClient = EchoClientFactory.create(projectId: "", clientId: "")
+ let echoClient = EchoClientFactory.create(projectId: "", clientId: "", echoHost: "echo.walletconnect.com")
walletPushClient = WalletPushClientFactory.create(logger: pushLogger,
keyValueStorage: keyValueStorage,
keychainStorage: keychain,
@@ -147,6 +147,7 @@ final class PushTests: XCTestCase {
func testDappSendsPushMessage() async {
let expectation = expectation(description: "expects wallet to receive push message")
let pushMessage = PushMessage.stub()
+ var pushSubscription: PushSubscription!
let uri = try! await dappPairingClient.create()
try! await walletPairingClient.pair(uri: uri)
try! await dappPushClient.request(account: Account.stub(), topic: uri.topic)
@@ -160,14 +161,18 @@ final class PushTests: XCTestCase {
XCTFail()
return
}
+ pushSubscription = subscription
Task(priority: .userInitiated) { try! await dappPushClient.notify(topic: subscription.topic, message: pushMessage) }
}.store(in: &publishers)
- walletPushClient.pushMessagePublisher.sink { receivedPushMessage in
+ walletPushClient.pushMessagePublisher.sink { [unowned self] receivedPushMessage in
+ let messageHistory = walletPushClient.getMessageHistory(topic: pushSubscription.topic)
XCTAssertEqual(pushMessage, receivedPushMessage)
+ XCTAssertTrue(messageHistory.contains(receivedPushMessage))
expectation.fulfill()
}.store(in: &publishers)
+
wait(for: [expectation], timeout: InputConfig.defaultTimeout)
}
diff --git a/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift b/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift
index 60ab35252..79e91d5f2 100644
--- a/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift
+++ b/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift
@@ -33,7 +33,7 @@ final class RelayClientEndToEndTests: XCTestCase {
subscribeExpectation.assertForOverFulfill = true
relayClient.socketConnectionStatusPublisher.sink { status in
if status == .connected {
- relayClient.subscribe(topic: "qwerty") { error in
+ relayClient.subscribe(topic: "ecb78f2df880c43d3418ddbf871092b847801932e21765b250cc50b9e96a9131") { error in
XCTAssertNil(error)
subscribeExpectation.fulfill()
}
diff --git a/Example/IntegrationTests/Sign/SignClientTests.swift b/Example/IntegrationTests/Sign/SignClientTests.swift
index a429d0884..c5a7fcab1 100644
--- a/Example/IntegrationTests/Sign/SignClientTests.swift
+++ b/Example/IntegrationTests/Sign/SignClientTests.swift
@@ -184,7 +184,7 @@ final class SignClientTests: XCTestCase {
}
dapp.onSessionSettled = { [unowned self] settledSession in
Task(priority: .high) {
- let request = Request(id: RPCID(0), topic: settledSession.topic, method: requestMethod, params: requestParams, chainId: chain)
+ let request = Request(id: RPCID(0), topic: settledSession.topic, method: requestMethod, params: requestParams, chainId: chain, expiry: nil)
try await dapp.client.request(params: request)
}
}
@@ -230,7 +230,7 @@ final class SignClientTests: XCTestCase {
}
dapp.onSessionSettled = { [unowned self] settledSession in
Task(priority: .high) {
- let request = Request(id: RPCID(0), topic: settledSession.topic, method: requestMethod, params: requestParams, chainId: chain)
+ let request = Request(id: RPCID(0), topic: settledSession.topic, method: requestMethod, params: requestParams, chainId: chain, expiry: nil)
try await dapp.client.request(params: request)
}
}
diff --git a/Example/PNDecryptionService/Info.plist b/Example/PNDecryptionService/Info.plist
new file mode 100644
index 000000000..57421ebf9
--- /dev/null
+++ b/Example/PNDecryptionService/Info.plist
@@ -0,0 +1,13 @@
+
+
+
+
+ NSExtension
+
+ NSExtensionPointIdentifier
+ com.apple.usernotifications.service
+ NSExtensionPrincipalClass
+ $(PRODUCT_MODULE_NAME).NotificationService
+
+
+
diff --git a/Example/PNDecryptionService/NotificationService.swift b/Example/PNDecryptionService/NotificationService.swift
new file mode 100644
index 000000000..26d9c8960
--- /dev/null
+++ b/Example/PNDecryptionService/NotificationService.swift
@@ -0,0 +1,41 @@
+
+import UserNotifications
+import WalletConnectPush
+
+class NotificationService: UNNotificationServiceExtension {
+
+ var contentHandler: ((UNNotificationContent) -> Void)?
+ var bestAttemptContent: UNMutableNotificationContent?
+
+ override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
+ self.contentHandler = contentHandler
+ bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
+ if let bestAttemptContent = bestAttemptContent,
+ let topic = bestAttemptContent.userInfo["topic"] as? String,
+ let ciphertext = bestAttemptContent.userInfo["blob"] as? String {
+ do {
+ let service = PushDecryptionService()
+ let pushMessage = try service.decryptMessage(topic: topic, ciphertext: ciphertext)
+ bestAttemptContent.title = pushMessage.title
+ bestAttemptContent.body = pushMessage.body
+ contentHandler(bestAttemptContent)
+ return
+ }
+ catch {
+ print(error)
+ }
+ bestAttemptContent.title = ""
+ bestAttemptContent.body = "content not set"
+ contentHandler(bestAttemptContent)
+ }
+ }
+
+ override func serviceExtensionTimeWillExpire() {
+ // Called just before the extension will be terminated by the system.
+ // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
+ if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
+ contentHandler(bestAttemptContent)
+ }
+ }
+
+}
diff --git a/Example/PNDecryptionService/PNDecryptionService.entitlements b/Example/PNDecryptionService/PNDecryptionService.entitlements
new file mode 100644
index 000000000..0bd7a7310
--- /dev/null
+++ b/Example/PNDecryptionService/PNDecryptionService.entitlements
@@ -0,0 +1,10 @@
+
+
+
+
+ com.apple.security.application-groups
+
+ group.com.walletconnect.sdk
+
+
+
diff --git a/Example/PNDecryptionService/PNDecryptionServiceRelease.entitlements b/Example/PNDecryptionService/PNDecryptionServiceRelease.entitlements
new file mode 100644
index 000000000..0bd7a7310
--- /dev/null
+++ b/Example/PNDecryptionService/PNDecryptionServiceRelease.entitlements
@@ -0,0 +1,10 @@
+
+
+
+
+ com.apple.security.application-groups
+
+ group.com.walletconnect.sdk
+
+
+
diff --git a/Example/PushDecryptionService/Info.plist b/Example/PushDecryptionService/Info.plist
index 57421ebf9..38ada2364 100644
--- a/Example/PushDecryptionService/Info.plist
+++ b/Example/PushDecryptionService/Info.plist
@@ -2,6 +2,10 @@
+ CFBundleShortVersionString
+ $(MARKETING_VERSION)
+ CFBundleVersion
+ 7
NSExtension
NSExtensionPointIdentifier
diff --git a/Example/PushDecryptionService/NotificationService.swift b/Example/PushDecryptionService/NotificationService.swift
index 737026677..f8da6c818 100644
--- a/Example/PushDecryptionService/NotificationService.swift
+++ b/Example/PushDecryptionService/NotificationService.swift
@@ -1,4 +1,3 @@
-//
import UserNotifications
import WalletConnectPush
@@ -13,7 +12,7 @@ class NotificationService: UNNotificationServiceExtension {
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
if let bestAttemptContent = bestAttemptContent {
let topic = bestAttemptContent.userInfo["topic"] as! String
- let ciphertext = bestAttemptContent.userInfo["ciphertext"] as! String
+ let ciphertext = bestAttemptContent.userInfo["blob"] as! String
do {
let service = PushDecryptionService()
let pushMessage = try service.decryptMessage(topic: topic, ciphertext: ciphertext)
diff --git a/Example/Showcase/Classes/ApplicationLayer/Application.swift b/Example/Showcase/Classes/ApplicationLayer/Application.swift
index 75d88c640..46b30ef87 100644
--- a/Example/Showcase/Classes/ApplicationLayer/Application.swift
+++ b/Example/Showcase/Classes/ApplicationLayer/Application.swift
@@ -4,7 +4,7 @@ import WalletConnectChat
final class Application {
lazy var chatService: ChatService = {
- return ChatService(client: Chat.instance)
+ return ChatService(accountStorage: accountStorage)
}()
lazy var accountStorage: AccountStorage = {
diff --git a/Example/Showcase/Classes/DomainLayer/Chat/ChatService.swift b/Example/Showcase/Classes/DomainLayer/Chat/ChatService.swift
index 991c06c66..839ef53a6 100644
--- a/Example/Showcase/Classes/DomainLayer/Chat/ChatService.swift
+++ b/Example/Showcase/Classes/DomainLayer/Chat/ChatService.swift
@@ -7,14 +7,26 @@ typealias Stream = AsyncPublisher>
final class ChatService {
- private let client: ChatClient
-
- init(client: ChatClient) {
- self.client = client
+ private lazy var client: ChatClient = {
+ guard let account = accountStorage.account else {
+ fatalError("Error - you must call Chat.configure(_:) before accessing the shared instance.")
+ }
+ Chat.configure(account: account)
+ return Chat.instance
+ }()
+
+ private lazy var networking: NetworkingClient = {
+ return Networking.instance
+ }()
+
+ private let accountStorage: AccountStorage
+
+ init(accountStorage: AccountStorage) {
+ self.accountStorage = accountStorage
}
var connectionPublisher: Stream {
- return client.socketConnectionStatusPublisher.values
+ return networking.socketConnectionStatusPublisher.values
}
var messagePublisher: Stream {
@@ -29,16 +41,16 @@ final class ChatService {
return client.invitePublisher.values
}
- func getMessages(thread: WalletConnectChat.Thread) async -> [WalletConnectChat.Message] {
- await client.getMessages(topic: thread.topic)
+ func getMessages(thread: WalletConnectChat.Thread) -> [WalletConnectChat.Message] {
+ client.getMessages(topic: thread.topic)
}
- func getThreads() async -> [WalletConnectChat.Thread] {
- await client.getThreads()
+ func getThreads() -> [WalletConnectChat.Thread] {
+ client.getThreads()
}
- func getInvites(account: Account) async -> [WalletConnectChat.Invite] {
- client.getInvites(account: account)
+ func getInvites() -> [WalletConnectChat.Invite] {
+ client.getInvites()
}
func sendMessage(topic: String, message: String) async throws {
@@ -53,8 +65,8 @@ final class ChatService {
try await client.reject(inviteId: invite.id)
}
- func invite(peerAccount: Account, message: String, selfAccount: Account) async throws {
- try await client.invite(peerAccount: peerAccount, openingMessage: message, account: selfAccount)
+ func invite(peerAccount: Account, message: String) async throws {
+ try await client.invite(peerAccount: peerAccount, openingMessage: message)
}
func register(account: Account) async throws {
diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListInteractor.swift b/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListInteractor.swift
index 87e40a7af..09a0c976a 100644
--- a/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListInteractor.swift
+++ b/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListInteractor.swift
@@ -10,16 +10,16 @@ final class ChatListInteractor {
self.accountStorage = accountStorage
}
- func getThreads() async -> [WalletConnectChat.Thread] {
- return await chatService.getThreads()
+ func getThreads() -> [WalletConnectChat.Thread] {
+ return chatService.getThreads()
}
func threadsSubscription() -> Stream {
return chatService.threadPublisher
}
- func getInvites(account: Account) async -> [Invite] {
- return await chatService.getInvites(account: account)
+ func getInvites() -> [Invite] {
+ return chatService.getInvites()
}
func invitesSubscription() -> Stream {
diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListPresenter.swift b/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListPresenter.swift
index a839d8dd7..0f7a0b412 100644
--- a/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListPresenter.swift
+++ b/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListPresenter.swift
@@ -44,7 +44,7 @@ final class ChatListPresenter: ObservableObject {
func didLogoutPress() {
interactor.logout()
- router.presentMain()
+ router.presentWelcome()
}
func didPressNewChat() {
@@ -88,26 +88,24 @@ private extension ChatListPresenter {
@MainActor
func setupInvites() async {
- await loadInvites()
+ loadInvites()
for await _ in interactor.invitesSubscription() {
- await loadInvites()
+ loadInvites()
}
}
@MainActor
func loadThreads() async {
- let threads = await interactor.getThreads()
- self.threads = threads
- .filter { $0.selfAccount == account }
+ self.threads = interactor.getThreads()
.sorted(by: { $0.topic < $1.topic })
.map { ThreadViewModel(thread: $0) }
}
@MainActor
- func loadInvites() async {
- let invites = await interactor.getInvites(account: account)
- self.invites = invites.sorted(by: { $0.publicKey < $1.publicKey })
+ func loadInvites() {
+ self.invites = interactor.getInvites()
+ .sorted(by: { $0.publicKey < $1.publicKey })
.map { InviteViewModel(invite: $0) }
}
diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListRouter.swift b/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListRouter.swift
index eb0564d37..94210189e 100644
--- a/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListRouter.swift
+++ b/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListRouter.swift
@@ -25,7 +25,7 @@ final class ChatListRouter {
ChatModule.create(thread: thread, app: app).push(from: viewController)
}
- func presentMain() {
- MainModule.create(app: app).present()
+ func presentWelcome() {
+ WelcomeModule.create(app: app).present()
}
}
diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListView.swift b/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListView.swift
index 11a6173de..72b5f62f7 100644
--- a/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListView.swift
+++ b/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListView.swift
@@ -47,6 +47,7 @@ struct ChatListView: View {
presenter.didLogoutPress()
}
.foregroundColor(.red)
+ .padding(.bottom, 16)
}
.onAppear {
presenter.setupInitialState()
diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportPresenter.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportPresenter.swift
index 9768456f4..09428a950 100644
--- a/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportPresenter.swift
+++ b/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportPresenter.swift
@@ -15,11 +15,12 @@ final class ImportPresenter: ObservableObject {
self.router = router
}
- func didPressImport() {
+ @MainActor
+ func didPressImport() async {
guard let account = AccountNameResolver.resolveAccount(input)
else { return input = .empty }
interactor.save(account: account)
- register(account: account)
+ await interactor.register(account: account)
router.presentChat(account: account)
}
}
@@ -44,10 +45,4 @@ private extension ImportPresenter {
func setupInitialState() {
}
-
- func register(account: Account) {
- Task(priority: .high) {
- await interactor.register(account: account)
- }
- }
}
diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportRouter.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportRouter.swift
index 0902e7251..9b8b2ac8f 100644
--- a/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportRouter.swift
+++ b/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportRouter.swift
@@ -11,8 +11,6 @@ final class ImportRouter {
}
func presentChat(account: Account) {
- ChatListModule.create(app: app, account: account)
- .wrapToNavigationController()
- .present()
+ MainModule.create(app: app, account: account).present()
}
}
diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportView.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportView.swift
index 6f83f4a4b..028156aaf 100644
--- a/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportView.swift
+++ b/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportView.swift
@@ -15,9 +15,9 @@ struct ImportView: View {
Spacer()
- BrandButton(title: "Ok, done", action: {
- presenter.didPressImport()
- })
+ BrandButton(title: "Ok, done" ) { Task(priority: .userInitiated) {
+ await presenter.didPressImport()
+ }}
.padding(16.0)
}
}
diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Invite/InviteInteractor.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Invite/InviteInteractor.swift
index 26af501c6..e707b2a1a 100644
--- a/Example/Showcase/Classes/PresentationLayer/Chat/Invite/InviteInteractor.swift
+++ b/Example/Showcase/Classes/PresentationLayer/Chat/Invite/InviteInteractor.swift
@@ -6,6 +6,6 @@ final class InviteInteractor {
}
func invite(peerAccount: Account, message: String, selfAccount: Account) async {
- try! await chatService.invite(peerAccount: peerAccount, message: message, selfAccount: selfAccount)
+ try! await chatService.invite(peerAccount: peerAccount, message: message)
}
}
diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/InviteList/InviteListInteractor.swift b/Example/Showcase/Classes/PresentationLayer/Chat/InviteList/InviteListInteractor.swift
index 58c404ab9..1880de742 100644
--- a/Example/Showcase/Classes/PresentationLayer/Chat/InviteList/InviteListInteractor.swift
+++ b/Example/Showcase/Classes/PresentationLayer/Chat/InviteList/InviteListInteractor.swift
@@ -7,8 +7,8 @@ final class InviteListInteractor {
self.chatService = chatService
}
- func getInvites(account: Account) async -> [Invite] {
- return await chatService.getInvites(account: account)
+ func getInvites() -> [Invite] {
+ return chatService.getInvites()
}
func invitesSubscription() -> Stream {
diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/InviteList/InviteListPresenter.swift b/Example/Showcase/Classes/PresentationLayer/Chat/InviteList/InviteListPresenter.swift
index 385a0d37d..915f08ed4 100644
--- a/Example/Showcase/Classes/PresentationLayer/Chat/InviteList/InviteListPresenter.swift
+++ b/Example/Showcase/Classes/PresentationLayer/Chat/InviteList/InviteListPresenter.swift
@@ -19,10 +19,10 @@ final class InviteListPresenter: ObservableObject {
@MainActor
func setupInitialState() async {
- await loadInvites(account: account)
+ loadInvites()
for await _ in interactor.invitesSubscription() {
- await loadInvites(account: account)
+ loadInvites()
}
}
@@ -58,10 +58,9 @@ extension InviteListPresenter: SceneViewModel {
private extension InviteListPresenter {
- @MainActor
- func loadInvites(account: Account) async {
- let invites = await interactor.getInvites(account: account)
- self.invites = invites.sorted(by: { $0.publicKey < $1.publicKey })
+ func loadInvites() {
+ invites = interactor.getInvites()
+ .sorted(by: { $0.publicKey < $1.publicKey })
.map { InviteViewModel(invite: $0) }
}
diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainModule.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainModule.swift
index f60ff5d77..fd6309823 100644
--- a/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainModule.swift
+++ b/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainModule.swift
@@ -3,9 +3,9 @@ import SwiftUI
final class MainModule {
@discardableResult
- static func create(app: Application) -> UIViewController {
+ static func create(app: Application, account: Account) -> UIViewController {
let router = MainRouter(app: app)
- let presenter = MainPresenter(router: router)
+ let presenter = MainPresenter(router: router, account: account)
let viewController = MainViewController(presenter: presenter)
router.viewController = viewController
diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainPresenter.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainPresenter.swift
index fde2e49fc..10d1de36f 100644
--- a/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainPresenter.swift
+++ b/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainPresenter.swift
@@ -3,6 +3,7 @@ import Combine
final class MainPresenter {
+ private let account: Account
private let router: MainRouter
var tabs: [TabPage] {
@@ -11,11 +12,13 @@ final class MainPresenter {
var viewControllers: [UIViewController] {
return [
- router.chatViewController
+ router.chatViewController(account: account),
+ router.web3InboxViewController(account: account),
]
}
- init(router: MainRouter) {
+ init(router: MainRouter, account: Account) {
+ self.account = account
self.router = router
}
}
diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainRouter.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainRouter.swift
index fdd32686d..9072fcdf1 100644
--- a/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainRouter.swift
+++ b/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainRouter.swift
@@ -6,8 +6,12 @@ final class MainRouter {
private let app: Application
- var chatViewController: UIViewController {
- return WelcomeModule.create(app: app)
+ func chatViewController(account: Account) -> UIViewController {
+ return ChatListModule.create(app: app, account: account).wrapToNavigationController()
+ }
+
+ func web3InboxViewController(account: Account) -> UIViewController {
+ return Web3InboxModule.create(app: app, account: account).wrapToNavigationController()
}
init(app: Application) {
diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Main/Model/TabPage.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Main/Model/TabPage.swift
index 2613f6568..2e6e3b43b 100644
--- a/Example/Showcase/Classes/PresentationLayer/Chat/Main/Model/TabPage.swift
+++ b/Example/Showcase/Classes/PresentationLayer/Chat/Main/Model/TabPage.swift
@@ -2,14 +2,14 @@ import UIKit
enum TabPage: CaseIterable {
case chat
- case wallet
+ case web3Inbox
var title: String {
switch self {
case .chat:
return "Chat"
- case .wallet:
- return "Wallet"
+ case .web3Inbox:
+ return "Web3Inbox"
}
}
@@ -17,8 +17,8 @@ enum TabPage: CaseIterable {
switch self {
case .chat:
return UIImage(systemName: "message.fill")!
- case .wallet:
- return UIImage(systemName: "signature")!
+ case .web3Inbox:
+ return UIImage(systemName: "safari.fill")!
}
}
@@ -27,6 +27,6 @@ enum TabPage: CaseIterable {
}
static var enabledTabs: [TabPage] {
- return [.chat, .wallet]
+ return [.chat, .web3Inbox]
}
}
diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Welcome/WelcomePresenter.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Welcome/WelcomePresenter.swift
index 0ad6296ee..dd9d00f1f 100644
--- a/Example/Showcase/Classes/PresentationLayer/Chat/Welcome/WelcomePresenter.swift
+++ b/Example/Showcase/Classes/PresentationLayer/Chat/Welcome/WelcomePresenter.swift
@@ -26,33 +26,11 @@ final class WelcomePresenter: ObservableObject {
return interactor.isAuthorized() ? "Start Messaging" : "Connect wallet"
}
- func didPressImport() async {
+ func didPressImport() {
if let account = interactor.account {
- router.presentChats(account: account)
+ router.presentMain(account: account)
} else {
- await authWithWallet()
- }
- }
-
- private func authWithWallet() async {
- let uri = await interactor.generateUri()
- try? await Auth.instance.request(
- RequestParams(
- domain: "example.wallet",
- chainId: "eip155:1",
- nonce: "32891756",
- aud: "https://example.wallet/login",
- nbf: nil,
- exp: nil,
- statement: "I accept the ServiceOrg Terms of Service: https://service.invalid/tos",
- requestId: nil,
- resources: ["ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/", "https://example.com/my-web2-claim.json"]
- ),
- topic: uri.topic
- )
-
- DispatchQueue.main.async {
- self.router.openWallet(uri: uri.absoluteString)
+ router.presentImport()
}
}
}
diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Welcome/WelcomeRouter.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Welcome/WelcomeRouter.swift
index e69faac65..b6ff4209d 100644
--- a/Example/Showcase/Classes/PresentationLayer/Chat/Welcome/WelcomeRouter.swift
+++ b/Example/Showcase/Classes/PresentationLayer/Chat/Welcome/WelcomeRouter.swift
@@ -16,9 +16,8 @@ final class WelcomeRouter {
.present()
}
- func presentChats(account: Account) {
- ChatListModule.create(app: app, account: account)
- .wrapToNavigationController()
+ func presentMain(account: Account) {
+ MainModule.create(app: app, account: account)
.present()
}
diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Welcome/WelcomeView.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Welcome/WelcomeView.swift
index b56ef64e0..2d9ba4129 100644
--- a/Example/Showcase/Classes/PresentationLayer/Chat/Welcome/WelcomeView.swift
+++ b/Example/Showcase/Classes/PresentationLayer/Chat/Welcome/WelcomeView.swift
@@ -32,9 +32,7 @@ struct WelcomeView: View {
.multilineTextAlignment(.center)
BrandButton(title: presenter.buttonTitle, action: {
- Task {
- await presenter.didPressImport()
- }
+ presenter.didPressImport()
})
Text("By connecting your wallet you agree with our\nTerms of Service")
diff --git a/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxModule.swift b/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxModule.swift
new file mode 100644
index 000000000..fe2242603
--- /dev/null
+++ b/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxModule.swift
@@ -0,0 +1,13 @@
+import SwiftUI
+
+final class Web3InboxModule {
+
+ @discardableResult
+ static func create(app: Application, account: Account) -> UIViewController {
+ let router = Web3InboxRouter(app: app)
+ let viewController = Web3InboxViewController(account: account)
+ router.viewController = viewController
+ return viewController
+ }
+
+}
diff --git a/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxRouter.swift b/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxRouter.swift
new file mode 100644
index 000000000..3631c35be
--- /dev/null
+++ b/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxRouter.swift
@@ -0,0 +1,12 @@
+import UIKit
+
+final class Web3InboxRouter {
+
+ weak var viewController: UIViewController!
+
+ private let app: Application
+
+ init(app: Application) {
+ self.app = app
+ }
+}
diff --git a/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift b/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift
new file mode 100644
index 000000000..7f5d62ab3
--- /dev/null
+++ b/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift
@@ -0,0 +1,26 @@
+import UIKit
+import Web3Inbox
+import WebKit
+
+final class Web3InboxViewController: UIViewController {
+
+ private let account: Account
+
+ init(account: Account) {
+ self.account = account
+ super.init(nibName: nil, bundle: nil)
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ Web3Inbox.configure(account: account)
+ view = Web3Inbox.instance.getWebView()
+
+ navigationItem.title = "Web3Inbox SDK"
+ }
+}
diff --git a/Example/Showcase/Other/Info.plist b/Example/Showcase/Other/Info.plist
index 136371e1c..f4245dd27 100644
--- a/Example/Showcase/Other/Info.plist
+++ b/Example/Showcase/Other/Info.plist
@@ -2,6 +2,10 @@
+ CFBundleShortVersionString
+ $(MARKETING_VERSION)
+ CFBundleVersion
+ 7
PROJECT_ID
$(PROJECT_ID)
RELAY_HOST
diff --git a/Example/WalletApp/ApplicationLayer/AppDelegate.swift b/Example/WalletApp/ApplicationLayer/AppDelegate.swift
index 821a8f719..25fa75d10 100644
--- a/Example/WalletApp/ApplicationLayer/AppDelegate.swift
+++ b/Example/WalletApp/ApplicationLayer/AppDelegate.swift
@@ -1,7 +1,11 @@
import UIKit
+import WalletConnectPush
+import Combine
@main
final class AppDelegate: UIResponder, UIApplicationDelegate {
+ private var publishers = [AnyCancellable]()
+
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
@@ -15,4 +19,26 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {}
+
+ func application(
+ _ application: UIApplication,
+ didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
+ ) {
+ Task(priority: .high) {
+ // Use pasteboard for testing purposes
+ let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
+ let token = tokenParts.joined()
+ let pasteboard = UIPasteboard.general
+ pasteboard.string = token
+ try await Push.wallet.register(deviceToken: deviceToken)
+ }
+ }
+
+ func application(
+ _ application: UIApplication,
+ didFailToRegisterForRemoteNotificationsWithError error: Error
+ ) {
+ print("Failed to register: \(error)")
+ }
+
}
diff --git a/Example/WalletApp/ApplicationLayer/Application.swift b/Example/WalletApp/ApplicationLayer/Application.swift
index 97fc2aaf6..91cd85b90 100644
--- a/Example/WalletApp/ApplicationLayer/Application.swift
+++ b/Example/WalletApp/ApplicationLayer/Application.swift
@@ -3,4 +3,5 @@ import WalletConnectChat
final class Application {
var uri: String?
+ let pushRegisterer = PushRegisterer()
}
diff --git a/Example/WalletApp/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift b/Example/WalletApp/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift
index 25279daaa..298207e2b 100644
--- a/Example/WalletApp/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift
+++ b/Example/WalletApp/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift
@@ -1,7 +1,9 @@
import WalletConnectNetworking
import Web3Wallet
+import WalletConnectPush
struct ThirdPartyConfigurator: Configurator {
+
func configure() {
Networking.configure(projectId: InputConfig.projectId, socketFactory: DefaultSocketFactory())
@@ -13,5 +15,8 @@ struct ThirdPartyConfigurator: Configurator {
)
Web3Wallet.configure(metadata: metadata, signerFactory: DefaultSignerFactory())
+ Push.configure()
+
}
+
}
diff --git a/Example/WalletApp/ApplicationLayer/PushRegisterer.swift b/Example/WalletApp/ApplicationLayer/PushRegisterer.swift
new file mode 100644
index 000000000..1f5f19162
--- /dev/null
+++ b/Example/WalletApp/ApplicationLayer/PushRegisterer.swift
@@ -0,0 +1,40 @@
+
+import WalletConnectPush
+import Combine
+import UIKit
+
+class PushRegisterer {
+
+ private var publishers = [AnyCancellable]()
+
+ func getNotificationSettings() {
+ UNUserNotificationCenter.current().getNotificationSettings { settings in
+ print("Notification settings: \(settings)")
+ guard settings.authorizationStatus == .authorized else { return }
+ DispatchQueue.main.async {
+ UIApplication.shared.registerForRemoteNotifications()
+ }
+ }
+ }
+
+ func registerForPushNotifications() {
+ UNUserNotificationCenter.current()
+ .requestAuthorization(
+ options: [.alert, .sound, .badge]) { [weak self] granted, _ in
+ print("Permission granted: \(granted)")
+ guard granted else { return }
+ self?.getNotificationSettings()
+#if targetEnvironment(simulator)
+ Networking.interactor.socketConnectionStatusPublisher
+ .first {$0 == .connected}
+ .sink{ status in
+ let deviceToken = InputConfig.simulatorIdentifier
+ assert(deviceToken != "SIMULATOR_IDENTIFIER", "Please set your Simulator identifier")
+ Task(priority: .high) {
+ try await Push.wallet.register(deviceToken: deviceToken)
+ }
+ }.store(in: &self!.publishers)
+#endif
+ }
+ }
+}
diff --git a/Example/WalletApp/ApplicationLayer/SceneDelegate.swift b/Example/WalletApp/ApplicationLayer/SceneDelegate.swift
index 1fa5738e2..999164f85 100644
--- a/Example/WalletApp/ApplicationLayer/SceneDelegate.swift
+++ b/Example/WalletApp/ApplicationLayer/SceneDelegate.swift
@@ -2,9 +2,11 @@ import UIKit
import Auth
import WalletConnectPairing
+
final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
+
private let app = Application()
private var configurators: [Configurator] {
@@ -31,6 +33,7 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
app.uri = connectionOptions.urlContexts.first?.url.absoluteString.replacingOccurrences(of: "walletapp://wc?uri=", with: "")
configurators.configure()
+ app.pushRegisterer.registerForPushNotifications()
}
func scene(_ scene: UIScene, openURLContexts URLContexts: Set) {
@@ -40,5 +43,6 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
Task {
try await Pair.instance.pair(uri: WalletConnectURI(string: uri)!)
}
+
}
}
diff --git a/Example/WalletApp/Common/InputConfig.swift b/Example/WalletApp/Common/InputConfig.swift
index 1a4e505cf..4cdd5ee73 100644
--- a/Example/WalletApp/Common/InputConfig.swift
+++ b/Example/WalletApp/Common/InputConfig.swift
@@ -5,7 +5,14 @@ struct InputConfig {
return config(for: "PROJECT_ID")!
}
+#if targetEnvironment(simulator)
+ static var simulatorIdentifier: String {
+ return config(for: "SIMULATOR_IDENTIFIER")!
+ }
+#endif
+
private static func config(for key: String) -> String? {
return Bundle.main.object(forInfoDictionaryKey: key) as? String
}
+
}
diff --git a/Example/WalletApp/Other/Info.plist b/Example/WalletApp/Other/Info.plist
index ef9e6d46f..08395f3e9 100644
--- a/Example/WalletApp/Other/Info.plist
+++ b/Example/WalletApp/Other/Info.plist
@@ -2,6 +2,10 @@
+ CFBundleShortVersionString
+ $(MARKETING_VERSION)
+ CFBundleVersion
+ 7
CFBundleIconName
AppIcon
CFBundleURLTypes
@@ -43,5 +47,7 @@
+ SIMULATOR_IDENTIFIER
+ $(SIMULATOR_IDENTIFIER)
diff --git a/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift
new file mode 100644
index 000000000..eb6d6f329
--- /dev/null
+++ b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift
@@ -0,0 +1,12 @@
+import Foundation
+import WalletConnectPush
+
+final class PushRequestInteractor {
+ func approve(pushRequest: PushRequest) async throws {
+ try await Push.wallet.approve(id: pushRequest.id)
+ }
+
+ func reject(pushRequest: PushRequest) async throws {
+ try await Push.wallet.reject(id: pushRequest.id)
+ }
+}
diff --git a/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestModule.swift b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestModule.swift
new file mode 100644
index 000000000..3062207e9
--- /dev/null
+++ b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestModule.swift
@@ -0,0 +1,17 @@
+import SwiftUI
+import WalletConnectPush
+
+final class PushRequestModule {
+ @discardableResult
+ static func create(app: Application, pushRequest: PushRequest) -> UIViewController {
+ let router = PushRequestRouter(app: app)
+ let interactor = PushRequestInteractor()
+ let presenter = PushRequestPresenter(interactor: interactor, router: router, pushRequest: pushRequest)
+ let view = PushRequestView().environmentObject(presenter)
+ let viewController = SceneViewController(viewModel: presenter, content: view)
+
+ router.viewController = viewController
+
+ return viewController
+ }
+}
diff --git a/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestPresenter.swift
new file mode 100644
index 000000000..194b5e8e3
--- /dev/null
+++ b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestPresenter.swift
@@ -0,0 +1,51 @@
+import UIKit
+import Combine
+import WalletConnectPush
+
+final class PushRequestPresenter: ObservableObject {
+ private let interactor: PushRequestInteractor
+ private let router: PushRequestRouter
+
+ let pushRequest: PushRequest
+
+ var message: String {
+ return String(describing: pushRequest.account)
+ }
+
+ private var disposeBag = Set()
+
+ init(
+ interactor: PushRequestInteractor,
+ router: PushRequestRouter,
+ pushRequest: PushRequest
+ ) {
+ defer { setupInitialState() }
+ self.interactor = interactor
+ self.router = router
+ self.pushRequest = pushRequest
+ }
+
+ @MainActor
+ func onApprove() async throws {
+ try await interactor.approve(pushRequest: pushRequest)
+ router.dismiss()
+ }
+
+ @MainActor
+ func onReject() async throws {
+ try await interactor.reject(pushRequest: pushRequest)
+ router.dismiss()
+ }
+}
+
+// MARK: - Private functions
+private extension PushRequestPresenter {
+ func setupInitialState() {
+
+ }
+}
+
+// MARK: - SceneViewModel
+extension PushRequestPresenter: SceneViewModel {
+
+}
diff --git a/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestRouter.swift b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestRouter.swift
new file mode 100644
index 000000000..6ac5f730c
--- /dev/null
+++ b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestRouter.swift
@@ -0,0 +1,15 @@
+import UIKit
+
+final class PushRequestRouter {
+ weak var viewController: UIViewController!
+
+ private let app: Application
+
+ init(app: Application) {
+ self.app = app
+ }
+
+ func dismiss() {
+ viewController.dismiss()
+ }
+}
diff --git a/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestView.swift b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestView.swift
new file mode 100644
index 000000000..62a21e17f
--- /dev/null
+++ b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestView.swift
@@ -0,0 +1,127 @@
+import SwiftUI
+
+struct PushRequestView: View {
+ @EnvironmentObject var presenter: PushRequestPresenter
+
+ @State var text = ""
+
+ var body: some View {
+ ZStack {
+ Color.black.opacity(0.6)
+
+ VStack {
+ Spacer()
+
+ VStack(spacing: 0) {
+ Image("header")
+ .resizable()
+ .scaledToFit()
+
+ Text("would you like to send notifications")
+ .foregroundColor(.grey8)
+ .font(.system(size: 22, weight: .bold, design: .rounded))
+ .padding(.top, 10)
+
+ pushRequestView()
+
+ HStack(spacing: 20) {
+ Button {
+ Task(priority: .userInitiated) { try await
+ presenter.onReject()
+ }
+ } label: {
+ Text("Decline")
+ .frame(maxWidth: .infinity)
+ .foregroundColor(.white)
+ .font(.system(size: 20, weight: .semibold, design: .rounded))
+ .padding(.vertical, 11)
+ .background(
+ LinearGradient(
+ gradient: Gradient(colors: [
+ .foregroundNegative,
+ .lightForegroundNegative
+ ]),
+ startPoint: .top, endPoint: .bottom)
+ )
+ .cornerRadius(20)
+ }
+ .shadow(color: .white.opacity(0.25), radius: 8, y: 2)
+
+ Button {
+ Task(priority: .userInitiated) { try await
+ presenter.onApprove()
+ }
+ } label: {
+ Text("Allow")
+ .frame(maxWidth: .infinity)
+ .foregroundColor(.white)
+ .font(.system(size: 20, weight: .semibold, design: .rounded))
+ .padding(.vertical, 11)
+ .background(
+ LinearGradient(
+ gradient: Gradient(colors: [
+ .foregroundPositive,
+ .lightForegroundPositive
+ ]),
+ startPoint: .top, endPoint: .bottom)
+ )
+ .cornerRadius(20)
+ }
+ .shadow(color: .white.opacity(0.25), radius: 8, y: 2)
+ }
+ .padding(.top, 25)
+ }
+ .padding(20)
+ .background(.ultraThinMaterial)
+ .cornerRadius(34)
+ .padding(.horizontal, 10)
+
+ Spacer()
+ }
+ }
+ .edgesIgnoringSafeArea(.all)
+ }
+
+ private func pushRequestView() -> some View {
+ VStack {
+ VStack(alignment: .leading) {
+ Text("Notifications")
+ .font(.system(size: 15, weight: .semibold, design: .rounded))
+ .foregroundColor(.whiteBackground)
+ .padding(.horizontal, 8)
+ .padding(.vertical, 5)
+ .background(Color.grey70)
+ .cornerRadius(28, corners: .allCorners)
+ .padding(.leading, 15)
+ .padding(.top, 9)
+
+ VStack(spacing: 0) {
+ ScrollView {
+ Text(presenter.message)
+ .foregroundColor(.grey50)
+ .font(.system(size: 13, weight: .semibold, design: .rounded))
+ }
+ .padding(.horizontal, 18)
+ .padding(.vertical, 10)
+ .frame(height: 250)
+ }
+ .background(Color.whiteBackground)
+ .cornerRadius(20, corners: .allCorners)
+ .padding(.horizontal, 5)
+ .padding(.bottom, 5)
+
+ }
+ .background(.thinMaterial)
+ .cornerRadius(25, corners: .allCorners)
+ }
+ .padding(.top, 30)
+ }
+}
+
+#if DEBUG
+struct PushRequestView_Previews: PreviewProvider {
+ static var previews: some View {
+ PushRequestView()
+ }
+}
+#endif
diff --git a/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalInteractor.swift b/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalInteractor.swift
index c88d43aef..fcbd47201 100644
--- a/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalInteractor.swift
+++ b/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalInteractor.swift
@@ -15,11 +15,7 @@ final class SessionProposalInteractor {
let proposalNamespace = $0.value
let accounts = Set(proposalNamespace.chains.compactMap { Account($0.absoluteString + ":\(self.accounts[$0.namespace]!)") })
- let extensions: [SessionNamespace.Extension]? = proposalNamespace.extensions?.map { element in
- let accounts = Set(element.chains.compactMap { Account($0.absoluteString + ":\(self.accounts[$0.namespace]!)") })
- return SessionNamespace.Extension(accounts: accounts, methods: element.methods, events: element.events)
- }
- let sessionNamespace = SessionNamespace(accounts: accounts, methods: proposalNamespace.methods, events: proposalNamespace.events, extensions: extensions)
+ let sessionNamespace = SessionNamespace(accounts: accounts, methods: proposalNamespace.methods, events: proposalNamespace.events)
sessionNamespaces[caip2Namespace] = sessionNamespace
}
diff --git a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletInteractor.swift b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletInteractor.swift
index b79c6b255..3a4e3601e 100644
--- a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletInteractor.swift
+++ b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletInteractor.swift
@@ -1,6 +1,7 @@
import Combine
import Web3Wallet
+import WalletConnectPush
final class WalletInteractor {
var requestPublisher: AnyPublisher {
@@ -15,10 +16,14 @@ final class WalletInteractor {
return Web3Wallet.instance.sessionRequestPublisher
}
+ var pushRequestPublisher: AnyPublisher<(id: RPCID, account: Account, metadata: AppMetadata), Never> {
+ return Push.wallet.requestPublisher
+ }
+
var sessionsPublisher: AnyPublisher<[Session], Never> {
return Web3Wallet.instance.sessionsPublisher
}
-
+
func getSessions() -> [Session] {
return Web3Wallet.instance.getSessions()
}
diff --git a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletPresenter.swift
index e6e677d8e..1e129ae89 100644
--- a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletPresenter.swift
+++ b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletPresenter.swift
@@ -71,7 +71,13 @@ extension WalletPresenter {
.sink { [weak self] sessionRequest in
self?.router.present(sessionRequest: sessionRequest)
}.store(in: &disposeBag)
-
+
+ interactor.pushRequestPublisher
+ .receive(on: DispatchQueue.main)
+ .sink { [weak self] request in
+ self?.router.present(pushRequest: request)
+ }.store(in: &disposeBag)
+
interactor.sessionProposalPublisher
.receive(on: DispatchQueue.main)
.sink { [weak self] proposal in
diff --git a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletRouter.swift b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletRouter.swift
index c32bfd8d3..8cc1c31ed 100644
--- a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletRouter.swift
+++ b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletRouter.swift
@@ -1,6 +1,7 @@
import UIKit
import Web3Wallet
+import WalletConnectPush
final class WalletRouter {
weak var viewController: UIViewController!
@@ -25,6 +26,11 @@ final class WalletRouter {
SessionRequestModule.create(app: app, sessionRequest: sessionRequest)
.presentFullScreen(from: viewController, transparentBackground: true)
}
+
+ func present(pushRequest: PushRequest) {
+ PushRequestModule.create(app: app, pushRequest: pushRequest)
+ .presentFullScreen(from: viewController, transparentBackground: true)
+ }
func presentPaste(onValue: @escaping (String) -> Void, onError: @escaping (Error) -> Void) {
PasteUriModule.create(app: app, onValue: onValue, onError: onError)
diff --git a/Example/WalletApp/WalletApp.entitlements b/Example/WalletApp/WalletApp.entitlements
new file mode 100644
index 000000000..2dccdcca9
--- /dev/null
+++ b/Example/WalletApp/WalletApp.entitlements
@@ -0,0 +1,12 @@
+
+
+
+
+ aps-environment
+ development
+ com.apple.security.application-groups
+
+ group.com.walletconnect.sdk
+
+
+
diff --git a/Example/WalletApp/WalletAppRelease.entitlements b/Example/WalletApp/WalletAppRelease.entitlements
new file mode 100644
index 000000000..315a4dbfc
--- /dev/null
+++ b/Example/WalletApp/WalletAppRelease.entitlements
@@ -0,0 +1,12 @@
+
+
+
+
+ aps-environment
+ production
+ com.apple.security.application-groups
+
+ group.com.walletconnect.sdk
+
+
+
diff --git a/Makefile b/Makefile
index 3ba7ee113..218b88441 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,40 @@ XCODE_USER_TEMPLATES_DIR=/Applications/Xcode.app/Contents/Developer/Library/Xcod
TEMPLATE_NAME=VIPER
TEMPLATES_DIR=Example/Templates/VIPER
+EXISTS_FASTLANE = $(shell command -v fastlane 2> /dev/null)
+
install_templates:
mkdir -p $(XCODE_USER_TEMPLATES_DIR)
rm -fR $(XCODE_USER_TEMPLATES_DIR)/$(TEMPLATE_NAME)
- cp -R $(TEMPLATES_DIR) $(XCODE_USER_TEMPLATES_DIR)
\ No newline at end of file
+ cp -R $(TEMPLATES_DIR) $(XCODE_USER_TEMPLATES_DIR)
+
+install_env:
+ifeq "${EXISTS_FASTLANE}" ""
+ @echo Installing fastlane
+ sudo gem install fastlane --no-document
+endif
+ @echo "All dependencies was installed"
+
+build_dapp:
+ fastlane build scheme:DApp
+
+build_wallet:
+ fastlane build scheme:WalletApp
+
+ui_tests:
+ echo "UI Tests disabled"
+
+unit_tests:
+ fastlane tests scheme:WalletConnect
+
+integration_tests:
+ fastlane tests scheme:IntegrationTests relay_host:$(RELAY_HOST) project_id:$(PROJECT_ID)
+
+resolve_packages:
+ fastlane resolve scheme:WalletApp
+
+release_wallet:
+ fastlane release_testflight username:$(APPLE_ID) --env WalletApp
+
+release_showcase:
+ fastlane release_testflight username:$(APPLE_ID) --env Showcase
\ No newline at end of file
diff --git a/Package.swift b/Package.swift
index 7110440bc..c633746d6 100644
--- a/Package.swift
+++ b/Package.swift
@@ -39,7 +39,10 @@ let package = Package(
targets: ["WalletConnectNetworking"]),
.library(
name: "WalletConnectVerify",
- targets: ["WalletConnectVerify"])
+ targets: ["WalletConnectVerify"]),
+ .library(
+ name: "Web3Inbox",
+ targets: ["Web3Inbox"]),
],
dependencies: [],
targets: [
@@ -79,6 +82,9 @@ let package = Package(
.target(
name: "WalletConnectPairing",
dependencies: ["WalletConnectNetworking"]),
+ .target(
+ name: "Web3Inbox",
+ dependencies: ["WalletConnectChat"]),
.target(
name: "WalletConnectUtils",
dependencies: ["JSONRPC"]),
diff --git a/README.md b/README.md
index d7724421b..9d0543d5f 100644
--- a/README.md
+++ b/README.md
@@ -50,7 +50,7 @@ open `Example/ExampleApp.xcodeproj`
## Web3Wallet
Web3Wallet SDK introduces a new interface for all wallets that wraps the Sign and Auth clients internally.
-- [Migration guide from Sign and Auth to Web3Wallet](https://github.com/WalletConnect/walletconnect-docs/blob/main/docs/swift/guides/web3wallet-migration.md)
+- [Migration guide from Sign and Auth to Web3Wallet](https://github.com/WalletConnect/walletconnect-docs/blob/main/docs/swift/web3wallet/upgrade-guide.md)
## License
diff --git a/Sources/Chat/Chat.swift b/Sources/Chat/Chat.swift
index 9c10e0492..31430fe0b 100644
--- a/Sources/Chat/Chat.swift
+++ b/Sources/Chat/Chat.swift
@@ -5,8 +5,20 @@ public class Chat {
/// Chat client instance
public static var instance: ChatClient = {
- return ChatClientFactory.create()
+ guard let account = account else {
+ fatalError("Error - you must call Chat.configure(_:) before accessing the shared instance.")
+ }
+ return ChatClientFactory.create(account: account)
}()
+ private static var account: Account?
+
private init() { }
+
+ /// Chat instance config method
+ /// - Parameters:
+ /// - account: Chat initial account
+ static public func configure(account: Account) {
+ Chat.account = account
+ }
}
diff --git a/Sources/Chat/ChatClient.swift b/Sources/Chat/ChatClient.swift
index b4c529076..57cdf66bf 100644
--- a/Sources/Chat/ChatClient.swift
+++ b/Sources/Chat/ChatClient.swift
@@ -6,14 +6,12 @@ public class ChatClient {
private let registry: Registry
private let registryService: RegistryService
private let messagingService: MessagingService
+ private let accountService: AccountService
private let invitationHandlingService: InvitationHandlingService
private let inviteService: InviteService
private let leaveService: LeaveService
- private let resubscriptionService: ResubscriptionService
private let kms: KeyManagementService
- private let threadStore: Database
- private let messagesStore: Database
- private let invitePayloadStore: CodableStore>
+ private let chatStorage: ChatStorage
public let socketConnectionStatusPublisher: AnyPublisher
@@ -37,27 +35,23 @@ public class ChatClient {
init(registry: Registry,
registryService: RegistryService,
messagingService: MessagingService,
+ accountService: AccountService,
invitationHandlingService: InvitationHandlingService,
inviteService: InviteService,
leaveService: LeaveService,
- resubscriptionService: ResubscriptionService,
kms: KeyManagementService,
- threadStore: Database,
- messagesStore: Database,
- invitePayloadStore: CodableStore>,
+ chatStorage: ChatStorage,
socketConnectionStatusPublisher: AnyPublisher
) {
self.registry = registry
self.registryService = registryService
self.messagingService = messagingService
+ self.accountService = accountService
self.invitationHandlingService = invitationHandlingService
self.inviteService = inviteService
self.leaveService = leaveService
- self.resubscriptionService = resubscriptionService
self.kms = kms
- self.threadStore = threadStore
- self.messagesStore = messagesStore
- self.invitePayloadStore = invitePayloadStore
+ self.chatStorage = chatStorage
self.socketConnectionStatusPublisher = socketConnectionStatusPublisher
setUpEnginesCallbacks()
@@ -86,15 +80,15 @@ public class ChatClient {
/// - publicKey: publicKey associated with a peer
/// - openingMessage: oppening message for a chat invite
/// TODO - peerAccount should be derived
- public func invite(peerAccount: Account, openingMessage: String, account: Account) async throws {
- try await inviteService.invite(peerAccount: peerAccount, openingMessage: openingMessage, account: account)
+ public func invite(peerAccount: Account, openingMessage: String) async throws {
+ try await inviteService.invite(peerAccount: peerAccount, openingMessage: openingMessage)
}
- public func accept(inviteId: String) async throws {
+ public func accept(inviteId: Int64) async throws {
try await invitationHandlingService.accept(inviteId: inviteId)
}
- public func reject(inviteId: String) async throws {
+ public func reject(inviteId: Int64) async throws {
try await invitationHandlingService.reject(inviteId: inviteId)
}
@@ -116,16 +110,16 @@ public class ChatClient {
try await leaveService.leave(topic: topic)
}
- public func getInvites(account: Account) -> [Invite] {
- return invitePayloadStore.getAll().map { $0.request }
+ public func getInvites() -> [Invite] {
+ return chatStorage.getInvites(account: accountService.currentAccount)
}
- public func getThreads() async -> [Thread] {
- await threadStore.getAll()
+ public func getThreads() -> [Thread] {
+ return chatStorage.getThreads(account: accountService.currentAccount)
}
- public func getMessages(topic: String) async -> [Message] {
- await messagesStore.filter {$0.topic == topic} ?? []
+ public func getMessages(topic: String) -> [Message] {
+ return chatStorage.getMessages(topic: topic, account: accountService.currentAccount)
}
private func setUpEnginesCallbacks() {
diff --git a/Sources/Chat/ChatClientFactory.swift b/Sources/Chat/ChatClientFactory.swift
index 84b79bc4c..526261241 100644
--- a/Sources/Chat/ChatClientFactory.swift
+++ b/Sources/Chat/ChatClientFactory.swift
@@ -2,11 +2,12 @@ import Foundation
public struct ChatClientFactory {
- static func create() -> ChatClient {
+ static func create(account: Account) -> ChatClient {
let keychain = KeychainStorage(serviceIdentifier: "com.walletconnect.showcase")
let client = HTTPNetworkClient(host: "keys.walletconnect.com")
let registry = KeyserverRegistryProvider(client: client)
return ChatClientFactory.create(
+ account: account,
registry: registry,
relayClient: Relay.instance,
kms: KeyManagementService(keychain: keychain),
@@ -16,6 +17,7 @@ public struct ChatClientFactory {
}
public static func create(
+ account: Account,
registry: Registry,
relayClient: RelayClient,
kms: KeyManagementService,
@@ -26,28 +28,28 @@ public struct ChatClientFactory {
let serialiser = Serializer(kms: kms)
let rpcHistory = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage)
let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serialiser, logger: logger, rpcHistory: rpcHistory)
- let invitePayloadStore = CodableStore>(defaults: keyValueStorage, identifier: ChatStorageIdentifiers.invite.rawValue)
- let registryService = RegistryService(registry: registry, networkingInteractor: networkingInteractor, kms: kms, logger: logger, topicToRegistryRecordStore: topicToRegistryRecordStore)
- let threadStore = Database(keyValueStorage: keyValueStorage, identifier: ChatStorageIdentifiers.threads.rawValue)
- let resubscriptionService = ResubscriptionService(networkingInteractor: networkingInteractor, threadStore: threadStore, logger: logger)
- let invitationHandlingService = InvitationHandlingService(registry: registry, networkingInteractor: networkingInteractor, kms: kms, logger: logger, topicToRegistryRecordStore: topicToRegistryRecordStore, invitePayloadStore: invitePayloadStore, threadsStore: threadStore)
- let inviteService = InviteService(networkingInteractor: networkingInteractor, kms: kms, threadStore: threadStore, rpcHistory: rpcHistory, logger: logger, registry: registry)
+ let messageStore = KeyedDatabase(storage: keyValueStorage, identifier: ChatStorageIdentifiers.messages.rawValue)
+ let inviteStore = KeyedDatabase(storage: keyValueStorage, identifier: ChatStorageIdentifiers.invites.rawValue)
+ let threadStore = KeyedDatabase(storage: keyValueStorage, identifier: ChatStorageIdentifiers.threads.rawValue)
+ let accountService = AccountService(currentAccount: account)
+ let chatStorage = ChatStorage(messageStore: messageStore, inviteStore: inviteStore, threadStore: threadStore)
+ let resubscriptionService = ResubscriptionService(networkingInteractor: networkingInteractor, accountService: accountService, chatStorage: chatStorage, logger: logger)
+ let registryService = RegistryService(registry: registry, accountService: accountService, resubscriptionService: resubscriptionService, networkingInteractor: networkingInteractor, kms: kms, logger: logger, topicToRegistryRecordStore: topicToRegistryRecordStore)
+ let invitationHandlingService = InvitationHandlingService(registry: registry, networkingInteractor: networkingInteractor, accountService: accountService, kms: kms, logger: logger, topicToRegistryRecordStore: topicToRegistryRecordStore, chatStorage: chatStorage)
+ let inviteService = InviteService(networkingInteractor: networkingInteractor, accountService: accountService, kms: kms, chatStorage: chatStorage, logger: logger, registry: registry)
let leaveService = LeaveService()
- let messagesStore = Database(keyValueStorage: keyValueStorage, identifier: ChatStorageIdentifiers.messages.rawValue)
- let messagingService = MessagingService(networkingInteractor: networkingInteractor, messagesStore: messagesStore, threadStore: threadStore, logger: logger)
+ let messagingService = MessagingService(networkingInteractor: networkingInteractor, accountService: accountService, chatStorage: chatStorage, logger: logger)
let client = ChatClient(
registry: registry,
registryService: registryService,
messagingService: messagingService,
+ accountService: accountService,
invitationHandlingService: invitationHandlingService,
inviteService: inviteService,
leaveService: leaveService,
- resubscriptionService: resubscriptionService,
kms: kms,
- threadStore: threadStore,
- messagesStore: messagesStore,
- invitePayloadStore: invitePayloadStore,
+ chatStorage: chatStorage,
socketConnectionStatusPublisher: relayClient.socketConnectionStatusPublisher
)
diff --git a/Sources/Chat/ChatStorage.swift b/Sources/Chat/ChatStorage.swift
new file mode 100644
index 000000000..f6f0c0a72
--- /dev/null
+++ b/Sources/Chat/ChatStorage.swift
@@ -0,0 +1,65 @@
+import Foundation
+
+struct ChatStorage {
+
+ private let messageStore: KeyedDatabase
+ private let inviteStore: KeyedDatabase
+ private let threadStore: KeyedDatabase
+
+ init(
+ messageStore: KeyedDatabase,
+ inviteStore: KeyedDatabase,
+ threadStore: KeyedDatabase
+ ) {
+ self.messageStore = messageStore
+ self.inviteStore = inviteStore
+ self.threadStore = threadStore
+ }
+
+ // MARK: - Invites
+
+ func getInvite(id: Int64, account: Account) -> Invite? {
+ return inviteStore.getElements(for: account.absoluteString)
+ .first(where: { $0.id == id })
+ }
+
+ func set(invite: Invite, account: Account) {
+ inviteStore.set(invite, for: account.absoluteString)
+ }
+
+ func getInviteTopic(id: Int64, account: Account) -> String? {
+ return getInvites(account: account).first(where: { $0.id == id })?.topic
+ }
+
+ func getInvites(account: Account) -> [Invite] {
+ return inviteStore.getElements(for: account.absoluteString)
+ }
+
+ func delete(invite: Invite, account: Account) {
+ inviteStore.delete(invite, for: account.absoluteString)
+ }
+
+ // MARK: - Threads
+
+ func getThreads(account: Account) -> [Thread] {
+ return threadStore.getElements(for: account.absoluteString)
+ }
+
+ func set(thread: Thread, account: Account) {
+ threadStore.set(thread, for: account.absoluteString)
+ }
+
+ // MARK: - Messages
+
+ func set(message: Message, account: Account) {
+ messageStore.set(message, for: account.absoluteString)
+ }
+
+ func getMessages(account: Account) -> [Message] {
+ return messageStore.getElements(for: account.absoluteString)
+ }
+
+ func getMessages(topic: String, account: Account) -> [Message] {
+ return messageStore.getElements(for: account.absoluteString).filter { $0.topic == topic }
+ }
+}
diff --git a/Sources/Chat/ChatStorageIdentifiers.swift b/Sources/Chat/ChatStorageIdentifiers.swift
index bcf560ed3..e9eda634d 100644
--- a/Sources/Chat/ChatStorageIdentifiers.swift
+++ b/Sources/Chat/ChatStorageIdentifiers.swift
@@ -2,8 +2,7 @@ import Foundation
enum ChatStorageIdentifiers: String {
case topicToInvitationPubKey = "com.walletconnect.chat.topicToInvitationPubKey"
- case invite = "com.walletconnect.chat.invite"
- case jsonRpcHistory = "com.walletconnect.chat.jsonRpcHistory"
- case threads = "com.walletconnect.chat.threads"
case messages = "com.walletconnect.chat.messages"
+ case threads = "com.walletconnect.chat.threads"
+ case invites = "com.walletconnect.chat.invites"
}
diff --git a/Sources/Chat/Database.swift b/Sources/Chat/Database.swift
deleted file mode 100644
index 08e6f1360..000000000
--- a/Sources/Chat/Database.swift
+++ /dev/null
@@ -1,37 +0,0 @@
-import Foundation
-
-class Database where Element: Codable {
-
- private var array = [Element]()
- private let keyValueStorage: KeyValueStorage
- private let identifier: String
-
- init(keyValueStorage: KeyValueStorage,
- identifier: String) {
- self.keyValueStorage = keyValueStorage
- self.identifier = identifier
- if let data = keyValueStorage.object(forKey: identifier) as? Data,
- let decoded = try? JSONDecoder().decode([Element].self, from: data) {
- array = decoded
- }
- }
-
- func filter(_ isIncluded: (Element) -> Bool) async -> [Element]? {
- return Array(self.array.filter(isIncluded))
- }
-
- func getAll() async -> [Element] {
- array
- }
-
- func add(_ element: Element) async {
- self.array.append(element)
- if let encoded = try? JSONEncoder().encode(array) {
- keyValueStorage.set(encoded, forKey: identifier)
- }
- }
-
- func first(where predicate: (Element) -> Bool) async -> Element? {
- self.array.first(where: predicate)
- }
-}
diff --git a/Sources/Chat/Extensions/Dictionary.swift b/Sources/Chat/Extensions/Dictionary.swift
new file mode 100644
index 000000000..90fb7ffc6
--- /dev/null
+++ b/Sources/Chat/Extensions/Dictionary.swift
@@ -0,0 +1,19 @@
+import Foundation
+
+extension Dictionary where Value: RangeReplaceableCollection, Value.Element: Equatable {
+
+ mutating func append(_ element: Value.Iterator.Element, for key: Key) {
+ var value: Value = self[key] ?? Value()
+ value.append(element)
+ self[key] = value
+ }
+
+ mutating func delete(_ element: Value.Iterator.Element, for key: Key) {
+ guard
+ let value: Value = self[key],
+ value.contains(where: { $0 == element })
+ else { return }
+
+ self[key] = value.filter { $0 != element }
+ }
+}
diff --git a/Sources/Chat/KeyedDatabase.swift b/Sources/Chat/KeyedDatabase.swift
new file mode 100644
index 000000000..bc110e572
--- /dev/null
+++ b/Sources/Chat/KeyedDatabase.swift
@@ -0,0 +1,47 @@
+import Foundation
+
+class KeyedDatabase where Element: Codable & Equatable {
+
+ private var index: [String: [Element]] = [:] {
+ didSet { self.set(index, for: identifier) }
+ }
+
+ private let storage: KeyValueStorage
+ private let identifier: String
+
+ init(storage: KeyValueStorage, identifier: String) {
+ self.storage = storage
+ self.identifier = identifier
+
+ initializeIndex()
+ }
+
+ func getElements(for key: String) -> [Element] {
+ return index[key] ?? []
+ }
+
+ func set(_ element: Element, for key: String) {
+ index.append(element, for: key)
+ }
+
+ func delete(_ element: Element, for key: String) {
+ index.delete(element, for: key)
+ }
+}
+
+private extension KeyedDatabase {
+
+ func initializeIndex() {
+ guard
+ let data = storage.object(forKey: identifier) as? Data,
+ let decoded = try? JSONDecoder().decode([String: [Element]].self, from: data)
+ else { return }
+
+ index = decoded
+ }
+
+ func set(_ value: [String: [Element]], for key: String) {
+ let data = try! JSONEncoder().encode(value)
+ storage.set(data, forKey: key)
+ }
+}
diff --git a/Sources/Chat/ProtocolServices/Accounts/AccountService.swift b/Sources/Chat/ProtocolServices/Accounts/AccountService.swift
new file mode 100644
index 000000000..e0343aa0d
--- /dev/null
+++ b/Sources/Chat/ProtocolServices/Accounts/AccountService.swift
@@ -0,0 +1,14 @@
+import Foundation
+
+final class AccountService {
+
+ private(set) var currentAccount: Account
+
+ init(currentAccount: Account) {
+ self.currentAccount = currentAccount
+ }
+
+ func setAccount(_ account: Account) {
+ currentAccount = account
+ }
+}
diff --git a/Sources/Chat/ProtocolServices/Common/MessagingService.swift b/Sources/Chat/ProtocolServices/Common/MessagingService.swift
index b9de3db59..323fe66ba 100644
--- a/Sources/Chat/ProtocolServices/Common/MessagingService.swift
+++ b/Sources/Chat/ProtocolServices/Common/MessagingService.swift
@@ -5,38 +5,42 @@ class MessagingService {
enum Errors: Error {
case threadDoNotExist
}
- let networkingInteractor: NetworkInteracting
- var messagesStore: Database
- let logger: ConsoleLogging
+
var onMessage: ((Message) -> Void)?
- var threadStore: Database
+
+ private let networkingInteractor: NetworkInteracting
+ private let accountService: AccountService
+ private let chatStorage: ChatStorage
+ private let logger: ConsoleLogging
+
private var publishers = [AnyCancellable]()
+ private var currentAccount: Account {
+ return accountService.currentAccount
+ }
+
init(networkingInteractor: NetworkInteracting,
- messagesStore: Database,
- threadStore: Database,
+ accountService: AccountService,
+ chatStorage: ChatStorage,
logger: ConsoleLogging) {
self.networkingInteractor = networkingInteractor
- self.messagesStore = messagesStore
+ self.accountService = accountService
+ self.chatStorage = chatStorage
self.logger = logger
- self.threadStore = threadStore
setUpResponseHandling()
setUpRequestHandling()
}
func send(topic: String, messageString: String) async throws {
- // TODO - manage author account
- let protocolMethod = ChatMessageProtocolMethod()
- let thread = await threadStore.first {$0.topic == topic}
- guard let authorAccount = thread?.selfAccount else { throw Errors.threadDoNotExist}
let timestamp = Int64(Date().timeIntervalSince1970 * 1000)
- let message = Message(topic: topic, message: messageString, authorAccount: authorAccount, timestamp: timestamp)
+ let message = Message(topic: topic, message: messageString, authorAccount: currentAccount, timestamp: timestamp)
+
+ let protocolMethod = ChatMessageProtocolMethod()
let request = RPCRequest(method: protocolMethod.method, params: message)
try await networkingInteractor.request(request, topic: topic, protocolMethod: protocolMethod)
- Task(priority: .background) {
- await messagesStore.add(message)
- onMessage?(message)
- }
+
+ chatStorage.set(message: message, account: currentAccount)
+ onMessage?(message)
}
private func setUpResponseHandling() {
@@ -48,18 +52,21 @@ class MessagingService {
private func setUpRequestHandling() {
networkingInteractor.requestSubscription(on: ChatMessageProtocolMethod())
- .sink { [unowned self] (payload: RequestSubscriptionPayload) in
- var message = payload.request
- message.topic = payload.topic
+ .sink { [unowned self] (payload: RequestSubscriptionPayload) in
+ let message = Message(topic: payload.topic, payload: payload.request)
handleMessage(message, topic: payload.topic, requestId: payload.id)
}.store(in: &publishers)
}
private func handleMessage(_ message: Message, topic: String, requestId: RPCID) {
- Task(priority: .background) {
- try await networkingInteractor.respondSuccess(topic: topic, requestId: requestId, protocolMethod: ChatMessageProtocolMethod())
- await messagesStore.add(message)
+ Task(priority: .high) {
+ try await networkingInteractor.respondSuccess(
+ topic: topic,
+ requestId: requestId,
+ protocolMethod: ChatMessageProtocolMethod()
+ )
logger.debug("Received message")
+ chatStorage.set(message: message, account: currentAccount)
onMessage?(message)
}
}
diff --git a/Sources/Chat/ProtocolServices/Common/ResubscriptionService.swift b/Sources/Chat/ProtocolServices/Common/ResubscriptionService.swift
index b01b901ae..754ceedfc 100644
--- a/Sources/Chat/ProtocolServices/Common/ResubscriptionService.swift
+++ b/Sources/Chat/ProtocolServices/Common/ResubscriptionService.swift
@@ -3,16 +3,19 @@ import Combine
class ResubscriptionService {
private let networkingInteractor: NetworkInteracting
+ private let accountService: AccountService
private let logger: ConsoleLogging
- private var threadStore: Database
+ private var chatStorage: ChatStorage
private var publishers = [AnyCancellable]()
init(networkingInteractor: NetworkInteracting,
- threadStore: Database,
+ accountService: AccountService,
+ chatStorage: ChatStorage,
logger: ConsoleLogging) {
self.networkingInteractor = networkingInteractor
+ self.accountService = accountService
self.logger = logger
- self.threadStore = threadStore
+ self.chatStorage = chatStorage
setUpResubscription()
}
@@ -20,10 +23,20 @@ class ResubscriptionService {
networkingInteractor.socketConnectionStatusPublisher
.sink { [unowned self] status in
guard status == .connected else { return }
- Task(priority: .background) {
- let topics = await threadStore.getAll().map {$0.topic}
- topics.forEach { topic in Task(priority: .background) { try? await networkingInteractor.subscribe(topic: topic) } }
+
+ Task(priority: .high) {
+ try await resubscribe(account: accountService.currentAccount)
}
}.store(in: &publishers)
}
+
+ func resubscribe(account: Account) async throws {
+ let topics = chatStorage.getThreads(account: account).map { $0.topic }
+ try await networkingInteractor.batchSubscribe(topics: topics)
+ }
+
+ func unsubscribe(account: Account) async throws {
+ let topics = chatStorage.getThreads(account: account).map { $0.topic }
+ try await networkingInteractor.batchUnsubscribe(topics: topics)
+ }
}
diff --git a/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift b/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift
index db0c13599..27f7b14c0 100644
--- a/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift
+++ b/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift
@@ -8,45 +8,59 @@ class InvitationHandlingService {
var onInvite: ((Invite) -> Void)?
var onNewThread: ((Thread) -> Void)?
private let networkingInteractor: NetworkInteracting
- private let invitePayloadStore: CodableStore>
+ private let chatStorage: ChatStorage
+ private let accountService: AccountService
private let topicToRegistryRecordStore: CodableStore
private let registry: Registry
private let logger: ConsoleLogging
private let kms: KeyManagementService
- private let threadsStore: Database
private var publishers = [AnyCancellable]()
+ private var currentAccount: Account {
+ return accountService.currentAccount
+ }
+
init(registry: Registry,
networkingInteractor: NetworkInteracting,
+ accountService: AccountService,
kms: KeyManagementService,
logger: ConsoleLogging,
topicToRegistryRecordStore: CodableStore,
- invitePayloadStore: CodableStore>,
- threadsStore: Database) {
+ chatStorage: ChatStorage) {
self.registry = registry
self.kms = kms
self.networkingInteractor = networkingInteractor
+ self.accountService = accountService
self.logger = logger
self.topicToRegistryRecordStore = topicToRegistryRecordStore
- self.invitePayloadStore = invitePayloadStore
- self.threadsStore = threadsStore
+ self.chatStorage = chatStorage
setUpRequestHandling()
}
- func accept(inviteId: String) async throws {
- let protocolMethod = ChatInviteProtocolMethod()
-
- guard let payload = try invitePayloadStore.get(key: inviteId) else { throw Error.inviteForIdNotFound }
+ func accept(inviteId: Int64) async throws {
+ guard
+ let invite = chatStorage.getInvite(id: inviteId, account: currentAccount),
+ let inviteTopic = chatStorage.getInviteTopic(id: inviteId, account: currentAccount)
+ else { throw Error.inviteForIdNotFound }
let selfThreadPubKey = try kms.createX25519KeyPair()
let inviteResponse = InviteResponse(publicKey: selfThreadPubKey.hexRepresentation)
- let response = RPCResponse(id: payload.id, result: inviteResponse)
- let responseTopic = try getInviteResponseTopic(requestTopic: payload.topic, invite: payload.request)
- try await networkingInteractor.respond(topic: responseTopic, response: response, protocolMethod: protocolMethod)
-
- let threadAgreementKeys = try kms.performKeyAgreement(selfPublicKey: selfThreadPubKey, peerPublicKey: payload.request.publicKey)
+ let responseTopic = try getInviteResponseTopic(
+ requestTopic: inviteTopic,
+ invite: invite
+ )
+ try await networkingInteractor.respond(
+ topic: responseTopic,
+ response: RPCResponse(id: inviteId, result: inviteResponse),
+ protocolMethod: ChatInviteProtocolMethod()
+ )
+
+ let threadAgreementKeys = try kms.performKeyAgreement(
+ selfPublicKey: selfThreadPubKey,
+ peerPublicKey: invite.publicKey
+ )
let threadTopic = threadAgreementKeys.derivedTopic()
try kms.setSymmetricKey(threadAgreementKeys.sharedKey, for: threadTopic)
try await networkingInteractor.subscribe(topic: threadTopic)
@@ -54,31 +68,49 @@ class InvitationHandlingService {
logger.debug("Accepting an invite on topic: \(threadTopic)")
// TODO - derive account
- let selfAccount = try! topicToRegistryRecordStore.get(key: payload.topic)!.account
- let thread = Thread(topic: threadTopic, selfAccount: selfAccount, peerAccount: payload.request.account)
- await threadsStore.add(thread)
+ let selfAccount = try! topicToRegistryRecordStore.get(key: inviteTopic)!.account
+
+ let thread = Thread(
+ topic: threadTopic,
+ selfAccount: selfAccount,
+ peerAccount: invite.account
+ )
- invitePayloadStore.delete(forKey: inviteId)
+ chatStorage.set(thread: thread, account: currentAccount)
+ chatStorage.delete(invite: invite, account: currentAccount)
onNewThread?(thread)
}
- func reject(inviteId: String) async throws {
- guard let payload = try invitePayloadStore.get(key: inviteId) else { throw Error.inviteForIdNotFound }
+ func reject(inviteId: Int64) async throws {
+ guard
+ let invite = chatStorage.getInvite(id: inviteId, account: currentAccount),
+ let inviteTopic = chatStorage.getInviteTopic(id: inviteId, account: currentAccount)
+ else { throw Error.inviteForIdNotFound }
- let responseTopic = try getInviteResponseTopic(requestTopic: payload.topic, invite: payload.request)
+ let responseTopic = try getInviteResponseTopic(requestTopic: inviteTopic, invite: invite)
- try await networkingInteractor.respondError(topic: responseTopic, requestId: payload.id, protocolMethod: ChatInviteProtocolMethod(), reason: ChatError.userRejected)
+ try await networkingInteractor.respondError(
+ topic: responseTopic,
+ requestId: RPCID(inviteId),
+ protocolMethod: ChatInviteProtocolMethod(),
+ reason: ChatError.userRejected
+ )
- invitePayloadStore.delete(forKey: inviteId)
+ chatStorage.delete(invite: invite, account: currentAccount)
}
private func setUpRequestHandling() {
networkingInteractor.requestSubscription(on: ChatInviteProtocolMethod())
- .sink { [unowned self] (payload: RequestSubscriptionPayload) in
+ .sink { [unowned self] (payload: RequestSubscriptionPayload) in
logger.debug("did receive an invite")
- invitePayloadStore.set(payload, forKey: payload.request.publicKey)
- onInvite?(payload.request)
+ let invite = Invite(
+ id: payload.id.integer,
+ topic: payload.topic,
+ payload: payload.request
+ )
+ chatStorage.set(invite: invite, account: currentAccount)
+ onInvite?(invite)
}.store(in: &publishers)
}
diff --git a/Sources/Chat/ProtocolServices/Invitee/RegistryService.swift b/Sources/Chat/ProtocolServices/Invitee/RegistryService.swift
index 002102fb3..47f34e155 100644
--- a/Sources/Chat/ProtocolServices/Invitee/RegistryService.swift
+++ b/Sources/Chat/ProtocolServices/Invitee/RegistryService.swift
@@ -1,19 +1,25 @@
import Foundation
actor RegistryService {
- let networkingInteractor: NetworkInteracting
- let topicToRegistryRecordStore: CodableStore
- let registry: Registry
- let logger: ConsoleLogging
- let kms: KeyManagementServiceProtocol
+ private let networkingInteractor: NetworkInteracting
+ private let accountService: AccountService
+ private let resubscriptionService: ResubscriptionService
+ private let topicToRegistryRecordStore: CodableStore
+ private let registry: Registry
+ private let logger: ConsoleLogging
+ private let kms: KeyManagementServiceProtocol
init(registry: Registry,
+ accountService: AccountService,
+ resubscriptionService: ResubscriptionService,
networkingInteractor: NetworkInteracting,
kms: KeyManagementServiceProtocol,
logger: ConsoleLogging,
topicToRegistryRecordStore: CodableStore) {
self.registry = registry
self.kms = kms
+ self.accountService = accountService
+ self.resubscriptionService = resubscriptionService
self.networkingInteractor = networkingInteractor
self.logger = logger
self.topicToRegistryRecordStore = topicToRegistryRecordStore
@@ -23,12 +29,22 @@ actor RegistryService {
let pubKey = try kms.createX25519KeyPair()
let pubKeyHex = pubKey.hexRepresentation
try await registry.register(account: account, pubKey: pubKeyHex)
+
let topic = pubKey.rawRepresentation.sha256().toHexString()
try kms.setPublicKey(publicKey: pubKey, for: topic)
+
let record = RegistryRecord(account: account, pubKey: pubKeyHex)
topicToRegistryRecordStore.set(record, forKey: topic)
+
try await networkingInteractor.subscribe(topic: topic)
+
+ let oldAccount = accountService.currentAccount
+ try await resubscriptionService.unsubscribe(account: oldAccount)
+ accountService.setAccount(account)
+ try await resubscriptionService.resubscribe(account: account)
+
logger.debug("Did register an account: \(account) and is subscribing on topic: \(topic)")
+
return pubKeyHex
}
}
diff --git a/Sources/Chat/ProtocolServices/Inviter/InviteService.swift b/Sources/Chat/ProtocolServices/Inviter/InviteService.swift
index f8b2339c3..8a357fba6 100644
--- a/Sources/Chat/ProtocolServices/Inviter/InviteService.swift
+++ b/Sources/Chat/ProtocolServices/Inviter/InviteService.swift
@@ -4,10 +4,10 @@ import Combine
class InviteService {
private var publishers = [AnyCancellable]()
private let networkingInteractor: NetworkInteracting
+ private let accountService: AccountService
private let logger: ConsoleLogging
private let kms: KeyManagementService
- private let threadStore: Database
- private let rpcHistory: RPCHistory
+ private let chatStorage: ChatStorage
private let registry: Registry
var onNewThread: ((Thread) -> Void)?
@@ -15,29 +15,28 @@ class InviteService {
init(
networkingInteractor: NetworkInteracting,
+ accountService: AccountService,
kms: KeyManagementService,
- threadStore: Database,
- rpcHistory: RPCHistory,
+ chatStorage: ChatStorage,
logger: ConsoleLogging,
registry: Registry
) {
self.kms = kms
self.networkingInteractor = networkingInteractor
+ self.accountService = accountService
self.logger = logger
- self.threadStore = threadStore
- self.rpcHistory = rpcHistory
+ self.chatStorage = chatStorage
self.registry = registry
setUpResponseHandling()
}
var peerAccount: Account!
- func invite(peerAccount: Account, openingMessage: String, account: Account) async throws {
+ func invite(peerAccount: Account, openingMessage: String) async throws {
// TODO ad storage
let protocolMethod = ChatInviteProtocolMethod()
self.peerAccount = peerAccount
let selfPubKeyY = try kms.createX25519KeyPair()
- let invite = Invite(message: openingMessage, account: account, publicKey: selfPubKeyY.hexRepresentation)
let peerPubKey = try await registry.resolve(account: peerAccount)
let symKeyI = try kms.performKeyAgreement(selfPublicKey: selfPubKeyY, peerPublicKey: peerPubKey)
let inviteTopic = try AgreementPublicKey(hex: peerPubKey).rawRepresentation.sha256().toHexString()
@@ -45,6 +44,7 @@ class InviteService {
// overrides on invite toipic
try kms.setSymmetricKey(symKeyI.sharedKey, for: inviteTopic)
+ let invite = InvitePayload(message: openingMessage, account: accountService.currentAccount, publicKey: selfPubKeyY.hexRepresentation)
let request = RPCRequest(method: protocolMethod.method, params: invite)
// 2. Proposer subscribes to topic R which is the hash of the derived symKey
@@ -60,10 +60,10 @@ class InviteService {
private func setUpResponseHandling() {
networkingInteractor.responseSubscription(on: ChatInviteProtocolMethod())
- .sink { [unowned self] (payload: ResponseSubscriptionPayload) in
+ .sink { [unowned self] (payload: ResponseSubscriptionPayload) in
logger.debug("Invite has been accepted")
- Task(priority: .background) {
+ Task(priority: .high) {
try await createThread(
selfPubKeyHex: payload.request.publicKey,
peerPubKey: payload.response.publicKey,
@@ -79,9 +79,17 @@ class InviteService {
let agreementKeys = try kms.performKeyAgreement(selfPublicKey: selfPubKey, peerPublicKey: peerPubKey)
let threadTopic = agreementKeys.derivedTopic()
try kms.setSymmetricKey(agreementKeys.sharedKey, for: threadTopic)
+
try await networkingInteractor.subscribe(topic: threadTopic)
- let thread = Thread(topic: threadTopic, selfAccount: account, peerAccount: peerAccount)
- await threadStore.add(thread)
+
+ let thread = Thread(
+ topic: threadTopic,
+ selfAccount: account,
+ peerAccount: peerAccount
+ )
+
+ chatStorage.set(thread: thread, account: accountService.currentAccount)
+
onNewThread?(thread)
// TODO - remove symKeyI
}
diff --git a/Sources/Chat/Types/Invite.swift b/Sources/Chat/Types/Invite.swift
index dc3dcb490..4815731d7 100644
--- a/Sources/Chat/Types/Invite.swift
+++ b/Sources/Chat/Types/Invite.swift
@@ -1,22 +1,27 @@
import Foundation
-struct InviteResponse: Codable {
- let publicKey: String
-}
-
public struct Invite: Codable, Equatable {
- public var id: String {
- return publicKey
- }
+ public let id: Int64
+ public let topic: String
public let message: String
public let account: Account
public let publicKey: String
- static var tag: Int {
- return 2000
+ init(id: Int64, topic: String, payload: InvitePayload) {
+ self.id = id
+ self.topic = topic
+ self.message = payload.message
+ self.account = payload.account
+ self.publicKey = payload.publicKey
}
+}
- static var method: String {
- return "wc_chatInvite"
- }
+struct InviteResponse: Codable {
+ let publicKey: String
+}
+
+struct InvitePayload: Codable {
+ let message: String
+ let account: Account
+ let publicKey: String
}
diff --git a/Sources/Chat/Types/Message.swift b/Sources/Chat/Types/Message.swift
index ac4e1b33e..ba6e3ac43 100644
--- a/Sources/Chat/Types/Message.swift
+++ b/Sources/Chat/Types/Message.swift
@@ -1,22 +1,28 @@
import Foundation
public struct Message: Codable, Equatable {
- public var topic: String?
+ public let topic: String
public let message: String
public let authorAccount: Account
public let timestamp: Int64
- enum CodingKeys: String, CodingKey {
- case topic
- case message
- case authorAccount
- case timestamp
- }
-
- init(topic: String? = nil, message: String, authorAccount: Account, timestamp: Int64) {
+ init(topic: String, message: String, authorAccount: Account, timestamp: Int64) {
self.topic = topic
self.message = message
self.authorAccount = authorAccount
self.timestamp = timestamp
}
+
+ init(topic: String, payload: MessagePayload) {
+ self.topic = topic
+ self.message = payload.message
+ self.authorAccount = payload.authorAccount
+ self.timestamp = payload.timestamp
+ }
+}
+
+public struct MessagePayload: Codable {
+ public let message: String
+ public let authorAccount: Account
+ public let timestamp: Int64
}
diff --git a/Sources/Chat/Types/Thread.swift b/Sources/Chat/Types/Thread.swift
index 8768c20a6..ed5ef5ca0 100644
--- a/Sources/Chat/Types/Thread.swift
+++ b/Sources/Chat/Types/Thread.swift
@@ -1,6 +1,6 @@
import Foundation
-public struct Thread: Codable {
+public struct Thread: Codable, Equatable {
public let topic: String
public let selfAccount: Account
public let peerAccount: Account
diff --git a/Sources/Commons/AnyCodable.swift b/Sources/Commons/AnyCodable.swift
index 52552e344..895c7cfc2 100644
--- a/Sources/Commons/AnyCodable.swift
+++ b/Sources/Commons/AnyCodable.swift
@@ -40,7 +40,16 @@ public struct AnyCodable {
genericEncoding = { encoder in
try codable.encode(to: encoder)
}
+ }
+
+ /**
+ Creates a type-erased codable value that wraps the given instance.
+ - parameters:
+ - any: Any value which supposed to be codable
+ */
+ public init(any value: Any) {
+ self.init(AnyCodable(value))
}
/**
diff --git a/Sources/JSONRPC/RPCID.swift b/Sources/JSONRPC/RPCID.swift
index 4bf915fbc..c067b67ae 100644
--- a/Sources/JSONRPC/RPCID.swift
+++ b/Sources/JSONRPC/RPCID.swift
@@ -17,6 +17,24 @@ struct IntIdentifierGenerator: IdentifierGenerator {
extension RPCID {
+ public var string: String {
+ switch self {
+ case .right(let int):
+ return int.description
+ case .left(let string):
+ return string
+ }
+ }
+
+ public var integer: Int64 {
+ switch self {
+ case .right(let int):
+ return int
+ case .left(let string):
+ return Int64(string) ?? 0
+ }
+ }
+
public var timestamp: Date {
guard let id = self.right else { return .distantPast }
let interval = TimeInterval(id / 1000 / 1000)
diff --git a/Sources/WalletConnectEcho/Echo.swift b/Sources/WalletConnectEcho/Echo.swift
index 3921ffebc..6ebc721e2 100644
--- a/Sources/WalletConnectEcho/Echo.swift
+++ b/Sources/WalletConnectEcho/Echo.swift
@@ -2,7 +2,7 @@ import Foundation
import WalletConnectNetworking
public class Echo {
-
+ static public let echoHost = "echo.walletconnect.com"
public static var instance: EchoClient = {
guard let config = Echo.config else {
fatalError("Error - you must call Echo.configure(_:) before accessing the shared instance.")
@@ -10,7 +10,8 @@ public class Echo {
return EchoClientFactory.create(
projectId: Networking.projectId,
- clientId: config.clientId)
+ clientId: config.clientId,
+ echoHost: config.echoHost)
}()
private static var config: Config?
@@ -19,7 +20,10 @@ public class Echo {
/// Echo instance config method
/// - Parameter clientId: https://github.com/WalletConnect/walletconnect-docs/blob/main/docs/specs/clients/core/relay/relay-client-auth.md#overview
- static public func configure(clientId: String) {
- Echo.config = Echo.Config(clientId: clientId)
+ static public func configure(
+ clientId: String,
+ echoHost: String = echoHost
+ ) {
+ Echo.config = Echo.Config(clientId: clientId, echoHost: echoHost)
}
}
diff --git a/Sources/WalletConnectEcho/EchoClientFactory.swift b/Sources/WalletConnectEcho/EchoClientFactory.swift
index 859b0d37f..ff7b8cbb6 100644
--- a/Sources/WalletConnectEcho/EchoClientFactory.swift
+++ b/Sources/WalletConnectEcho/EchoClientFactory.swift
@@ -2,9 +2,9 @@ import Foundation
import WalletConnectNetworking
public struct EchoClientFactory {
- public static func create(projectId: String, clientId: String) -> EchoClient {
+ public static func create(projectId: String, clientId: String, echoHost: String) -> EchoClient {
- let httpClient = HTTPNetworkClient(host: "echo.walletconnect.com")
+ let httpClient = HTTPNetworkClient(host: echoHost)
return EchoClientFactory.create(
projectId: projectId,
@@ -16,7 +16,7 @@ public struct EchoClientFactory {
clientId: String,
httpClient: HTTPClient) -> EchoClient {
- let logger = ConsoleLogger(loggingLevel: .debug)
+ let logger = ConsoleLogger(loggingLevel: .off)
let registerService = EchoRegisterService(httpClient: httpClient, projectId: projectId, clientId: clientId, logger: logger)
return EchoClient(
diff --git a/Sources/WalletConnectEcho/EchoConfig.swift b/Sources/WalletConnectEcho/EchoConfig.swift
index acd6ade9f..d1eab366f 100644
--- a/Sources/WalletConnectEcho/EchoConfig.swift
+++ b/Sources/WalletConnectEcho/EchoConfig.swift
@@ -3,5 +3,6 @@ import Foundation
extension Echo {
struct Config {
let clientId: String
+ let echoHost: String
}
}
diff --git a/Sources/WalletConnectEcho/Register/EchoRegisterService.swift b/Sources/WalletConnectEcho/Register/EchoRegisterService.swift
index c3e507af2..51fbd7560 100644
--- a/Sources/WalletConnectEcho/Register/EchoRegisterService.swift
+++ b/Sources/WalletConnectEcho/Register/EchoRegisterService.swift
@@ -33,9 +33,10 @@ actor EchoRegisterService {
EchoResponse.self,
at: EchoAPI.register(clientId: clientIdMutlibase, token: token, projectId: projectId)
)
- guard response.status == .ok else {
+ guard response.status == .success else {
throw Errors.registrationFailed
}
+ logger.debug("Successfully registered at Echo Server")
}
#if DEBUG
@@ -44,9 +45,10 @@ actor EchoRegisterService {
EchoResponse.self,
at: EchoAPI.register(clientId: clientIdMutlibase, token: deviceToken, projectId: projectId)
)
- guard response.status == .ok else {
+ guard response.status == .success else {
throw Errors.registrationFailed
}
+ logger.debug("Successfully registered at Echo Server")
}
#endif
}
diff --git a/Sources/WalletConnectEcho/Register/EcoResponse.swift b/Sources/WalletConnectEcho/Register/EcoResponse.swift
index 4c50053a0..f480c510c 100644
--- a/Sources/WalletConnectEcho/Register/EcoResponse.swift
+++ b/Sources/WalletConnectEcho/Register/EcoResponse.swift
@@ -2,7 +2,7 @@ import Foundation
struct EchoResponse: Codable {
enum Status: String, Codable {
- case ok = "OK"
+ case success = "SUCCESS"
case failed = "FAILED"
}
diff --git a/Sources/WalletConnectNetworking/NetworkInteracting.swift b/Sources/WalletConnectNetworking/NetworkInteracting.swift
index ea759adfe..109415ad6 100644
--- a/Sources/WalletConnectNetworking/NetworkInteracting.swift
+++ b/Sources/WalletConnectNetworking/NetworkInteracting.swift
@@ -6,6 +6,7 @@ public protocol NetworkInteracting {
var requestPublisher: AnyPublisher<(topic: String, request: RPCRequest), Never> { get }
func subscribe(topic: String) async throws
func unsubscribe(topic: String)
+ func batchSubscribe(topics: [String]) async throws
func batchUnsubscribe(topics: [String]) async throws
func request(_ request: RPCRequest, topic: String, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws
func requestNetworkAck(_ request: RPCRequest, topic: String, protocolMethod: ProtocolMethod) async throws
diff --git a/Sources/WalletConnectNetworking/NetworkingInteractor.swift b/Sources/WalletConnectNetworking/NetworkingInteractor.swift
index 0b0e0f37e..634f350d2 100644
--- a/Sources/WalletConnectNetworking/NetworkingInteractor.swift
+++ b/Sources/WalletConnectNetworking/NetworkingInteractor.swift
@@ -57,6 +57,10 @@ public class NetworkingInteractor: NetworkInteracting {
}
}
+ public func batchSubscribe(topics: [String]) async throws {
+ try await relayClient.batchSubscribe(topics: topics)
+ }
+
public func batchUnsubscribe(topics: [String]) async throws {
try await relayClient.batchUnsubscribe(topics: topics)
rpcHistory.deleteAll(forTopics: topics)
diff --git a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift
index 4afa24002..5e4b85ebe 100644
--- a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift
+++ b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift
@@ -46,7 +46,7 @@ class ProposalResponseSubscriber {
let selfpublicKeyHex = payload.request.publicKey
let (topic, _) = try generateAgreementKeys(peerPublicKeyHex: peerPublicKeyHex, selfpublicKeyHex: selfpublicKeyHex)
- let pushSubscription = PushSubscription(topic: topic, relay: relay, metadata: metadata)
+ let pushSubscription = PushSubscription(topic: topic, account: payload.request.account, relay: relay, metadata: metadata)
subscriptionsStore.set(pushSubscription, forKey: topic)
kms.deletePrivateKey(for: selfpublicKeyHex)
try await networkingInteractor.subscribe(topic: topic)
diff --git a/Sources/WalletConnectPush/Client/Wallet/PushMessagesProvider.swift b/Sources/WalletConnectPush/Client/Wallet/PushMessagesProvider.swift
new file mode 100644
index 000000000..236fb0879
--- /dev/null
+++ b/Sources/WalletConnectPush/Client/Wallet/PushMessagesProvider.swift
@@ -0,0 +1,15 @@
+
+import Foundation
+import WalletConnectUtils
+
+class PushMessagesProvider {
+ private let history: RPCHistory
+
+ init(history: RPCHistory) {
+ self.history = history
+ }
+
+ public func getMessageHistory(topic: String) -> [PushMessage] {
+ history.getAll(of: PushMessage.self, topic: topic)
+ }
+}
diff --git a/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift b/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift
index 840e85cfa..c674d63a0 100644
--- a/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift
+++ b/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift
@@ -51,7 +51,7 @@ class PushRequestResponder {
let response = RPCResponse(id: requestId, result: responseParams)
let requestParams = try requestRecord.request.params!.get(PushRequestParams.self)
- let pushSubscription = PushSubscription(topic: pushTopic, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: requestParams.metadata)
+ let pushSubscription = PushSubscription(topic: pushTopic, account: requestParams.account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: requestParams.metadata)
subscriptionsStore.set(pushSubscription, forKey: pushTopic)
try await networkingInteractor.respond(topic: pairingTopic, response: response, protocolMethod: PushRequestProtocolMethod())
diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift
index 18c7c30b3..65ef69e89 100644
--- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift
+++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift
@@ -4,13 +4,14 @@ import WalletConnectNetworking
import WalletConnectPairing
import WalletConnectEcho
+
public class WalletPushClient {
private var publishers = Set()
- private let requestPublisherSubject = PassthroughSubject<(id: RPCID, account: Account, metadata: AppMetadata), Never>()
+ private let requestPublisherSubject = PassthroughSubject()
- public var requestPublisher: AnyPublisher<(id: RPCID, account: Account, metadata: AppMetadata), Never> {
+ public var requestPublisher: AnyPublisher {
requestPublisherSubject.eraseToAnyPublisher()
}
@@ -36,6 +37,7 @@ public class WalletPushClient {
private let proposeResponder: PushRequestResponder
private let pushMessageSubscriber: PushMessageSubscriber
private let subscriptionsProvider: SubscriptionsProvider
+ private let pushMessagesProvider: PushMessagesProvider
private let resubscribeService: PushResubscribeService
init(logger: ConsoleLogging,
@@ -45,6 +47,7 @@ public class WalletPushClient {
proposeResponder: PushRequestResponder,
pushMessageSubscriber: PushMessageSubscriber,
subscriptionsProvider: SubscriptionsProvider,
+ pushMessagesProvider: PushMessagesProvider,
deletePushSubscriptionService: DeletePushSubscriptionService,
deletePushSubscriptionSubscriber: DeletePushSubscriptionSubscriber,
resubscribeService: PushResubscribeService) {
@@ -54,6 +57,7 @@ public class WalletPushClient {
self.echoClient = echoClient
self.pushMessageSubscriber = pushMessageSubscriber
self.subscriptionsProvider = subscriptionsProvider
+ self.pushMessagesProvider = pushMessagesProvider
self.deletePushSubscriptionService = deletePushSubscriptionService
self.deletePushSubscriptionSubscriber = deletePushSubscriptionSubscriber
self.resubscribeService = resubscribeService
@@ -72,6 +76,10 @@ public class WalletPushClient {
subscriptionsProvider.getActiveSubscriptions()
}
+ public func getMessageHistory(topic: String) -> [PushMessage] {
+ pushMessagesProvider.getMessageHistory(topic: topic)
+ }
+
public func delete(topic: String) async throws {
try await deletePushSubscriptionService.delete(topic: topic)
}
diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift
index 7d11f4ade..2ed976c16 100644
--- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift
+++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift
@@ -36,6 +36,7 @@ public struct WalletPushClientFactory {
let deletePushSubscriptionService = DeletePushSubscriptionService(networkingInteractor: networkInteractor, kms: kms, logger: logger, pushSubscriptionStore: subscriptionStore)
let deletePushSubscriptionSubscriber = DeletePushSubscriptionSubscriber(networkingInteractor: networkInteractor, kms: kms, logger: logger, pushSubscriptionStore: subscriptionStore)
let resubscribeService = PushResubscribeService(networkInteractor: networkInteractor, subscriptionsStorage: subscriptionStore)
+ let pushMessagesProvider = PushMessagesProvider(history: history)
return WalletPushClient(
logger: logger,
kms: kms,
@@ -44,6 +45,7 @@ public struct WalletPushClientFactory {
proposeResponder: proposeResponder,
pushMessageSubscriber: pushMessageSubscriber,
subscriptionsProvider: subscriptionProvider,
+ pushMessagesProvider: pushMessagesProvider,
deletePushSubscriptionService: deletePushSubscriptionService,
deletePushSubscriptionSubscriber: deletePushSubscriptionSubscriber,
resubscribeService: resubscribeService
diff --git a/Sources/WalletConnectPush/Push.swift b/Sources/WalletConnectPush/Push.swift
index eea9a582c..5b159bf58 100644
--- a/Sources/WalletConnectPush/Push.swift
+++ b/Sources/WalletConnectPush/Push.swift
@@ -16,7 +16,7 @@ public class Push {
guard let config = Push.config else {
fatalError("Error - you must call Push.configure(_:) before accessing the shared wallet instance.")
}
- Echo.configure(clientId: config.clientId)
+ Echo.configure(clientId: config.clientId, echoHost: config.echoHost)
return WalletPushClientFactory.create(
networkInteractor: Networking.interactor,
pairingRegisterer: Pair.registerer,
@@ -29,9 +29,9 @@ public class Push {
private init() { }
/// Wallet's configuration method
- static public func configure() {
+ static public func configure(echoHost: String = "echo.walletconnect.com") {
let clientId = try! Networking.interactor.getClientId()
- Push.config = Push.Config(clientId: clientId)
+ Push.config = Push.Config(clientId: clientId, echoHost: echoHost)
}
}
diff --git a/Sources/WalletConnectPush/PushConfig.swift b/Sources/WalletConnectPush/PushConfig.swift
index bf0f083d1..8c7445dcc 100644
--- a/Sources/WalletConnectPush/PushConfig.swift
+++ b/Sources/WalletConnectPush/PushConfig.swift
@@ -3,5 +3,6 @@ import Foundation
extension Push {
struct Config {
let clientId: String
+ let echoHost: String
}
}
diff --git a/Sources/WalletConnectPush/Types/PushRequest.swift b/Sources/WalletConnectPush/Types/PushRequest.swift
new file mode 100644
index 000000000..a27b87a9b
--- /dev/null
+++ b/Sources/WalletConnectPush/Types/PushRequest.swift
@@ -0,0 +1,5 @@
+
+import Foundation
+import WalletConnectPairing
+
+public typealias PushRequest = (id: RPCID, account: Account, metadata: AppMetadata)
diff --git a/Sources/WalletConnectPush/Types/PushSubscription.swift b/Sources/WalletConnectPush/Types/PushSubscription.swift
index 0dc61526b..783ec3dd1 100644
--- a/Sources/WalletConnectPush/Types/PushSubscription.swift
+++ b/Sources/WalletConnectPush/Types/PushSubscription.swift
@@ -3,7 +3,8 @@ import WalletConnectUtils
import WalletConnectPairing
public struct PushSubscription: Codable, Equatable {
- let topic: String
- let relay: RelayProtocolOptions
- let metadata: AppMetadata
+ public let topic: String
+ public let account: Account
+ public let relay: RelayProtocolOptions
+ public let metadata: AppMetadata
}
diff --git a/Sources/WalletConnectRelay/PackageConfig.json b/Sources/WalletConnectRelay/PackageConfig.json
index c712e32f1..1663b3678 100644
--- a/Sources/WalletConnectRelay/PackageConfig.json
+++ b/Sources/WalletConnectRelay/PackageConfig.json
@@ -1 +1 @@
-{"version": "1.3.0"}
+{"version": "1.3.1"}
diff --git a/Sources/WalletConnectRelay/RelayClient.swift b/Sources/WalletConnectRelay/RelayClient.swift
index ca9f9b649..104c92d69 100644
--- a/Sources/WalletConnectRelay/RelayClient.swift
+++ b/Sources/WalletConnectRelay/RelayClient.swift
@@ -213,6 +213,16 @@ public final class RelayClient {
}
}
+ public func batchSubscribe(topics: [String]) async throws {
+ await withThrowingTaskGroup(of: Void.self) { group in
+ for topic in topics {
+ group.addTask {
+ try await self.subscribe(topic: topic)
+ }
+ }
+ }
+ }
+
public func batchUnsubscribe(topics: [String]) async throws {
await withThrowingTaskGroup(of: Void.self) { group in
for topic in topics {
@@ -251,7 +261,6 @@ public final class RelayClient {
self?.concurrentQueue.async(flags: .barrier) {
self?.subscriptions[topic] = nil
}
- completion(nil)
}
}
}
diff --git a/Sources/WalletConnectRouter/Router.m b/Sources/WalletConnectRouter/Router.m
index 9010aa7e6..0648d6eb1 100644
--- a/Sources/WalletConnectRouter/Router.m
+++ b/Sources/WalletConnectRouter/Router.m
@@ -1,6 +1,8 @@
#import
#import "Router.h"
+#if __has_include()
+
@import UIKit;
@import ObjectiveC.runtime;
@@ -24,3 +26,5 @@ + (void)goBack {
@end
+#endif
+
diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift
index a3624466a..a00b62c00 100644
--- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift
+++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift
@@ -15,13 +15,11 @@ final class ApproveEngine {
var onSessionRejected: ((Session.Proposal, Reason) -> Void)?
var onSessionSettle: ((Session) -> Void)?
- var settlingProposal: SessionProposal?
-
private let networkingInteractor: NetworkInteracting
private let pairingStore: WCPairingStorage
private let sessionStore: WCSessionStorage
private let proposalPayloadsStore: CodableStore>
- private let sessionToPairingTopic: CodableStore
+ private let sessionTopicToProposal: CodableStore
private let pairingRegisterer: PairingRegisterer
private let metadata: AppMetadata
private let kms: KeyManagementServiceProtocol
@@ -32,7 +30,7 @@ final class ApproveEngine {
init(
networkingInteractor: NetworkInteracting,
proposalPayloadsStore: CodableStore>,
- sessionToPairingTopic: CodableStore,
+ sessionTopicToProposal: CodableStore,
pairingRegisterer: PairingRegisterer,
metadata: AppMetadata,
kms: KeyManagementServiceProtocol,
@@ -42,7 +40,7 @@ final class ApproveEngine {
) {
self.networkingInteractor = networkingInteractor
self.proposalPayloadsStore = proposalPayloadsStore
- self.sessionToPairingTopic = sessionToPairingTopic
+ self.sessionTopicToProposal = sessionTopicToProposal
self.pairingRegisterer = pairingRegisterer
self.metadata = metadata
self.kms = kms
@@ -62,6 +60,7 @@ final class ApproveEngine {
}
let proposal = payload.request
+ let pairingTopic = payload.topic
proposalPayloadsStore.delete(forKey: proposerPubKey)
@@ -77,7 +76,6 @@ final class ApproveEngine {
let sessionTopic = agreementKey.derivedTopic()
try kms.setAgreementSecret(agreementKey, topic: sessionTopic)
- sessionToPairingTopic.set(payload.topic, forKey: sessionTopic)
guard let relay = proposal.relays.first else {
throw Errors.relayNotFound
@@ -88,7 +86,7 @@ final class ApproveEngine {
async let proposeResponse: () = networkingInteractor.respond(topic: payload.topic, response: response, protocolMethod: SessionProposeProtocolMethod())
- async let settleRequest: () = settle(topic: sessionTopic, proposal: proposal, namespaces: sessionNamespaces)
+ async let settleRequest: () = settle(topic: sessionTopic, proposal: proposal, namespaces: sessionNamespaces, pairingTopic: pairingTopic)
_ = try await [proposeResponse, settleRequest]
@@ -107,7 +105,7 @@ final class ApproveEngine {
// TODO: Delete pairing if inactive
}
- func settle(topic: String, proposal: SessionProposal, namespaces: [String: SessionNamespace]) async throws {
+ func settle(topic: String, proposal: SessionProposal, namespaces: [String: SessionNamespace], pairingTopic: String) async throws {
guard let agreementKeys = kms.getAgreementSecret(for: topic) else {
throw Errors.agreementMissingOrInvalid
}
@@ -132,6 +130,7 @@ final class ApproveEngine {
let session = WCSession(
topic: topic,
+ pairingTopic: pairingTopic,
timestamp: Date(),
selfParticipant: selfParticipant,
peerParticipant: proposal.proposer,
@@ -150,7 +149,6 @@ final class ApproveEngine {
async let settleRequest: () = networkingInteractor.request(request, topic: topic, protocolMethod: protocolMethod)
_ = try await [settleRequest, subscription]
-
onSessionSettle?(session.publicRepresentation())
}
}
@@ -216,10 +214,9 @@ private extension ApproveEngine {
logger.debug("Received Session Proposal response")
try kms.setAgreementSecret(agreementKeys, topic: sessionTopic)
- sessionToPairingTopic.set(payload.topic, forKey: sessionTopic)
-
- settlingProposal = payload.request
+ let proposal = payload.request.publicRepresentation(pairingTopic: payload.topic)
+ sessionTopicToProposal.set(proposal, forKey: sessionTopic)
Task(priority: .high) {
try await networkingInteractor.subscribe(topic: sessionTopic)
}
@@ -290,11 +287,13 @@ private extension ApproveEngine {
let protocolMethod = SessionSettleProtocolMethod()
- guard let proposedNamespaces = settlingProposal?.requiredNamespaces else {
- return respondError(payload: payload, reason: .invalidUpdateRequest, protocolMethod: protocolMethod)
- }
+ let sessionTopic = payload.topic
- settlingProposal = nil
+ guard let proposal = try? sessionTopicToProposal.get(key: sessionTopic) else {
+ return respondError(payload: payload, reason: .sessionSettlementFailed, protocolMethod: protocolMethod)
+ }
+ let pairingTopic = proposal.pairingTopic
+ let proposedNamespaces = proposal.requiredNamespaces
let params = payload.request
let sessionNamespaces = params.namespaces
@@ -308,22 +307,20 @@ private extension ApproveEngine {
return respondError(payload: payload, reason: .invalidUpdateRequest, protocolMethod: protocolMethod)
}
- let topic = payload.topic
- let agreementKeys = kms.getAgreementSecret(for: topic)!
+ let agreementKeys = kms.getAgreementSecret(for: sessionTopic)!
let selfParticipant = Participant(
publicKey: agreementKeys.publicKey.hexRepresentation,
metadata: metadata
)
- if let pairingTopic = try? sessionToPairingTopic.get(key: topic) {
- pairingRegisterer.activate(
- pairingTopic: pairingTopic,
- peerMetadata: params.controller.metadata
- )
- }
+ pairingRegisterer.activate(
+ pairingTopic: pairingTopic,
+ peerMetadata: params.controller.metadata
+ )
let session = WCSession(
- topic: topic,
+ topic: sessionTopic,
+ pairingTopic: pairingTopic,
timestamp: Date(),
selfParticipant: selfParticipant,
peerParticipant: params.controller,
diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift
index 7f552ca32..562726920 100644
--- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift
+++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift
@@ -4,6 +4,7 @@ import Combine
final class SessionEngine {
enum Errors: Error {
case sessionNotFound(topic: String)
+ case sessionRequestExpired
}
var onSessionsUpdate: (([Session]) -> Void)?
@@ -15,17 +16,20 @@ final class SessionEngine {
private let sessionStore: WCSessionStorage
private let networkingInteractor: NetworkInteracting
+ private let historyService: HistoryService
private let kms: KeyManagementServiceProtocol
private var publishers = [AnyCancellable]()
private let logger: ConsoleLogging
init(
networkingInteractor: NetworkInteracting,
+ historyService: HistoryService,
kms: KeyManagementServiceProtocol,
sessionStore: WCSessionStorage,
logger: ConsoleLogging
) {
self.networkingInteractor = networkingInteractor
+ self.historyService = historyService
self.kms = kms
self.sessionStore = sessionStore
self.logger = logger
@@ -54,9 +58,9 @@ final class SessionEngine {
guard session.hasPermission(forMethod: request.method, onChain: request.chainId) else {
throw WalletConnectError.invalidPermissions
}
- let chainRequest = SessionType.RequestParams.Request(method: request.method, params: request.params)
+ let chainRequest = SessionType.RequestParams.Request(method: request.method, params: request.params, expiry: request.expiry)
let sessionRequestParams = SessionType.RequestParams(request: chainRequest, chainId: request.chainId)
- let protocolMethod = SessionRequestProtocolMethod()
+ let protocolMethod = SessionRequestProtocolMethod(ttl: request.calculateTtl())
let rpcRequest = RPCRequest(method: protocolMethod.method, params: sessionRequestParams)
try await networkingInteractor.request(rpcRequest, topic: request.topic, protocolMethod: SessionRequestProtocolMethod())
}
@@ -65,8 +69,24 @@ final class SessionEngine {
guard sessionStore.hasSession(forTopic: topic) else {
throw Errors.sessionNotFound(topic: topic)
}
- let response = RPCResponse(id: requestId, outcome: response)
- try await networkingInteractor.respond(topic: topic, response: response, protocolMethod: SessionRequestProtocolMethod())
+
+ let protocolMethod = SessionRequestProtocolMethod()
+
+ guard sessionRequestNotExpired(requestId: requestId) else {
+ try await networkingInteractor.respondError(
+ topic: topic,
+ requestId: requestId,
+ protocolMethod: protocolMethod,
+ reason: SignReasonCode.sessionRequestExpired
+ )
+ throw Errors.sessionRequestExpired
+ }
+
+ try await networkingInteractor.respond(
+ topic: topic,
+ response: RPCResponse(id: requestId, outcome: response),
+ protocolMethod: protocolMethod
+ )
}
func emit(topic: String, event: SessionType.EventParams.Event, chainId: Blockchain) async throws {
@@ -159,6 +179,13 @@ private extension SessionEngine {
}
}
+ func sessionRequestNotExpired(requestId: RPCID) -> Bool {
+ guard let request = historyService.getSessionRequest(id: requestId)
+ else { return false }
+
+ return !request.isExpired()
+ }
+
func respondError(payload: SubscriptionPayload, reason: SignReasonCode, protocolMethod: ProtocolMethod) {
Task(priority: .high) {
do {
@@ -191,7 +218,9 @@ private extension SessionEngine {
topic: payload.topic,
method: payload.request.request.method,
params: payload.request.request.params,
- chainId: payload.request.chainId)
+ chainId: payload.request.chainId,
+ expiry: payload.request.request.expiry
+ )
guard let session = sessionStore.getSession(forTopic: topic) else {
return respondError(payload: payload, reason: .noSessionForTopic, protocolMethod: protocolMethod)
@@ -202,6 +231,11 @@ private extension SessionEngine {
guard session.hasPermission(forMethod: request.method, onChain: request.chainId) else {
return respondError(payload: payload, reason: .unauthorizedMethod(request.method), protocolMethod: protocolMethod)
}
+
+ guard !request.isExpired() else {
+ return respondError(payload: payload, reason: .sessionRequestExpired, protocolMethod: protocolMethod)
+ }
+
onSessionRequest?(request)
}
diff --git a/Sources/WalletConnectSign/Namespace.swift b/Sources/WalletConnectSign/Namespace.swift
index 931ae2d4b..f9d2f8e16 100644
--- a/Sources/WalletConnectSign/Namespace.swift
+++ b/Sources/WalletConnectSign/Namespace.swift
@@ -3,25 +3,11 @@ public struct ProposalNamespace: Equatable, Codable {
public let chains: Set
public let methods: Set
public let events: Set
- public let extensions: [Extension]?
- public struct Extension: Equatable, Codable {
- public let chains: Set
- public let methods: Set
- public let events: Set
-
- public init(chains: Set, methods: Set, events: Set) {
- self.chains = chains
- self.methods = methods
- self.events = events
- }
- }
-
- public init(chains: Set, methods: Set, events: Set, extensions: [ProposalNamespace.Extension]? = nil) {
+ public init(chains: Set, methods: Set, events: Set) {
self.chains = chains
self.methods = methods
self.events = events
- self.extensions = extensions
}
}
@@ -30,36 +16,11 @@ public struct SessionNamespace: Equatable, Codable {
public let accounts: Set
public let methods: Set
public let events: Set
- public let extensions: [Extension]?
-
- public struct Extension: Equatable, Codable {
- public let accounts: Set
- public let methods: Set
- public let events: Set
-
- public init(accounts: Set, methods: Set, events: Set) {
- self.accounts = accounts
- self.methods = methods
- self.events = events
- }
-
- func isCompliant(to required: ProposalNamespace.Extension) -> Bool {
- guard
- SessionNamespace.accountsAreCompliant(accounts, toChains: required.chains),
- methods.isSuperset(of: required.methods),
- events.isSuperset(of: required.events)
- else {
- return false
- }
- return true
- }
- }
- public init(accounts: Set, methods: Set, events: Set, extensions: [SessionNamespace.Extension]? = nil) {
+ public init(accounts: Set, methods: Set, events: Set) {
self.accounts = accounts
self.methods = methods
self.events = events
- self.extensions = extensions
}
static func accountsAreCompliant(_ accounts: Set, toChains chains: Set) -> Bool {
@@ -84,13 +45,6 @@ enum Namespace {
throw WalletConnectError.unsupportedNamespace(.unsupportedChains)
}
}
- if let extensions = namespace.extensions {
- for ext in extensions {
- if ext.chains.isEmpty {
- throw WalletConnectError.unsupportedNamespace(.unsupportedChains)
- }
- }
- }
}
}
@@ -104,13 +58,6 @@ enum Namespace {
throw WalletConnectError.unsupportedNamespace(.unsupportedAccounts)
}
}
- if let extensions = namespace.extensions {
- for ext in extensions {
- if ext.accounts.isEmpty {
- throw WalletConnectError.unsupportedNamespace(.unsupportedAccounts)
- }
- }
- }
}
}
diff --git a/Sources/WalletConnectSign/Request.swift b/Sources/WalletConnectSign/Request.swift
index 4820b8fc0..1cae4e0cd 100644
--- a/Sources/WalletConnectSign/Request.swift
+++ b/Sources/WalletConnectSign/Request.swift
@@ -6,20 +6,57 @@ public struct Request: Codable, Equatable {
public let method: String
public let params: AnyCodable
public let chainId: Blockchain
+ public let expiry: UInt64?
- internal init(id: RPCID, topic: String, method: String, params: AnyCodable, chainId: Blockchain) {
+ internal init(id: RPCID, topic: String, method: String, params: AnyCodable, chainId: Blockchain, expiry: UInt64?) {
self.id = id
self.topic = topic
self.method = method
self.params = params
self.chainId = chainId
+ self.expiry = expiry
}
- public init(topic: String, method: String, params: AnyCodable, chainId: Blockchain) {
- self.init(id: RPCID(JsonRpcID.generate()), topic: topic, method: method, params: params, chainId: chainId)
+ public init(topic: String, method: String, params: AnyCodable, chainId: Blockchain, expiry: UInt64? = nil) {
+ self.init(id: RPCID(JsonRpcID.generate()), topic: topic, method: method, params: params, chainId: chainId, expiry: expiry)
}
- internal init(id: RPCID, topic: String, method: String, params: C, chainId: Blockchain) where C: Codable {
- self.init(id: id, topic: topic, method: method, params: AnyCodable(params), chainId: chainId)
+ init(id: RPCID, topic: String, method: String, params: C, chainId: Blockchain, expiry: UInt64?) where C: Codable {
+ self.init(id: id, topic: topic, method: method, params: AnyCodable(params), chainId: chainId, expiry: expiry)
+ }
+
+ func isExpired(currentDate: Date = Date()) -> Bool {
+ guard let expiry = expiry else { return false }
+
+ let expiryDate = Date(timeIntervalSince1970: TimeInterval(expiry))
+
+ guard
+ abs(currentDate.distance(to: expiryDate)) < Constants.maxExpiry,
+ abs(currentDate.distance(to: expiryDate)) > Constants.minExpiry
+ else { return true }
+
+ return expiryDate < currentDate
+ }
+
+ func calculateTtl(currentDate: Date = Date()) -> Int {
+ guard let expiry = expiry else { return SessionRequestProtocolMethod.defaultTtl }
+
+ let expiryDate = Date(timeIntervalSince1970: TimeInterval(expiry))
+ let diff = expiryDate - currentDate.timeIntervalSince1970
+
+ guard
+ diff.timeIntervalSince1970 < Constants.maxExpiry,
+ diff.timeIntervalSince1970 > Constants.minExpiry
+ else { return SessionRequestProtocolMethod.defaultTtl }
+
+ return Int(diff.timeIntervalSince1970)
+ }
+}
+
+private extension Request {
+
+ struct Constants {
+ static let minExpiry: TimeInterval = 300 // 5 minutes
+ static let maxExpiry: TimeInterval = 604800 // 7 days
}
}
diff --git a/Sources/WalletConnectSign/Services/HistoryService.swift b/Sources/WalletConnectSign/Services/HistoryService.swift
new file mode 100644
index 000000000..394ff0c61
--- /dev/null
+++ b/Sources/WalletConnectSign/Services/HistoryService.swift
@@ -0,0 +1,42 @@
+import Foundation
+
+final class HistoryService {
+
+ private let history: RPCHistory
+
+ init(history: RPCHistory) {
+ self.history = history
+ }
+
+ func getPendingRequests() -> [Request] {
+ return history.getPending()
+ .compactMap { mapRequestRecord($0) }
+ .filter { !$0.isExpired() }
+ }
+
+ func getPendingRequests(topic: String) -> [Request] {
+ return getPendingRequests().filter { $0.topic == topic }
+ }
+
+ public func getSessionRequest(id: RPCID) -> Request? {
+ guard let record = history.get(recordId: id) else { return nil }
+ return mapRequestRecord(record)
+ }
+}
+
+private extension HistoryService {
+
+ func mapRequestRecord(_ record: RPCHistory.Record) -> Request? {
+ guard let request = try? record.request.params?.get(SessionType.RequestParams.self)
+ else { return nil }
+
+ return Request(
+ id: record.id,
+ topic: record.topic,
+ method: request.request.method,
+ params: request.request.params,
+ chainId: request.chainId,
+ expiry: request.request.expiry
+ )
+ }
+}
diff --git a/Sources/WalletConnectSign/Services/SignCleanupService.swift b/Sources/WalletConnectSign/Services/SignCleanupService.swift
index 1da2aee3e..abee34063 100644
--- a/Sources/WalletConnectSign/Services/SignCleanupService.swift
+++ b/Sources/WalletConnectSign/Services/SignCleanupService.swift
@@ -5,13 +5,13 @@ final class SignCleanupService {
private let pairingStore: WCPairingStorage
private let sessionStore: WCSessionStorage
private let kms: KeyManagementServiceProtocol
- private let sessionToPairingTopic: CodableStore
+ private let sessionTopicToProposal: CodableStore
private let networkInteractor: NetworkInteracting
- init(pairingStore: WCPairingStorage, sessionStore: WCSessionStorage, kms: KeyManagementServiceProtocol, sessionToPairingTopic: CodableStore, networkInteractor: NetworkInteracting) {
+ init(pairingStore: WCPairingStorage, sessionStore: WCSessionStorage, kms: KeyManagementServiceProtocol, sessionTopicToProposal: CodableStore, networkInteractor: NetworkInteracting) {
self.pairingStore = pairingStore
self.sessionStore = sessionStore
- self.sessionToPairingTopic = sessionToPairingTopic
+ self.sessionTopicToProposal = sessionTopicToProposal
self.networkInteractor = networkInteractor
self.kms = kms
}
@@ -38,7 +38,7 @@ private extension SignCleanupService {
func cleanupStorages() throws {
pairingStore.deleteAll()
sessionStore.deleteAll()
- sessionToPairingTopic.deleteAll()
+ sessionTopicToProposal.deleteAll()
try kms.deleteAll()
}
}
diff --git a/Sources/WalletConnectSign/Session.swift b/Sources/WalletConnectSign/Session.swift
index 99e981591..8c85f4d6a 100644
--- a/Sources/WalletConnectSign/Session.swift
+++ b/Sources/WalletConnectSign/Session.swift
@@ -5,6 +5,7 @@ import Foundation
*/
public struct Session {
public let topic: String
+ public let pairingTopic: String
public let peer: AppMetadata
public let namespaces: [String: SessionNamespace]
public let expiryDate: Date
@@ -15,7 +16,7 @@ public struct Session {
extension Session {
- public struct Proposal: Equatable {
+ public struct Proposal: Equatable, Codable {
public var id: String
public let pairingTopic: String
public let proposer: AppMetadata
diff --git a/Sources/WalletConnectSign/Sign/SignClient.swift b/Sources/WalletConnectSign/Sign/SignClient.swift
index 6d015414e..dd0614324 100644
--- a/Sources/WalletConnectSign/Sign/SignClient.swift
+++ b/Sources/WalletConnectSign/Sign/SignClient.swift
@@ -110,7 +110,7 @@ public final class SignClient: SignClientProtocol {
private let nonControllerSessionStateMachine: NonControllerSessionStateMachine
private let controllerSessionStateMachine: ControllerSessionStateMachine
private let appProposeService: AppProposeService
- private let history: RPCHistory
+ private let historyService: HistoryService
private let cleanupService: SignCleanupService
private let sessionProposalPublisherSubject = PassthroughSubject()
@@ -140,7 +140,7 @@ public final class SignClient: SignClientProtocol {
controllerSessionStateMachine: ControllerSessionStateMachine,
appProposeService: AppProposeService,
disconnectService: DisconnectService,
- history: RPCHistory,
+ historyService: HistoryService,
cleanupService: SignCleanupService,
pairingClient: PairingClient
) {
@@ -153,7 +153,7 @@ public final class SignClient: SignClientProtocol {
self.nonControllerSessionStateMachine = nonControllerSessionStateMachine
self.controllerSessionStateMachine = controllerSessionStateMachine
self.appProposeService = appProposeService
- self.history = history
+ self.historyService = historyService
self.cleanupService = cleanupService
self.disconnectService = disconnectService
self.pairingClient = pairingClient
@@ -323,27 +323,17 @@ public final class SignClient: SignClientProtocol {
/// - Returns: Pending requests received from peer with `wc_sessionRequest` protocol method
/// - Parameter topic: topic representing session for which you want to get pending requests. If nil, you will receive pending requests for all active sessions.
public func getPendingRequests(topic: String? = nil) -> [Request] {
- let pendingRequests: [Request] = history.getPending()
- .compactMap {
- guard let request = try? $0.request.params?.get(SessionType.RequestParams.self) else { return nil }
- return Request(id: $0.id, topic: $0.topic, method: request.request.method, params: request.request.params, chainId: request.chainId)
- }
if let topic = topic {
- return pendingRequests.filter {$0.topic == topic}
+ return historyService.getPendingRequests(topic: topic)
} else {
- return pendingRequests
+ return historyService.getPendingRequests()
}
}
/// - Parameter id: id of a wc_sessionRequest jsonrpc request
/// - Returns: json rpc record object for given id or nil if record for give id does not exits
public func getSessionRequestRecord(id: RPCID) -> Request? {
- guard
- let record = history.get(recordId: id),
- let request = try? record.request.params?.get(SessionType.RequestParams.self)
- else { return nil }
-
- return Request(id: record.id, topic: record.topic, method: record.request.method, params: request, chainId: request.chainId)
+ return historyService.getSessionRequest(id: id)
}
/// Delete all stored data such as: pairings, sessions, keys
diff --git a/Sources/WalletConnectSign/Sign/SignClientFactory.swift b/Sources/WalletConnectSign/Sign/SignClientFactory.swift
index ce27c1e4c..ef68dd39e 100644
--- a/Sources/WalletConnectSign/Sign/SignClientFactory.swift
+++ b/Sources/WalletConnectSign/Sign/SignClientFactory.swift
@@ -23,13 +23,14 @@ public struct SignClientFactory {
let rpcHistory = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage)
let pairingStore = PairingStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: SignStorageIdentifiers.pairings.rawValue)))
let sessionStore = SessionStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: SignStorageIdentifiers.sessions.rawValue)))
- let sessionToPairingTopic = CodableStore(defaults: RuntimeKeyValueStorage(), identifier: SignStorageIdentifiers.sessionToPairingTopic.rawValue)
let proposalPayloadsStore = CodableStore>(defaults: RuntimeKeyValueStorage(), identifier: SignStorageIdentifiers.proposals.rawValue)
- let sessionEngine = SessionEngine(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger)
+ let historyService = HistoryService(history: rpcHistory)
+ let sessionEngine = SessionEngine(networkingInteractor: networkingClient, historyService: historyService, kms: kms, sessionStore: sessionStore, logger: logger)
let nonControllerSessionStateMachine = NonControllerSessionStateMachine(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger)
let controllerSessionStateMachine = ControllerSessionStateMachine(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger)
- let approveEngine = ApproveEngine(networkingInteractor: networkingClient, proposalPayloadsStore: proposalPayloadsStore, sessionToPairingTopic: sessionToPairingTopic, pairingRegisterer: pairingClient, metadata: metadata, kms: kms, logger: logger, pairingStore: pairingStore, sessionStore: sessionStore)
- let cleanupService = SignCleanupService(pairingStore: pairingStore, sessionStore: sessionStore, kms: kms, sessionToPairingTopic: sessionToPairingTopic, networkInteractor: networkingClient)
+ let sessionTopicToProposal = CodableStore(defaults: RuntimeKeyValueStorage(), identifier: SignStorageIdentifiers.sessionTopicToProposal.rawValue)
+ let approveEngine = ApproveEngine(networkingInteractor: networkingClient, proposalPayloadsStore: proposalPayloadsStore, sessionTopicToProposal: sessionTopicToProposal, pairingRegisterer: pairingClient, metadata: metadata, kms: kms, logger: logger, pairingStore: pairingStore, sessionStore: sessionStore)
+ let cleanupService = SignCleanupService(pairingStore: pairingStore, sessionStore: sessionStore, kms: kms, sessionTopicToProposal: sessionTopicToProposal, networkInteractor: networkingClient)
let deleteSessionService = DeleteSessionService(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger)
let disconnectService = DisconnectService(deleteSessionService: deleteSessionService, sessionStorage: sessionStore)
let sessionPingService = SessionPingService(sessionStorage: sessionStore, networkingInteractor: networkingClient, logger: logger)
@@ -47,7 +48,7 @@ public struct SignClientFactory {
controllerSessionStateMachine: controllerSessionStateMachine,
appProposeService: appProposerService,
disconnectService: disconnectService,
- history: rpcHistory,
+ historyService: historyService,
cleanupService: cleanupService,
pairingClient: pairingClient
)
diff --git a/Sources/WalletConnectSign/SignStorageIdentifiers.swift b/Sources/WalletConnectSign/SignStorageIdentifiers.swift
index 4da79ec0e..8854dd4e8 100644
--- a/Sources/WalletConnectSign/SignStorageIdentifiers.swift
+++ b/Sources/WalletConnectSign/SignStorageIdentifiers.swift
@@ -4,5 +4,5 @@ enum SignStorageIdentifiers: String {
case pairings = "com.walletconnect.sdk.pairingSequences"
case sessions = "com.walletconnect.sdk.sessionSequences"
case proposals = "com.walletconnect.sdk.sessionProposals"
- case sessionToPairingTopic = "com.walletconnect.sdk.sessionToPairingTopic"
+ case sessionTopicToProposal = "com.walletconnect.sdk.sessionTopicToProposal"
}
diff --git a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionRequestProtocolMethod.swift b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionRequestProtocolMethod.swift
index 13d77a32e..28ee6d446 100644
--- a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionRequestProtocolMethod.swift
+++ b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionRequestProtocolMethod.swift
@@ -1,9 +1,22 @@
import Foundation
struct SessionRequestProtocolMethod: ProtocolMethod {
+
+ static let defaultTtl: Int = 300
+
let method: String = "wc_sessionRequest"
- let requestConfig = RelayConfig(tag: 1108, prompt: true, ttl: 300)
+ private let ttl: Int
+
+ var requestConfig: RelayConfig {
+ RelayConfig(tag: 1108, prompt: true, ttl: ttl)
+ }
+
+ var responseConfig: RelayConfig {
+ RelayConfig(tag: 1109, prompt: false, ttl: ttl)
+ }
- let responseConfig = RelayConfig(tag: 1109, prompt: false, ttl: 300)
+ init(ttl: Int = Self.defaultTtl) {
+ self.ttl = ttl
+ }
}
diff --git a/Sources/WalletConnectSign/Types/Session/SessionType.swift b/Sources/WalletConnectSign/Types/Session/SessionType.swift
index d6c7c87d5..2731e448c 100644
--- a/Sources/WalletConnectSign/Types/Session/SessionType.swift
+++ b/Sources/WalletConnectSign/Types/Session/SessionType.swift
@@ -42,6 +42,7 @@ internal enum SessionType {
struct Request: Codable, Equatable {
let method: String
let params: AnyCodable
+ let expiry: UInt64?
}
}
diff --git a/Sources/WalletConnectSign/Types/Session/WCSession.swift b/Sources/WalletConnectSign/Types/Session/WCSession.swift
index e4e4d2b14..7cbcf82c0 100644
--- a/Sources/WalletConnectSign/Types/Session/WCSession.swift
+++ b/Sources/WalletConnectSign/Types/Session/WCSession.swift
@@ -7,6 +7,7 @@ struct WCSession: SequenceObject, Equatable {
}
let topic: String
+ let pairingTopic: String
let relay: RelayProtocolOptions
let selfParticipant: Participant
let peerParticipant: Participant
@@ -27,6 +28,7 @@ struct WCSession: SequenceObject, Equatable {
}
init(topic: String,
+ pairingTopic: String,
timestamp: Date,
selfParticipant: Participant,
peerParticipant: Participant,
@@ -34,6 +36,7 @@ struct WCSession: SequenceObject, Equatable {
requiredNamespaces: [String: ProposalNamespace],
acknowledged: Bool) {
self.topic = topic
+ self.pairingTopic = pairingTopic
self.timestamp = timestamp
self.relay = settleParams.relay
self.controller = AgreementPeer(publicKey: settleParams.controller.publicKey)
@@ -48,6 +51,7 @@ struct WCSession: SequenceObject, Equatable {
#if DEBUG
internal init(
topic: String,
+ pairingTopic: String,
timestamp: Date,
relay: RelayProtocolOptions,
controller: AgreementPeer,
@@ -61,6 +65,7 @@ struct WCSession: SequenceObject, Equatable {
expiry: Int64
) {
self.topic = topic
+ self.pairingTopic = pairingTopic
self.timestamp = timestamp
self.relay = relay
self.controller = controller
@@ -95,15 +100,6 @@ struct WCSession: SequenceObject, Equatable {
if namespace.methods.contains(method) {
return true
}
- if let extensions = namespace.extensions {
- for extended in extensions {
- if extended.accounts.contains(where: { $0.blockchain == chain }) {
- if extended.methods.contains(method) {
- return true
- }
- }
- }
- }
}
}
return false
@@ -115,15 +111,6 @@ struct WCSession: SequenceObject, Equatable {
if namespace.events.contains(event) {
return true
}
- if let extensions = namespace.extensions {
- for extended in extensions {
- if extended.accounts.contains(where: { $0.blockchain == chain }) {
- if extended.events.contains(event) {
- return true
- }
- }
- }
- }
}
}
return false
@@ -139,16 +126,6 @@ struct WCSession: SequenceObject, Equatable {
else {
throw Error.unsatisfiedUpdateNamespaceRequirement
}
- if let extensions = item.value.extensions {
- guard let compliantExtensions = compliantNamespace.extensions else {
- throw Error.unsatisfiedUpdateNamespaceRequirement
- }
- for existingExtension in extensions {
- guard compliantExtensions.contains(where: { $0.isCompliant(to: existingExtension) }) else {
- throw Error.unsatisfiedUpdateNamespaceRequirement
- }
- }
- }
}
self.namespaces = namespaces
self.timestamp = timestamp
@@ -179,6 +156,7 @@ struct WCSession: SequenceObject, Equatable {
func publicRepresentation() -> Session {
return Session(
topic: topic,
+ pairingTopic: pairingTopic,
peer: peerParticipant.metadata,
namespaces: namespaces,
expiryDate: expiryDate)
@@ -190,7 +168,7 @@ struct WCSession: SequenceObject, Equatable {
extension WCSession {
enum CodingKeys: String, CodingKey {
- case topic, relay, selfParticipant, peerParticipant, expiryDate, acknowledged, controller, namespaces, timestamp, requiredNamespaces
+ case topic, pairingTopic, relay, selfParticipant, peerParticipant, expiryDate, acknowledged, controller, namespaces, timestamp, requiredNamespaces
}
init(from decoder: Decoder) throws {
@@ -203,15 +181,15 @@ extension WCSession {
self.namespaces = try container.decode([String: SessionNamespace].self, forKey: .namespaces)
self.acknowledged = try container.decode(Bool.self, forKey: .acknowledged)
self.expiryDate = try container.decode(Date.self, forKey: .expiryDate)
-
- // Migration beta.102
- self.timestamp = try container.decodeIfPresent(Date.self, forKey: .timestamp) ?? .distantPast
- self.requiredNamespaces = try container.decodeIfPresent([String: ProposalNamespace].self, forKey: .requiredNamespaces) ?? [:]
+ self.timestamp = try container.decode(Date.self, forKey: .timestamp)
+ self.requiredNamespaces = try container.decode([String: ProposalNamespace].self, forKey: .requiredNamespaces)
+ self.pairingTopic = try container.decode(String.self, forKey: .pairingTopic)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(topic, forKey: .topic)
+ try container.encode(pairingTopic, forKey: .pairingTopic)
try container.encode(relay, forKey: .relay)
try container.encode(controller, forKey: .controller)
try container.encode(selfParticipant, forKey: .selfParticipant)
diff --git a/Sources/WalletConnectSign/Types/SignReasonCode.swift b/Sources/WalletConnectSign/Types/SignReasonCode.swift
index 522cbfb4f..5d4471b63 100644
--- a/Sources/WalletConnectSign/Types/SignReasonCode.swift
+++ b/Sources/WalletConnectSign/Types/SignReasonCode.swift
@@ -37,6 +37,11 @@ enum SignReasonCode: Reason, Codable, Equatable {
// 6000
case userDisconnected
+ // 7000
+ case sessionSettlementFailed
+ // 8000
+ case sessionRequestExpired
+
var code: Int {
switch self {
case .invalidMethod: return 1001
@@ -65,7 +70,9 @@ enum SignReasonCode: Reason, Codable, Equatable {
case .userDisconnected: return 6000
+ case .sessionSettlementFailed: return 7000
case .noSessionForTopic: return 7001
+ case .sessionRequestExpired: return 8000
}
}
@@ -79,9 +86,6 @@ enum SignReasonCode: Reason, Codable, Equatable {
return "Invalid update namespace request"
case .invalidExtendRequest:
return "Invalid update expiry request"
- case .noSessionForTopic:
- return "No matching session matching topic"
-
case .unauthorizedMethod(let method):
return "Unauthorized JSON-RPC method requested: \(method)"
case .unauthorizedEvent(let type):
@@ -117,6 +121,12 @@ enum SignReasonCode: Reason, Codable, Equatable {
return "Unsupported namespace key"
case .userDisconnected:
return "User discconnected"
+ case .sessionSettlementFailed:
+ return "Session Settlement Failed"
+ case .noSessionForTopic:
+ return "No matching session matching topic"
+ case .sessionRequestExpired:
+ return "Session request expired or expiry param validation failed (MIN_INTERVAL: 300, MAX_INTERVAL: 604800)"
}
}
}
diff --git a/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift b/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift
index 8e130c106..4fc00aebe 100644
--- a/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift
+++ b/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift
@@ -27,7 +27,7 @@ public final class RPCHistory {
}
public func get(recordId: RPCID) -> Record? {
- try? storage.get(key: "\(recordId)")
+ try? storage.get(key: recordId.string)
}
public func set(_ request: RPCRequest, forTopic topic: String, emmitedBy origin: Record.Origin) throws {
@@ -65,11 +65,33 @@ public final class RPCHistory {
}
}
+ public func getAll(of type: Object.Type, topic: String) -> [Object] {
+ return storage.getAll()
+ .filter{$0.topic == topic}
+ .compactMap { try? $0.request.params?.get(Object.self) }
+ }
+
+ public func getAll(of type: Object.Type) -> [Object] {
+ return getAllWithIDs(of: type).map { $0.value }
+ }
+
+ public func getAllWithIDs(of type: Object.Type) -> [(id: RPCID, value: Object)] {
+ return storage.getAll().compactMap { record in
+ guard let object = try? record.request.params?.get(Object.self)
+ else { return nil }
+ return (record.id, object)
+ }
+ }
+
+ public func delete(id: RPCID) {
+ storage.delete(forKey: id.string)
+ }
+
public func deleteAll(forTopic topic: String) {
deleteAll(forTopics: [topic])
}
public func getPending() -> [Record] {
- storage.getAll().filter {$0.response == nil}
+ storage.getAll().filter { $0.response == nil }
}
}
diff --git a/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift b/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift
new file mode 100644
index 000000000..291d0b1a3
--- /dev/null
+++ b/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift
@@ -0,0 +1,108 @@
+import Foundation
+
+final class ChatClientProxy {
+
+ private let client: ChatClient
+
+ var onResponse: ((RPCResponse) async throws -> Void)?
+
+ init(client: ChatClient) {
+ self.client = client
+ }
+
+ func request(_ request: RPCRequest) async throws {
+ guard let event = WebViewEvent(rawValue: request.method)
+ else { throw Errors.unregisteredMethod }
+
+ switch event {
+ case .getInvites:
+ let invites = client.getInvites()
+ try await respond(with: invites, request: request)
+
+ case .getThreads:
+ let threads = client.getThreads()
+ try await respond(with: threads, request: request)
+
+ case .register:
+ let params = try parse(RegisterRequest.self, params: request.params)
+ try await client.register(account: params.account)
+ try await respond(request: request)
+
+ case .getMessages:
+ let params = try parse(GetMessagesRequest.self, params: request.params)
+ let messages = client.getMessages(topic: params.topic)
+ try await respond(with: messages, request: request)
+
+ case .message:
+ let params = try parse(MessageRequest.self, params: request.params)
+ try await client.message(topic: params.topic, message: params.payload.message)
+ try await respond(with: params.payload, request: request)
+
+ case .accept:
+ let params = try parse(AcceptRequest.self, params: request.params)
+ try await client.accept(inviteId: params.id)
+ try await respond(request: request)
+
+ case .reject:
+ let params = try parse(RejectRequest.self, params: request.params)
+ try await client.reject(inviteId: params.id)
+ try await respond(request: request)
+
+ case .invite:
+ let params = try parse(InviteRequest.self, params: request.params)
+ try await client.invite(peerAccount: params.invite.account, openingMessage: params.invite.message)
+ try await respond(request: request)
+ }
+ }
+}
+
+private extension ChatClientProxy {
+
+ private typealias Blob = Dictionary
+
+ enum Errors: Error {
+ case unregisteredMethod
+ case unregisteredParams
+ }
+
+ struct RegisterRequest: Codable {
+ let account: Account
+ }
+
+ struct GetMessagesRequest: Codable {
+ let topic: String
+ }
+
+ struct MessageRequest: Codable {
+ let topic: String
+ let payload: MessagePayload
+ }
+
+ struct AcceptRequest: Codable {
+ let id: Int64
+ }
+
+ struct RejectRequest: Codable {
+ let id: Int64
+ }
+
+ struct InviteRequest: Codable {
+ struct Invite: Codable {
+ let account: Account
+ let message: String
+ }
+ let account: Account
+ let invite: Invite
+ }
+
+ func parse(_ type: Request.Type, params: AnyCodable?) throws -> Request {
+ guard let params = try params?.get([Request].self).first
+ else { throw Errors.unregisteredParams }
+ return params
+ }
+
+ func respond(with object: Object = Blob(), request: RPCRequest) async throws {
+ let response = RPCResponse(matchingRequest: request, result: object)
+ try await onResponse?(response)
+ }
+}
diff --git a/Sources/Web3Inbox/ChatClient/ChatClientRequest.swift b/Sources/Web3Inbox/ChatClient/ChatClientRequest.swift
new file mode 100644
index 000000000..c5dcb307d
--- /dev/null
+++ b/Sources/Web3Inbox/ChatClient/ChatClientRequest.swift
@@ -0,0 +1,12 @@
+import Foundation
+
+enum ChatClientRequest: String {
+ case chatInvite = "chat_invite"
+ case chatThread = "chat_joined"
+ case chatMessage = "chat_message"
+ case setAccount = "setAccount"
+
+ var method: String {
+ return rawValue
+ }
+}
diff --git a/Sources/Web3Inbox/ChatClient/ChatClientRequestSubscriber.swift b/Sources/Web3Inbox/ChatClient/ChatClientRequestSubscriber.swift
new file mode 100644
index 000000000..12d3c24b1
--- /dev/null
+++ b/Sources/Web3Inbox/ChatClient/ChatClientRequestSubscriber.swift
@@ -0,0 +1,53 @@
+import Foundation
+import Combine
+
+final class ChatClientRequestSubscriber {
+
+ private var publishers: Set = []
+
+ private let chatClient: ChatClient
+ private let logger: ConsoleLogging
+
+ var onRequest: ((RPCRequest) async throws -> Void)?
+
+ init(chatClient: ChatClient, logger: ConsoleLogging) {
+ self.chatClient = chatClient
+ self.logger = logger
+
+ setupSubscriptions()
+ }
+
+ func setupSubscriptions() {
+ chatClient.invitePublisher
+ .sink { [unowned self] invite in
+ handle(event: .chatInvite, params: invite)
+ }.store(in: &publishers)
+
+ chatClient.newThreadPublisher
+ .sink { [unowned self] thread in
+ handle(event: .chatThread, params: thread)
+ }.store(in: &publishers)
+
+ chatClient.messagePublisher
+ .sink { [unowned self] message in
+ handle(event: .chatMessage, params: message)
+ }.store(in: &publishers)
+ }
+}
+
+private extension ChatClientRequestSubscriber {
+
+ func handle(event: ChatClientRequest, params: Codable) {
+ Task {
+ do {
+ let request = RPCRequest(
+ method: event.method,
+ params: params
+ )
+ try await onRequest?(request)
+ } catch {
+ logger.error("Client Request error: \(error.localizedDescription)")
+ }
+ }
+ }
+}
diff --git a/Sources/Web3Inbox/Web3Inbox.swift b/Sources/Web3Inbox/Web3Inbox.swift
new file mode 100644
index 000000000..c6fdb4a69
--- /dev/null
+++ b/Sources/Web3Inbox/Web3Inbox.swift
@@ -0,0 +1,24 @@
+import Foundation
+
+public final class Web3Inbox {
+
+ /// Web3Inbox client instance
+ public static var instance: Web3InboxClient = {
+ guard let account = account else {
+ fatalError("Error - you must call Web3Inbox.configure(_:) before accessing the shared instance.")
+ }
+ return Web3InboxClientFactory.create(chatClient: Chat.instance, account: account)
+ }()
+
+ private static var account: Account?
+
+ private init() { }
+
+ /// Web3Inbox instance config method
+ /// - Parameters:
+ /// - account: Web3Inbox initial account
+ static public func configure(account: Account) {
+ Chat.configure(account: account)
+ Web3Inbox.account = account
+ }
+}
diff --git a/Sources/Web3Inbox/Web3InboxClient.swift b/Sources/Web3Inbox/Web3InboxClient.swift
new file mode 100644
index 000000000..a02bb6d08
--- /dev/null
+++ b/Sources/Web3Inbox/Web3InboxClient.swift
@@ -0,0 +1,73 @@
+import Foundation
+import WebKit
+
+public final class Web3InboxClient {
+
+ private let webView: WKWebView
+ private var account: Account
+ private let logger: ConsoleLogging
+
+ private let clientProxy: ChatClientProxy
+ private let clientSubscriber: ChatClientRequestSubscriber
+
+ private let webviewProxy: WebViewProxy
+ private let webviewSubscriber: WebViewRequestSubscriber
+
+ init(
+ webView: WKWebView,
+ account: Account,
+ logger: ConsoleLogging,
+ clientProxy: ChatClientProxy,
+ clientSubscriber: ChatClientRequestSubscriber,
+ webviewProxy: WebViewProxy,
+ webviewSubscriber: WebViewRequestSubscriber
+ ) {
+ self.webView = webView
+ self.account = account
+ self.logger = logger
+ self.clientProxy = clientProxy
+ self.clientSubscriber = clientSubscriber
+ self.webviewProxy = webviewProxy
+ self.webviewSubscriber = webviewSubscriber
+
+ setupSubscriptions()
+ }
+
+ public func getWebView() -> WKWebView {
+ return webView
+ }
+
+ public func setAccount(_ account: Account) async throws {
+ try await authorize(account: account)
+ }
+}
+
+// MARK: - Privates
+
+private extension Web3InboxClient {
+
+ func setupSubscriptions() {
+ webviewSubscriber.onLogin = { [unowned self] in
+ try await self.authorize(account: self.account)
+ }
+ webviewSubscriber.onRequest = { [unowned self] request in
+ try await self.clientProxy.request(request)
+ }
+ clientProxy.onResponse = { [unowned self] response in
+ try await self.webviewProxy.respond(response)
+ }
+ clientSubscriber.onRequest = { [unowned self] request in
+ try await self.webviewProxy.request(request)
+ }
+ }
+
+ func authorize(account: Account) async throws {
+ self.account = account
+
+ let request = RPCRequest(
+ method: ChatClientRequest.setAccount.method,
+ params: ["account": account.address]
+ )
+ try await webviewProxy.request(request)
+ }
+}
diff --git a/Sources/Web3Inbox/Web3InboxClientFactory.swift b/Sources/Web3Inbox/Web3InboxClientFactory.swift
new file mode 100644
index 000000000..5a54dc042
--- /dev/null
+++ b/Sources/Web3Inbox/Web3InboxClientFactory.swift
@@ -0,0 +1,25 @@
+import Foundation
+import WebKit
+
+final class Web3InboxClientFactory {
+
+ static func create(chatClient: ChatClient, account: Account) -> Web3InboxClient {
+ let host = "https://web3inbox-dev-hidden.vercel.app/?chatProvider=ios"
+ let logger = ConsoleLogger(suffix: "📬")
+ let webviewSubscriber = WebViewRequestSubscriber(logger: logger)
+ let webView = WebViewFactory(host: host, webviewSubscriber: webviewSubscriber).create()
+ let webViewProxy = WebViewProxy(webView: webView)
+ let clientProxy = ChatClientProxy(client: chatClient)
+ let clientSubscriber = ChatClientRequestSubscriber(chatClient: chatClient, logger: logger)
+
+ return Web3InboxClient(
+ webView: webView,
+ account: account,
+ logger: ConsoleLogger(),
+ clientProxy: clientProxy,
+ clientSubscriber: clientSubscriber,
+ webviewProxy: webViewProxy,
+ webviewSubscriber: webviewSubscriber
+ )
+ }
+}
diff --git a/Sources/Web3Inbox/Web3InboxImports.swift b/Sources/Web3Inbox/Web3InboxImports.swift
new file mode 100644
index 000000000..54b421e88
--- /dev/null
+++ b/Sources/Web3Inbox/Web3InboxImports.swift
@@ -0,0 +1,3 @@
+#if !CocoaPods
+@_exported import WalletConnectChat
+#endif
diff --git a/Sources/Web3Inbox/WebView/WebViewEvent.swift b/Sources/Web3Inbox/WebView/WebViewEvent.swift
new file mode 100644
index 000000000..b83a3249b
--- /dev/null
+++ b/Sources/Web3Inbox/WebView/WebViewEvent.swift
@@ -0,0 +1,12 @@
+import Foundation
+
+enum WebViewEvent: String {
+ case getInvites
+ case getThreads
+ case register
+ case getMessages
+ case message
+ case accept
+ case reject
+ case invite
+}
diff --git a/Sources/Web3Inbox/WebView/WebViewFactory.swift b/Sources/Web3Inbox/WebView/WebViewFactory.swift
new file mode 100644
index 000000000..8b1cf0256
--- /dev/null
+++ b/Sources/Web3Inbox/WebView/WebViewFactory.swift
@@ -0,0 +1,26 @@
+import Foundation
+import WebKit
+
+final class WebViewFactory {
+
+ private let host: String
+ private let webviewSubscriber: WebViewRequestSubscriber
+
+ init(host: String, webviewSubscriber: WebViewRequestSubscriber) {
+ self.host = host
+ self.webviewSubscriber = webviewSubscriber
+ }
+
+ func create() -> WKWebView {
+ let configuration = WKWebViewConfiguration()
+ configuration.userContentController.add(
+ webviewSubscriber,
+ name: WebViewRequestSubscriber.name
+ )
+ let webview = WKWebView(frame: .zero, configuration: configuration)
+ webview.navigationDelegate = webviewSubscriber
+ let request = URLRequest(url: URL(string: host)!)
+ webview.load(request)
+ return webview
+ }
+}
diff --git a/Sources/Web3Inbox/WebView/WebViewProxy.swift b/Sources/Web3Inbox/WebView/WebViewProxy.swift
new file mode 100644
index 000000000..73130fede
--- /dev/null
+++ b/Sources/Web3Inbox/WebView/WebViewProxy.swift
@@ -0,0 +1,32 @@
+import Foundation
+import WebKit
+
+actor WebViewProxy {
+
+ private let webView: WKWebView
+
+ init(webView: WKWebView) {
+ self.webView = webView
+ }
+
+ @MainActor
+ func respond(_ response: RPCResponse) async throws {
+ let body = try response.json()
+ let script = await formatScript(body: body)
+ webView.evaluateJavaScript(script, completionHandler: nil)
+ }
+
+ @MainActor
+ func request(_ request: RPCRequest) async throws {
+ let body = try request.json()
+ let script = await formatScript(body: body)
+ webView.evaluateJavaScript(script, completionHandler: nil)
+ }
+}
+
+private extension WebViewProxy {
+
+ func formatScript(body: String) -> String {
+ return "window.\(WebViewRequestSubscriber.name).chat.postMessage(\(body))"
+ }
+}
diff --git a/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift b/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift
new file mode 100644
index 000000000..fea0dc95b
--- /dev/null
+++ b/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift
@@ -0,0 +1,50 @@
+import Foundation
+import WebKit
+
+final class WebViewRequestSubscriber: NSObject, WKScriptMessageHandler {
+
+ static let name = "web3inbox"
+
+ var onRequest: ((RPCRequest) async throws -> Void)?
+ var onLogin: (() async throws -> Void)?
+
+ private let logger: ConsoleLogging
+
+ init(logger: ConsoleLogging) {
+ self.logger = logger
+ }
+
+ func userContentController(
+ _ userContentController: WKUserContentController,
+ didReceive message: WKScriptMessage
+ ) {
+ guard message.name == WebViewRequestSubscriber.name else { return }
+
+ guard
+ let dict = message.body as? [String: Any],
+ let data = try? JSONSerialization.data(withJSONObject: dict),
+ let request = try? JSONDecoder().decode(RPCRequest.self, from: data)
+ else { return }
+
+ Task {
+ do {
+ try await onRequest?(request)
+ } catch {
+ logger.error("WebView Request error: \(error.localizedDescription)")
+ }
+ }
+ }
+}
+
+// MARK: - WKNavigationDelegate
+
+extension WebViewRequestSubscriber: WKNavigationDelegate {
+
+ func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
+ guard webView.url?.path == "/login" else { return }
+
+ Task {
+ try await onLogin?()
+ }
+ }
+}
diff --git a/Tests/ChatTests/RegistryManagerTests.swift b/Tests/ChatTests/RegistryManagerTests.swift
index 1b87397c6..ec9308be5 100644
--- a/Tests/ChatTests/RegistryManagerTests.swift
+++ b/Tests/ChatTests/RegistryManagerTests.swift
@@ -11,27 +11,64 @@ final class RegistryManagerTests: XCTestCase {
var networkingInteractor: NetworkingInteractorMock!
var topicToRegistryRecordStore: CodableStore!
var registry: Registry!
+ var accountService: AccountService!
var kms: KeyManagementServiceMock!
+ var resubscriptionService: ResubscriptionService!
+ var chatStorage: ChatStorage!
+ var threadStore: KeyedDatabase!
+
+ let initialAccount = Account("eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb")!
+ let newAccount = Account("eip155:2:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb")!
+ let mockAccount = Account("eip155:3:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb")!
override func setUp() {
registry = KeyValueRegistry()
networkingInteractor = NetworkingInteractorMock()
kms = KeyManagementServiceMock()
topicToRegistryRecordStore = CodableStore(defaults: RuntimeKeyValueStorage(), identifier: "")
+ accountService = AccountService(currentAccount: initialAccount)
+ threadStore = KeyedDatabase(storage: RuntimeKeyValueStorage(), identifier: "")
+ chatStorage = ChatStorage(
+ messageStore: .init(storage: RuntimeKeyValueStorage(), identifier: ""),
+ inviteStore: .init(storage: RuntimeKeyValueStorage(), identifier: ""),
+ threadStore: threadStore
+ )
+ resubscriptionService = ResubscriptionService(networkingInteractor: networkingInteractor, accountService: accountService, chatStorage: chatStorage, logger: ConsoleLoggerMock())
registryManager = RegistryService(
registry: registry,
+ accountService: accountService,
+ resubscriptionService: resubscriptionService,
networkingInteractor: networkingInteractor,
kms: kms,
logger: ConsoleLoggerMock(),
topicToRegistryRecordStore: topicToRegistryRecordStore)
}
- func testRegister() async {
- let account = Account("eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb")!
- _ = try! await registryManager.register(account: account)
+ func testRegister() async throws {
+ threadStore.set(Thread(topic: "topic1", selfAccount: mockAccount, peerAccount: mockAccount), for: newAccount.absoluteString)
+ threadStore.set(Thread(topic: "topic2", selfAccount: mockAccount, peerAccount: mockAccount), for: newAccount.absoluteString)
+
+ // Test accountService initial state
+ XCTAssertEqual(accountService.currentAccount, initialAccount)
+
+ let pubKey = try await registryManager.register(account: newAccount)
+
+ // Test subscription for invite topic
XCTAssert(!networkingInteractor.subscriptions.isEmpty, "networkingInteractors subscribes to new topic")
- let resolved = try! await registry.resolve(account: account)
- XCTAssertNotNil(resolved, "register account is resolvable")
+
+ // Test resubscription for threads
+ XCTAssertTrue(networkingInteractor.subscriptions.contains("topic1"))
+ XCTAssertTrue(networkingInteractor.subscriptions.contains("topic2"))
+
+ let resolved = try await registry.resolve(account: newAccount)
+
+ // Test resolved account pubKey
+ XCTAssertEqual(pubKey, resolved)
+
+ // Test topicToRegistryRecordStore filled
XCTAssertFalse(topicToRegistryRecordStore.getAll().isEmpty, "stores topic to invitation")
+
+ // Test current account changed
+ XCTAssertEqual(accountService.currentAccount, newAccount)
}
}
diff --git a/Tests/TestingUtils/NetworkingInteractorMock.swift b/Tests/TestingUtils/NetworkingInteractorMock.swift
index 323c52279..9fdd2eb54 100644
--- a/Tests/TestingUtils/NetworkingInteractorMock.swift
+++ b/Tests/TestingUtils/NetworkingInteractorMock.swift
@@ -23,6 +23,7 @@ public class NetworkingInteractorMock: NetworkInteracting {
var didCallRequest: Bool { requestCallCount > 0 }
var onSubscribeCalled: (() -> Void)?
+ var onRespondError: ((Int) -> Void)?
public let socketConnectionStatusPublisherSubject = PassthroughSubject()
public var socketConnectionStatusPublisher: AnyPublisher {
@@ -105,6 +106,12 @@ public class NetworkingInteractorMock: NetworkInteracting {
}
}
+ public func batchSubscribe(topics: [String]) async throws {
+ for topic in topics {
+ try await subscribe(topic: topic)
+ }
+ }
+
public func request(_ request: RPCRequest, topic: String, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws {
requestCallCount += 1
requests.append((topic, request))
@@ -121,6 +128,7 @@ public class NetworkingInteractorMock: NetworkInteracting {
public func respondError(topic: String, requestId: RPCID, protocolMethod: ProtocolMethod, reason: Reason, envelopeType: Envelope.EnvelopeType) async throws {
lastErrorCode = reason.code
didRespondError = true
+ onRespondError?(reason.code)
}
public func requestNetworkAck(_ request: RPCRequest, topic: String, protocolMethod: ProtocolMethod) async throws {
diff --git a/Tests/WalletConnectSignTests/AppProposalServiceTests.swift b/Tests/WalletConnectSignTests/AppProposalServiceTests.swift
index f733358ea..d10924417 100644
--- a/Tests/WalletConnectSignTests/AppProposalServiceTests.swift
+++ b/Tests/WalletConnectSignTests/AppProposalServiceTests.swift
@@ -62,7 +62,7 @@ final class AppProposalServiceTests: XCTestCase {
approveEngine = ApproveEngine(
networkingInteractor: networkingInteractor,
proposalPayloadsStore: .init(defaults: RuntimeKeyValueStorage(), identifier: ""),
- sessionToPairingTopic: CodableStore(defaults: RuntimeKeyValueStorage(), identifier: ""),
+ sessionTopicToProposal: CodableStore(defaults: RuntimeKeyValueStorage(), identifier: ""),
pairingRegisterer: pairingRegisterer,
metadata: meta,
kms: cryptoMock,
@@ -117,7 +117,7 @@ final class AppProposalServiceTests: XCTestCase {
networkingInteractor.responsePublisherSubject.send((topicA, request, response))
let privateKey = try! cryptoMock.getPrivateKey(for: proposal.proposer.publicKey)!
let topicB = deriveTopic(publicKey: responder.publicKey, privateKey: privateKey)
- let storedPairing = storageMock.getPairing(forTopic: topicA)!
+ _ = storageMock.getPairing(forTopic: topicA)!
wait(for: [exp], timeout: 5)
diff --git a/Tests/WalletConnectSignTests/ApproveEngineTests.swift b/Tests/WalletConnectSignTests/ApproveEngineTests.swift
index dfbf0523e..8db9dd112 100644
--- a/Tests/WalletConnectSignTests/ApproveEngineTests.swift
+++ b/Tests/WalletConnectSignTests/ApproveEngineTests.swift
@@ -18,6 +18,7 @@ final class ApproveEngineTests: XCTestCase {
var sessionStorageMock: WCSessionStorageMock!
var pairingRegisterer: PairingRegistererMock!
var proposalPayloadsStore: CodableStore>!
+ var sessionTopicToProposal: CodableStore!
var publishers = Set()
@@ -29,10 +30,11 @@ final class ApproveEngineTests: XCTestCase {
sessionStorageMock = WCSessionStorageMock()
pairingRegisterer = PairingRegistererMock()
proposalPayloadsStore = CodableStore>(defaults: RuntimeKeyValueStorage(), identifier: "")
+ sessionTopicToProposal = CodableStore(defaults: RuntimeKeyValueStorage(), identifier: "")
engine = ApproveEngine(
networkingInteractor: networkingInteractor,
proposalPayloadsStore: proposalPayloadsStore,
- sessionToPairingTopic: CodableStore(defaults: RuntimeKeyValueStorage(), identifier: ""),
+ sessionTopicToProposal: sessionTopicToProposal,
pairingRegisterer: pairingRegisterer,
metadata: metadata,
kms: cryptoMock,
@@ -92,7 +94,7 @@ final class ApproveEngineTests: XCTestCase {
let topicB = String.generateTopic()
cryptoMock.setAgreementSecret(agreementKeys, topic: topicB)
let proposal = SessionProposal.stub(proposerPubKey: AgreementPrivateKey().publicKey.hexRepresentation)
- try await engine.settle(topic: topicB, proposal: proposal, namespaces: SessionNamespace.stubDictionary())
+ try await engine.settle(topic: topicB, proposal: proposal, namespaces: SessionNamespace.stubDictionary(), pairingTopic: "")
XCTAssertTrue(sessionStorageMock.hasSession(forTopic: topicB), "Responder must persist session on topic B")
XCTAssert(networkingInteractor.didSubscribe(to: topicB), "Responder must subscribe for topic B")
XCTAssertTrue(networkingInteractor.didCallRequest, "Responder must send session settle payload on topic B")
@@ -105,8 +107,7 @@ final class ApproveEngineTests: XCTestCase {
engine.onSessionSettle = { _ in
didCallBackOnSessionApproved = true
}
-
- engine.settlingProposal = SessionProposal.stub()
+ sessionTopicToProposal.set(SessionProposal.stub().publicRepresentation(pairingTopic: ""), forKey: sessionTopic)
networkingInteractor.requestPublisherSubject.send((sessionTopic, RPCRequest.stubSettle()))
usleep(100)
diff --git a/Tests/WalletConnectSignTests/NamespaceValidationTests.swift b/Tests/WalletConnectSignTests/NamespaceValidationTests.swift
index 599b40c70..6fea012f9 100644
--- a/Tests/WalletConnectSignTests/NamespaceValidationTests.swift
+++ b/Tests/WalletConnectSignTests/NamespaceValidationTests.swift
@@ -18,16 +18,12 @@ final class NamespaceValidationTests: XCTestCase {
"eip155": ProposalNamespace(
chains: [ethChain],
methods: ["method"],
- events: ["event"],
- extensions: [
- ProposalNamespace.Extension(chains: [Blockchain("eip155:137")!], methods: ["otherMethod"], events: ["otherEvent"])
- ]
+ events: ["event"]
),
"cosmos": ProposalNamespace(
chains: [cosmosChain],
methods: ["someMethod"],
- events: ["someEvent"],
- extensions: nil
+ events: ["someEvent"]
)
]
XCTAssertNoThrow(try Namespace.validate(namespace))
@@ -38,8 +34,7 @@ final class NamespaceValidationTests: XCTestCase {
"eip155": ProposalNamespace(
chains: [],
methods: ["method"],
- events: ["event"],
- extensions: nil)
+ events: ["event"])
]
XCTAssertThrowsError(try Namespace.validate(namespace))
}
@@ -49,8 +44,7 @@ final class NamespaceValidationTests: XCTestCase {
"eip155": ProposalNamespace(
chains: [ethChain],
methods: [],
- events: ["event"],
- extensions: nil)
+ events: ["event"])
]
XCTAssertNoThrow(try Namespace.validate(namespace))
}
@@ -60,8 +54,7 @@ final class NamespaceValidationTests: XCTestCase {
"eip155": ProposalNamespace(
chains: [ethChain],
methods: ["method"],
- events: [],
- extensions: nil)
+ events: [])
]
XCTAssertNoThrow(try Namespace.validate(namespace))
}
@@ -71,43 +64,26 @@ final class NamespaceValidationTests: XCTestCase {
"eip155": ProposalNamespace(
chains: [ethChain, Blockchain("eip155:137")!, Blockchain("eip155:10")!],
methods: ["method"],
- events: ["event"],
- extensions: nil)
+ events: ["event"])
]
let invalidNamespace = [
"eip155": ProposalNamespace(
chains: [ethChain, Blockchain("cosmos:cosmoshub-4")!],
methods: ["method"],
- events: ["event"],
- extensions: nil)
+ events: ["event"])
]
XCTAssertNoThrow(try Namespace.validate(validNamespace))
XCTAssertThrowsError(try Namespace.validate(invalidNamespace))
}
- func testExtensionChainsMustNotBeEmpty() {
- let namespace = [
- "eip155": ProposalNamespace(
- chains: [ethChain],
- methods: ["method"],
- events: ["event"],
- extensions: [
- ProposalNamespace.Extension(chains: [], methods: ["otherMethod"], events: ["otherEvent"])
- ]
- )
- ]
- XCTAssertThrowsError(try Namespace.validate(namespace))
- }
-
func testValidateAllProposalNamespaces() {
let namespace = [
"eip155": ProposalNamespace(
chains: [ethChain],
methods: ["method"],
- events: ["event"],
- extensions: nil),
+ events: ["event"]),
"cosmos": ProposalNamespace(
- chains: [], methods: [], events: [], extensions: nil)
+ chains: [], methods: [], events: [])
]
XCTAssertThrowsError(try Namespace.validate(namespace))
}
@@ -119,10 +95,7 @@ final class NamespaceValidationTests: XCTestCase {
"eip155": SessionNamespace(
accounts: [ethAccount],
methods: ["method"],
- events: ["event"],
- extensions: [
- SessionNamespace.Extension(accounts: [polyAccount], methods: ["otherMethod"], events: ["otherEvent"])
- ]
+ events: ["event"]
)
]
XCTAssertNoThrow(try Namespace.validate(namespace))
@@ -133,8 +106,7 @@ final class NamespaceValidationTests: XCTestCase {
"eip155": SessionNamespace(
accounts: [],
methods: ["method"],
- events: ["event"],
- extensions: nil)
+ events: ["event"])
]
XCTAssertThrowsError(try Namespace.validate(namespace))
}
@@ -144,8 +116,7 @@ final class NamespaceValidationTests: XCTestCase {
"eip155": SessionNamespace(
accounts: [ethAccount],
methods: [],
- events: ["event"],
- extensions: nil)
+ events: ["event"])
]
XCTAssertNoThrow(try Namespace.validate(namespace))
}
@@ -155,8 +126,7 @@ final class NamespaceValidationTests: XCTestCase {
"eip155": SessionNamespace(
accounts: [ethAccount],
methods: ["method"],
- events: [],
- extensions: nil)
+ events: [])
]
XCTAssertNoThrow(try Namespace.validate(namespace))
}
@@ -166,43 +136,26 @@ final class NamespaceValidationTests: XCTestCase {
"eip155": SessionNamespace(
accounts: [ethAccount, polyAccount],
methods: ["method"],
- events: ["event"],
- extensions: nil)
+ events: ["event"])
]
let invalidNamespace = [
"eip155": SessionNamespace(
accounts: [ethAccount, cosmosAccount],
methods: ["method"],
- events: ["event"],
- extensions: nil)
+ events: ["event"])
]
XCTAssertNoThrow(try Namespace.validate(validNamespace))
XCTAssertThrowsError(try Namespace.validate(invalidNamespace))
}
- func testExtensionAccountsMustNotBeEmpty() {
- let namespace = [
- "eip155": SessionNamespace(
- accounts: [ethAccount],
- methods: ["method"],
- events: ["event"],
- extensions: [
- SessionNamespace.Extension(accounts: [], methods: ["otherMethod"], events: ["otherEvent"])
- ]
- )
- ]
- XCTAssertThrowsError(try Namespace.validate(namespace))
- }
-
func testValidateAllSessionNamespaces() {
let namespace = [
"eip155": SessionNamespace(
accounts: [ethAccount],
methods: ["method"],
- events: ["event"],
- extensions: nil),
+ events: ["event"]),
"cosmos": SessionNamespace(
- accounts: [], methods: [], events: [], extensions: nil)
+ accounts: [], methods: [], events: [])
]
XCTAssertThrowsError(try Namespace.validate(namespace))
}
@@ -214,22 +167,19 @@ final class NamespaceValidationTests: XCTestCase {
"eip155": ProposalNamespace(
chains: [ethChain],
methods: ["eth_sign"],
- events: [],
- extensions: nil)
+ events: [])
]
let validSessionNamespace = [
"eip155": SessionNamespace(
accounts: [ethAccount],
methods: ["eth_sign"],
- events: [],
- extensions: nil)
+ events: [])
]
let invalidSessionNamespace = [
"eip155": SessionNamespace(
accounts: [ethAccount],
methods: [],
- events: [],
- extensions: nil)
+ events: [])
]
XCTAssertNoThrow(try Namespace.validateApproved(validSessionNamespace, against: proposalNamespace))
XCTAssertThrowsError(try Namespace.validateApproved(invalidSessionNamespace, against: proposalNamespace))
@@ -240,22 +190,19 @@ final class NamespaceValidationTests: XCTestCase {
"eip155": ProposalNamespace(
chains: [ethChain, polyChain],
methods: ["eth_sign"],
- events: ["accountsChanged"],
- extensions: nil)
+ events: ["accountsChanged"])
]
let validSessionNamespace = [
"eip155": SessionNamespace(
accounts: [ethAccount, polyAccount],
methods: ["eth_sign"],
- events: ["accountsChanged"],
- extensions: nil)
+ events: ["accountsChanged"])
]
let invalidSessionNamespace = [
"eip155": SessionNamespace(
accounts: [ethAccount],
methods: ["eth_sign"],
- events: ["accountsChanged"],
- extensions: nil)
+ events: ["accountsChanged"])
]
XCTAssertNoThrow(try Namespace.validateApproved(validSessionNamespace, against: proposalNamespace))
XCTAssertThrowsError(try Namespace.validateApproved(invalidSessionNamespace, against: proposalNamespace))
@@ -266,8 +213,7 @@ final class NamespaceValidationTests: XCTestCase {
"eip155": ProposalNamespace(
chains: [ethChain],
methods: ["eth_sign"],
- events: ["accountsChanged"],
- extensions: nil)
+ events: ["accountsChanged"])
]
let sessionNamespace = [
"eip155": SessionNamespace(
@@ -277,8 +223,7 @@ final class NamespaceValidationTests: XCTestCase {
Account("eip155:1:0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8")!,
Account("eip155:1:0xEB2F31B0224222D774541BfF89A221e7eb15a17E")!],
methods: ["eth_sign"],
- events: ["accountsChanged"],
- extensions: nil)
+ events: ["accountsChanged"])
]
XCTAssertNoThrow(try Namespace.validateApproved(sessionNamespace, against: proposalNamespace))
}
@@ -288,15 +233,13 @@ final class NamespaceValidationTests: XCTestCase {
"eip155": ProposalNamespace(
chains: [ethChain],
methods: ["eth_sign"],
- events: ["accountsChanged"],
- extensions: nil)
+ events: ["accountsChanged"])
]
let sessionNamespace = [
"eip155": SessionNamespace(
accounts: [ethAccount],
methods: ["eth_sign", "personalSign"],
- events: ["accountsChanged", "someEvent"],
- extensions: nil)
+ events: ["accountsChanged", "someEvent"])
]
XCTAssertNoThrow(try Namespace.validateApproved(sessionNamespace, against: proposalNamespace))
}
@@ -306,15 +249,13 @@ final class NamespaceValidationTests: XCTestCase {
"eip155": ProposalNamespace(
chains: [ethChain],
methods: ["eth_sign"],
- events: ["accountsChanged"],
- extensions: nil)
+ events: ["accountsChanged"])
]
let sessionNamespace = [
"eip155": SessionNamespace(
accounts: [ethAccount, polyAccount],
methods: ["eth_sign"],
- events: ["accountsChanged"],
- extensions: nil)
+ events: ["accountsChanged"])
]
XCTAssertNoThrow(try Namespace.validateApproved(sessionNamespace, against: proposalNamespace))
}
@@ -324,154 +265,45 @@ final class NamespaceValidationTests: XCTestCase {
"eip155": ProposalNamespace(
chains: [ethChain],
methods: ["eth_sign"],
- events: ["accountsChanged"],
- extensions: nil),
+ events: ["accountsChanged"]),
"cosmos": ProposalNamespace(
chains: [cosmosChain],
methods: ["cosmos_signDirect"],
- events: ["someEvent"],
- extensions: nil)
+ events: ["someEvent"])
]
let validNamespace = [
"eip155": SessionNamespace(
accounts: [ethAccount],
methods: ["eth_sign"],
- events: ["accountsChanged"],
- extensions: nil),
+ events: ["accountsChanged"]),
"cosmos": SessionNamespace(
accounts: [cosmosAccount],
methods: ["cosmos_signDirect"],
- events: ["someEvent"],
- extensions: nil)
+ events: ["someEvent"])
]
let invalidNamespace = [
"eip155": SessionNamespace(
accounts: [ethAccount],
methods: ["eth_sign", "cosmos_signDirect"],
- events: ["accountsChanged", "someEvent"],
- extensions: nil)
+ events: ["accountsChanged", "someEvent"])
]
XCTAssertNoThrow(try Namespace.validateApproved(validNamespace, against: proposalNamespace))
XCTAssertThrowsError(try Namespace.validateApproved(invalidNamespace, against: proposalNamespace))
}
- func testExtensionsMayBeMerged() {
- let proposalNamespace = [
- "eip155": ProposalNamespace(
- chains: [ethChain, polyChain],
- methods: ["eth_sign"],
- events: ["accountsChanged"],
- extensions: [
- ProposalNamespace.Extension(chains: [polyChain], methods: ["personalSign"], events: [])
- ]
- )
- ]
- let sessionNamespace = [
- "eip155": SessionNamespace(
- accounts: [ethAccount, polyAccount],
- methods: ["eth_sign"],
- events: ["accountsChanged", "personalSign"],
- extensions: nil)
- ]
- XCTAssertNoThrow(try Namespace.validateApproved(sessionNamespace, against: proposalNamespace))
- }
-
func testApprovalMustContainAllEvents() {
let proposalNamespace = [
"eip155": ProposalNamespace(
chains: [ethChain],
methods: [],
- events: ["chainChanged"],
- extensions: nil)
+ events: ["chainChanged"])
]
let sessionNamespace = [
"eip155": SessionNamespace(
accounts: [ethAccount],
methods: [],
- events: [],
- extensions: nil)
+ events: [])
]
XCTAssertThrowsError(try Namespace.validateApproved(sessionNamespace, against: proposalNamespace))
}
-
- func testApprovalMayExtendoMethodsAndEventsInExtensions() {
- let proposalNamespace = [
- "eip155": ProposalNamespace(
- chains: [ethChain, polyChain],
- methods: [],
- events: ["chainChanged"],
- extensions: [
- ProposalNamespace.Extension(chains: [polyChain], methods: ["eth_sign"], events: [])
- ]
- )
- ]
- let sessionNamespace = [
- "eip155": SessionNamespace(
- accounts: [ethAccount, polyAccount],
- methods: [],
- events: ["chainChanged"],
- extensions: [
- SessionNamespace.Extension(
- accounts: [polyAccount],
- methods: ["eth_sign", "personalSign"],
- events: ["accountsChanged"]
- )
- ]
- )
- ]
- XCTAssertNoThrow(try Namespace.validateApproved(sessionNamespace, against: proposalNamespace))
- }
-
- func testApprovalExtensionsMayContainAccountsNotDefinedInProposal() {
- let proposalNamespace = [
- "eip155": ProposalNamespace(
- chains: [ethChain, polyChain],
- methods: ["eth_sign"],
- events: ["accountsChanged"],
- extensions: [
- ProposalNamespace.Extension(chains: [polyChain], methods: ["personalSign"], events: [])
- ]
- )
- ]
- let sessionNamespace = [
- "eip155": SessionNamespace(
- accounts: [ethAccount, polyAccount],
- methods: ["eth_sign"],
- events: ["accountsChanged"],
- extensions: [
- SessionNamespace.Extension(
- accounts: [polyAccount, Account("eip155:42:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb")!],
- methods: ["personalSign"],
- events: []
- )
- ]
- )
- ]
- XCTAssertNoThrow(try Namespace.validateApproved(sessionNamespace, against: proposalNamespace))
- }
-
- func testApprovalMayAddExtensionsNotDefinedInProposal() {
- let proposalNamespace = [
- "eip155": ProposalNamespace(
- chains: [ethChain, polyChain],
- methods: ["eth_sign"],
- events: ["accountsChanged"],
- extensions: nil)
- ]
- let sessionNamespace = [
- "eip155": SessionNamespace(
- accounts: [ethAccount, polyAccount],
- methods: ["eth_sign"],
- events: ["accountsChanged"],
- extensions: [
- SessionNamespace.Extension(
- accounts: [polyAccount],
- methods: ["personalSign"],
- events: ["accountsChanged"]
- )
- ]
- )
- ]
- XCTAssertNoThrow(try Namespace.validateApproved(sessionNamespace, against: proposalNamespace))
- }
}
diff --git a/Tests/WalletConnectSignTests/SessionEngineTests.swift b/Tests/WalletConnectSignTests/SessionEngineTests.swift
new file mode 100644
index 000000000..051329ffe
--- /dev/null
+++ b/Tests/WalletConnectSignTests/SessionEngineTests.swift
@@ -0,0 +1,54 @@
+import XCTest
+@testable import WalletConnectSign
+@testable import WalletConnectUtils
+@testable import TestingUtils
+
+final class SessionEngineTests: XCTestCase {
+
+ var networkingInteractor: NetworkingInteractorMock!
+ var sessionStorage: WCSessionStorageMock!
+ var engine: SessionEngine!
+
+ override func setUp() {
+ networkingInteractor = NetworkingInteractorMock()
+ sessionStorage = WCSessionStorageMock()
+ engine = SessionEngine(
+ networkingInteractor: networkingInteractor,
+ historyService: HistoryService(
+ history: RPCHistory(
+ keyValueStore: .init(
+ defaults: RuntimeKeyValueStorage(),
+ identifier: ""
+ )
+ )
+ ),
+ kms: KeyManagementServiceMock(),
+ sessionStore: sessionStorage,
+ logger: ConsoleLoggerMock()
+ )
+ }
+
+ func testErrorOnRequestExpiry() {
+ let expectation = expectation(description: "TestErrorOnRequestExpiry")
+
+ sessionStorage.setSession(WCSession.stub(
+ topic: "topic",
+ namespaces: SessionNamespace.stubDictionary()
+ ))
+
+ networkingInteractor.onRespondError = { code in
+ XCTAssertEqual(code, 8000)
+ expectation.fulfill()
+ }
+
+ let request = RPCRequest.stubRequest(
+ method: "method",
+ chainId: Blockchain("eip155:1")!,
+ expiry: UInt64(Date().timeIntervalSince1970)
+ )
+
+ networkingInteractor.requestPublisherSubject.send(("topic", request))
+
+ wait(for: [expectation], timeout: 0.5)
+ }
+}
diff --git a/Tests/WalletConnectSignTests/SessionRequestTests.swift b/Tests/WalletConnectSignTests/SessionRequestTests.swift
new file mode 100644
index 000000000..e89ff347d
--- /dev/null
+++ b/Tests/WalletConnectSignTests/SessionRequestTests.swift
@@ -0,0 +1,83 @@
+import XCTest
+@testable import WalletConnectSign
+
+final class SessionRequestTests: XCTestCase {
+
+ func testRequestTtlDefault() {
+ let request = Request.stub()
+
+ XCTAssertEqual(request.calculateTtl(), SessionRequestProtocolMethod.defaultTtl)
+ }
+
+ func testRequestTtlExtended() {
+ let currentDate = Date(timeIntervalSince1970: 0)
+ let expiry = currentDate.advanced(by: 500)
+ let request = Request.stub(expiry: UInt64(expiry.timeIntervalSince1970))
+
+ XCTAssertEqual(request.calculateTtl(currentDate: currentDate), 500)
+ }
+
+ func testRequestTtlNotExtendedMinValidation() {
+ let currentDate = Date(timeIntervalSince1970: 0)
+ let expiry = currentDate.advanced(by: 200)
+ let request = Request.stub(expiry: UInt64(expiry.timeIntervalSince1970))
+
+ XCTAssertEqual(request.calculateTtl(currentDate: currentDate), SessionRequestProtocolMethod.defaultTtl)
+ }
+
+ func testRequestTtlNotExtendedMaxValidation() {
+ let currentDate = Date(timeIntervalSince1970: 0)
+ let expiry = currentDate.advanced(by: 700000)
+ let request = Request.stub(expiry: UInt64(expiry.timeIntervalSince1970))
+
+ XCTAssertEqual(request.calculateTtl(currentDate: currentDate), SessionRequestProtocolMethod.defaultTtl)
+ }
+
+ func testIsExpiredDefault() {
+ let request = Request.stub()
+
+ XCTAssertFalse(request.isExpired())
+ }
+
+ func testIsExpiredTrue() {
+ let currentDate = Date(timeIntervalSince1970: 500)
+ let expiry = Date(timeIntervalSince1970: 0)
+ let request = Request.stub(expiry: UInt64(expiry.timeIntervalSince1970))
+ XCTAssertTrue(request.isExpired(currentDate: currentDate))
+ }
+
+ func testIsExpiredTrueMinValidation() {
+ let currentDate = Date(timeIntervalSince1970: 500)
+ let expiry = Date(timeIntervalSince1970: 600)
+ let request = Request.stub(expiry: UInt64(expiry.timeIntervalSince1970))
+ XCTAssertTrue(request.isExpired(currentDate: currentDate))
+ }
+
+ func testIsExpiredTrueMaxValidation() {
+ let currentDate = Date(timeIntervalSince1970: 500)
+ let expiry = Date(timeIntervalSince1970: 700000)
+ let request = Request.stub(expiry: UInt64(expiry.timeIntervalSince1970))
+ XCTAssertTrue(request.isExpired(currentDate: currentDate))
+ }
+
+ func testIsExpiredFalse() {
+ let currentDate = Date(timeIntervalSince1970: 0)
+ let expiry = Date(timeIntervalSince1970: 500)
+ let request = Request.stub(expiry: UInt64(expiry.timeIntervalSince1970))
+
+ XCTAssertFalse(request.isExpired(currentDate: currentDate))
+ }
+}
+
+private extension Request {
+
+ static func stub(expiry: UInt64? = nil) -> Request {
+ return Request(
+ topic: "topic",
+ method: "method",
+ params: AnyCodable("params"),
+ chainId: Blockchain("eip155:1")!,
+ expiry: expiry
+ )
+ }
+}
diff --git a/Tests/WalletConnectSignTests/Stub/Session+Stub.swift b/Tests/WalletConnectSignTests/Stub/Session+Stub.swift
index bbef34c9b..e60b778ec 100644
--- a/Tests/WalletConnectSignTests/Stub/Session+Stub.swift
+++ b/Tests/WalletConnectSignTests/Stub/Session+Stub.swift
@@ -18,6 +18,7 @@ extension WCSession {
let controllerKey = isSelfController ? selfKey : peerKey
return WCSession(
topic: topic,
+ pairingTopic: "",
timestamp: timestamp,
relay: RelayProtocolOptions.stub(),
controller: AgreementPeer(publicKey: controllerKey),
diff --git a/Tests/WalletConnectSignTests/Stub/Stubs.swift b/Tests/WalletConnectSignTests/Stub/Stubs.swift
index c83298a20..d34075b0c 100644
--- a/Tests/WalletConnectSignTests/Stub/Stubs.swift
+++ b/Tests/WalletConnectSignTests/Stub/Stubs.swift
@@ -24,8 +24,7 @@ extension ProposalNamespace {
"eip155": ProposalNamespace(
chains: [Blockchain("eip155:1")!],
methods: ["method"],
- events: ["event"],
- extensions: nil)
+ events: ["event"])
]
}
}
@@ -36,8 +35,7 @@ extension SessionNamespace {
"eip155": SessionNamespace(
accounts: [Account("eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb")!],
methods: ["method"],
- events: ["event"],
- extensions: nil)
+ events: ["event"])
]
}
}
@@ -68,9 +66,9 @@ extension RPCRequest {
return RPCRequest(method: SessionSettleProtocolMethod().method, params: SessionType.SettleParams.stub())
}
- static func stubRequest(method: String, chainId: Blockchain) -> RPCRequest {
+ static func stubRequest(method: String, chainId: Blockchain, expiry: UInt64? = nil) -> RPCRequest {
let params = SessionType.RequestParams(
- request: SessionType.RequestParams.Request(method: method, params: AnyCodable(EmptyCodable())),
+ request: SessionType.RequestParams.Request(method: method, params: AnyCodable(EmptyCodable()), expiry: expiry),
chainId: chainId)
return RPCRequest(method: SessionRequestProtocolMethod().method, params: params)
}
diff --git a/Tests/WalletConnectSignTests/WCSessionTests.swift b/Tests/WalletConnectSignTests/WCSessionTests.swift
index 3a0d567b4..097dd8883 100644
--- a/Tests/WalletConnectSignTests/WCSessionTests.swift
+++ b/Tests/WalletConnectSignTests/WCSessionTests.swift
@@ -14,25 +14,7 @@ final class WCSessionTests: XCTestCase {
"eip155": SessionNamespace(
accounts: [ethAccount],
methods: ["method"],
- events: [],
- extensions: nil)
- ]
- var session = WCSession.stub()
- XCTAssertNoThrow(try session.updateNamespaces(namespace))
- XCTAssertTrue(session.hasPermission(forMethod: "method", onChain: ethAccount.blockchain))
- }
-
- func testHasPermissionForMethodInExtension() {
- let namespace = [
- "eip155": SessionNamespace(
- accounts: [ethAccount],
- methods: [],
- events: [],
- extensions: [
- SessionNamespace.Extension(
- accounts: [ethAccount],
- methods: ["method"],
- events: [])])
+ events: [])
]
var session = WCSession.stub()
XCTAssertNoThrow(try session.updateNamespaces(namespace))
@@ -44,30 +26,11 @@ final class WCSessionTests: XCTestCase {
"eip155": SessionNamespace(
accounts: [ethAccount],
methods: [],
- events: [],
- extensions: nil),
+ events: []),
"cosmos": SessionNamespace(
accounts: [cosmosAccount],
methods: ["method"],
- events: [],
- extensions: nil)
- ]
- var session = WCSession.stub()
- XCTAssertNoThrow(try session.updateNamespaces(namespace))
- XCTAssertFalse(session.hasPermission(forMethod: "method", onChain: ethAccount.blockchain))
- }
-
- func testDenyPermissionForMethodInOtherChainExtension() {
- let namespace = [
- "eip155": SessionNamespace(
- accounts: [ethAccount, polyAccount],
- methods: [],
- events: [],
- extensions: [
- SessionNamespace.Extension(
- accounts: [polyAccount],
- methods: ["method"],
- events: [])])
+ events: [])
]
var session = WCSession.stub()
XCTAssertNoThrow(try session.updateNamespaces(namespace))
@@ -79,25 +42,7 @@ final class WCSessionTests: XCTestCase {
"eip155": SessionNamespace(
accounts: [ethAccount],
methods: [],
- events: ["event"],
- extensions: nil)
- ]
- var session = WCSession.stub()
- XCTAssertNoThrow(try session.updateNamespaces(namespace))
- XCTAssertTrue(session.hasPermission(forEvent: "event", onChain: ethAccount.blockchain))
- }
-
- func testHasPermissionForEventInExtension() {
- let namespace = [
- "eip155": SessionNamespace(
- accounts: [ethAccount],
- methods: [],
- events: [],
- extensions: [
- SessionNamespace.Extension(
- accounts: [ethAccount],
- methods: [],
- events: ["event"])])
+ events: ["event"])
]
var session = WCSession.stub()
XCTAssertNoThrow(try session.updateNamespaces(namespace))
@@ -109,30 +54,11 @@ final class WCSessionTests: XCTestCase {
"eip155": SessionNamespace(
accounts: [ethAccount],
methods: [],
- events: [],
- extensions: nil),
+ events: []),
"cosmos": SessionNamespace(
accounts: [cosmosAccount],
methods: [],
- events: ["event"],
- extensions: nil)
- ]
- var session = WCSession.stub()
- XCTAssertNoThrow(try session.updateNamespaces(namespace))
- XCTAssertFalse(session.hasPermission(forEvent: "event", onChain: ethAccount.blockchain))
- }
-
- func testDenyPermissionForEventInOtherChainExtension() {
- let namespace = [
- "eip155": SessionNamespace(
- accounts: [ethAccount, polyAccount],
- methods: [],
- events: [],
- extensions: [
- SessionNamespace.Extension(
- accounts: [polyAccount],
- methods: [],
- events: ["event"])])
+ events: ["event"])
]
var session = WCSession.stub()
XCTAssertNoThrow(try session.updateNamespaces(namespace))
@@ -146,8 +72,7 @@ final class WCSessionTests: XCTestCase {
"eip155": ProposalNamespace(
chains: [ethAccount.blockchain, polyAccount.blockchain],
methods: ["method", "method-2"],
- events: ["event", "event-2"],
- extensions: nil)]
+ events: ["event", "event-2"])]
}
private func stubCompliantNamespaces() -> [String: SessionNamespace] {
@@ -155,21 +80,7 @@ final class WCSessionTests: XCTestCase {
"eip155": SessionNamespace(
accounts: [ethAccount, polyAccount],
methods: ["method", "method-2"],
- events: ["event", "event-2"],
- extensions: nil)]
- }
-
- private func stubRequiredNamespacesWithExtension() -> [String: ProposalNamespace] {
- return [
- "eip155": ProposalNamespace(
- chains: [ethAccount.blockchain, polyAccount.blockchain],
- methods: ["method", "method-2"],
- events: ["event", "event-2"],
- extensions: [
- ProposalNamespace.Extension(
- chains: [ethAccount.blockchain, polyAccount.blockchain],
- methods: ["method-2", "newMethod-2"],
- events: ["event-2", "newEvent-2"])])]
+ events: ["event", "event-2"])]
}
func testUpdateEqualNamespaces() {
@@ -182,22 +93,12 @@ final class WCSessionTests: XCTestCase {
"eip155": SessionNamespace(
accounts: [ethAccount],
methods: ["method"],
- events: ["event"],
- extensions: [
- SessionNamespace.Extension(
- accounts: [ethAccount],
- methods: ["method-2"],
- events: ["event-2"])])]
+ events: ["event"])]
let newNamespace = [
"eip155": SessionNamespace(
accounts: [ethAccount, polyAccount],
methods: ["method", "newMethod"],
- events: ["event", "newEvent"],
- extensions: [
- SessionNamespace.Extension(
- accounts: [ethAccount, polyAccount],
- methods: ["method-2", "newMethod-2"],
- events: ["event-2", "newEvent-2"])])]
+ events: ["event", "newEvent"])]
var session = WCSession.stub(namespaces: namespace)
XCTAssertNoThrow(try session.updateNamespaces(newNamespace))
}
@@ -213,8 +114,7 @@ final class WCSessionTests: XCTestCase {
"eip155": SessionNamespace(
accounts: [newEthAccount, polyAccount],
methods: ["method", "method-2"],
- events: ["event", "event-2"],
- extensions: nil)]
+ events: ["event", "event-2"])]
var session = WCSession.stub(requiredNamespaces: stubRequiredNamespaces())
XCTAssertNoThrow(try session.updateNamespaces(valid))
}
@@ -224,8 +124,7 @@ final class WCSessionTests: XCTestCase {
"eip155": SessionNamespace(
accounts: [ethAccount],
methods: ["method", "method-2"],
- events: ["event", "event-2"],
- extensions: nil)]
+ events: ["event", "event-2"])]
var session = WCSession.stub(requiredNamespaces: stubRequiredNamespaces())
XCTAssertThrowsError(try session.updateNamespaces(invalid))
}
@@ -235,8 +134,7 @@ final class WCSessionTests: XCTestCase {
"eip155": SessionNamespace(
accounts: [ethAccount, polyAccount],
methods: ["method"],
- events: ["event", "event-2"],
- extensions: nil)]
+ events: ["event", "event-2"])]
var session = WCSession.stub(requiredNamespaces: stubRequiredNamespaces())
XCTAssertThrowsError(try session.updateNamespaces(invalid))
}
@@ -246,65 +144,8 @@ final class WCSessionTests: XCTestCase {
"eip155": SessionNamespace(
accounts: [ethAccount, polyAccount],
methods: ["method", "method-2"],
- events: ["event"],
- extensions: nil)]
+ events: ["event"])]
var session = WCSession.stub(requiredNamespaces: stubRequiredNamespaces())
XCTAssertThrowsError(try session.updateNamespaces(invalid))
}
-
- func testUpdateLessThanRequiredExtension() {
- let invalid = [
- "eip155": SessionNamespace(
- accounts: [ethAccount, polyAccount],
- methods: ["method", "method-2"],
- events: ["event", "event-2"],
- extensions: nil)]
- var session = WCSession.stub(requiredNamespaces: stubRequiredNamespacesWithExtension())
- XCTAssertThrowsError(try session.updateNamespaces(invalid))
- }
-
- func testUpdateLessThanRequiredExtensionAccounts() {
- let invalid = [
- "eip155": SessionNamespace(
- accounts: [ethAccount, polyAccount],
- methods: ["method", "method-2"],
- events: ["event", "event-2"],
- extensions: [
- SessionNamespace.Extension(
- accounts: [ethAccount],
- methods: ["method-2", "newMethod-2"],
- events: ["event-2", "newEvent-2"])])]
- var session = WCSession.stub(requiredNamespaces: stubRequiredNamespacesWithExtension())
- XCTAssertThrowsError(try session.updateNamespaces(invalid))
- }
-
- func testUpdateLessThanRequiredExtensionMethods() {
- let invalid = [
- "eip155": SessionNamespace(
- accounts: [ethAccount, polyAccount],
- methods: ["method", "method-2"],
- events: ["event", "event-2"],
- extensions: [
- SessionNamespace.Extension(
- accounts: [ethAccount, polyAccount],
- methods: ["method-2"],
- events: ["event-2", "newEvent-2"])])]
- var session = WCSession.stub(requiredNamespaces: stubRequiredNamespacesWithExtension())
- XCTAssertThrowsError(try session.updateNamespaces(invalid))
- }
-
- func testUpdateLessThanRequiredExtensionEvents() {
- let invalid = [
- "eip155": SessionNamespace(
- accounts: [ethAccount, polyAccount],
- methods: ["method", "method-2"],
- events: ["event", "event-2"],
- extensions: [
- SessionNamespace.Extension(
- accounts: [ethAccount, polyAccount],
- methods: ["method-2", "newMethod-2"],
- events: ["event-2"])])]
- var session = WCSession.stub(requiredNamespaces: stubRequiredNamespacesWithExtension())
- XCTAssertThrowsError(try session.updateNamespaces(invalid))
- }
}
diff --git a/Tests/Web3WalletTests/Mocks/SignClientMock.swift b/Tests/Web3WalletTests/Mocks/SignClientMock.swift
index 72ad8f006..ab03aca18 100644
--- a/Tests/Web3WalletTests/Mocks/SignClientMock.swift
+++ b/Tests/Web3WalletTests/Mocks/SignClientMock.swift
@@ -16,7 +16,7 @@ final class SignClientMock: SignClientProtocol {
var disconnectCalled = false
private let metadata = AppMetadata(name: "", description: "", url: "", icons: [])
- private let request = WalletConnectSign.Request(id: .left(""), topic: "", method: "", params: "", chainId: Blockchain("eip155:1")!)
+ private let request = WalletConnectSign.Request(id: .left(""), topic: "", method: "", params: "", chainId: Blockchain("eip155:1")!, expiry: nil)
var sessionProposalPublisher: AnyPublisher {
let proposer = Participant(publicKey: "", metadata: metadata)
@@ -24,8 +24,7 @@ final class SignClientMock: SignClientProtocol {
relays: [],
proposer: proposer,
requiredNamespaces: [:]
- )
- .publicRepresentation()
+ ).publicRepresentation(pairingTopic: "")
return Result.Publisher(sessionProposal)
.eraseToAnyPublisher()
@@ -37,7 +36,7 @@ final class SignClientMock: SignClientProtocol {
}
var sessionsPublisher: AnyPublisher<[WalletConnectSign.Session], Never> {
- return Result.Publisher([WalletConnectSign.Session(topic: "", peer: metadata, namespaces: [:], expiryDate: Date())])
+ return Result.Publisher([WalletConnectSign.Session(topic: "", pairingTopic: "", peer: metadata, namespaces: [:], expiryDate: Date())])
.eraseToAnyPublisher()
}
@@ -74,7 +73,7 @@ final class SignClientMock: SignClientProtocol {
}
func getSessions() -> [WalletConnectSign.Session] {
- return [WalletConnectSign.Session(topic: "", peer: metadata, namespaces: [:], expiryDate: Date())]
+ return [WalletConnectSign.Session(topic: "", pairingTopic: "", peer: metadata, namespaces: [:], expiryDate: Date())]
}
func getPendingRequests(topic: String?) -> [WalletConnectSign.Request] {
diff --git a/fastlane/Appfile b/fastlane/Appfile
new file mode 100644
index 000000000..9fae697da
--- /dev/null
+++ b/fastlane/Appfile
@@ -0,0 +1,3 @@
+itc_team_id("123564616")
+team_id("W5R8AG9K22")
+git_url("https://github.com/WalletConnect/match-swift")
diff --git a/fastlane/Fastfile b/fastlane/Fastfile
new file mode 100644
index 000000000..ba33be662
--- /dev/null
+++ b/fastlane/Fastfile
@@ -0,0 +1,82 @@
+# This file contains the fastlane.tools configuration
+# You can find the documentation at https://docs.fastlane.tools
+#
+# For a list of all available actions, check out
+#
+# https://docs.fastlane.tools/actions
+#
+# For a list of all available plugins, check out
+#
+# https://docs.fastlane.tools/plugins/available-plugins
+#
+
+# Uncomment the line if you want fastlane to automatically update itself
+# update_fastlane
+
+ENV["FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT"] = "120"
+
+default_platform(:ios)
+
+platform :ios do
+
+ lane :tests do |options|
+ run_tests(
+ project: 'Example/ExampleApp.xcodeproj',
+ scheme: options[:scheme],
+ cloned_source_packages_path: 'SourcePackagesCache',
+ destination: 'platform=iOS Simulator,name=iPhone 13',
+ derived_data_path: 'DerivedDataCache',
+ skip_package_dependencies_resolution: true,
+ skip_build: true,
+ xcargs: "RELAY_HOST='#{options[:relay_host]}' PROJECT_ID='#{options[:project_id]}'"
+ )
+ end
+
+ lane :build do |options|
+ xcodebuild(
+ project: 'Example/ExampleApp.xcodeproj',
+ scheme: options[:scheme],
+ destination: 'platform=iOS Simulator,name=iPhone 13',
+ xcargs: "-clonedSourcePackagesDirPath SourcePackagesCache -derivedDataPath DerivedDataCache"
+ )
+ end
+
+ lane :resolve do |options|
+ xcodebuild(
+ project: 'Example/ExampleApp.xcodeproj',
+ scheme: options[:scheme],
+ destination: 'platform=iOS Simulator,name=iPhone 13',
+ xcargs: "-resolvePackageDependencies -clonedSourcePackagesDirPath SourcePackagesCache -derivedDataPath DerivedDataCache"
+ )
+ end
+
+ lane :release_testflight do |options|
+ match(
+ readonly: false,
+ type: "appstore",
+ app_identifier: ENV["APP_IDENTIFIER"],
+ git_url: "https://github.com/WalletConnect/match-swift.git",
+ username: options[:username],
+ )
+ number = latest_testflight_build_number(
+ app_identifier: ENV["APP_IDENTIFIER"],
+ username: options[:username],
+ )
+ increment_build_number(
+ build_number: number + 1,
+ xcodeproj: "Example/ExampleApp.xcodeproj"
+ )
+ build_app(
+ project: "Example/ExampleApp.xcodeproj",
+ scheme: ENV["SCHEME"]
+ )
+ upload_to_testflight(
+ app_identifier: ENV["APP_IDENTIFIER"],
+ username: options[:username],
+ apple_id: ENV["APPLE_ID"],
+ skip_waiting_for_build_processing: true,
+ )
+ clean_build_artifacts()
+ end
+
+end
\ No newline at end of file